Invio dei pacchetti con Scapy
by admin on May.17, 2009, under Hacking, Linux, Networking, Tools
E’ una logica conseguenza che, una volta manipolati, i pacchetti vengano inviati.
A tale scopo scapy mette a disposizione fondamentalmente due funzioni, sendp() e send(), ed un certo numero di altre funzioni derivate da esse.
In particolare, sendp() invia un pacchetto di layer 2, mentre send() un pacchetto di layer 3.
Ciò implica che send() sia in grado di gestire autonomamente il routing ed il livello 2.
In particolare essa sceglierà il livello 2 in conformità all’interfaccia di output.
Se necessario effettuerà preventivamente una richiesta ARP per ottenere l’indirizzo MAC dell’eventuale gateway.
All’utente invece viene riservata la responsabilità di specificare layer e interfaccia quando viene utilizzato il metodo sendp(), come in:
>>> sendp(Ether()/IP(dst="192.168.1.1",ttl=(1,4)), iface="eth0")
....
Sent 4 packets.
La funzione fuzz() è in grado di cambiare qualsiasi valore di default che non venga calcolato (come il checksum) con valori random del tipo adatto.
Nell’esempio seguente, il layer IP è normale, mentre quelli UDP ed NTP sono fuzzed. Il checksum UDP sarà corretto, la porta di destinazione UDP verrà condizionata da NTP ad essere 123 a la versione NTP forzata a 4. Le porte di origine saranno casuali:
>>> send(IP(dst="target")/fuzz(UDP()/NTP(version=4)),loop=1)
................^C
Sent 16 packets.
Nelle comunicazioni su reti a pacchetto vi è sempre una qualche forma di dialogo protocollare a un qualche livello.
La funzione sr() si occupa proprio di inviare pacchetti e ricevere pacchetti in risposta.
La funzione sr1() ne costituisce una variante che ritorna solamente il primo pacchetto di risposta.
Le funzioni srp() e srp1() operano allo stesso modo per i pacchetti layer 2 (Ethernet, 802.3, etc.).
Ecco come effettuare una query ricorsiva ad un dns server relativamente ad un certo indirizzo:
>>> p=sr1(IP(dst="212.216.112.112")/UDP()/DNS(rd=1,
qd=DNSQR(qname="www.voipandhack.it")))
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
>>> p
<IP version=4L ihl=5L tos=0x0 len=161 id=27375 flags= frag=0L ttl=48 proto=udp
chksum=0x1862 src=212.216.112.112 dst=192.168.1.10 options='' |<UDP sport=domain
dport=domain len=141 chksum=0xf6d1 |<DNS id=0 qr=1L opcode=QUERY aa=0L tc=0L
rd=1L ra=1L z=0L rcode=ok qdcount=1 ancount=1 nscount=2 arcount=2 qd=<DNSQR
qname='www.voipandhack.it.' qtype=A qclass=IN |> an=<DNSRR rrname='www.voipandhack.it.'
type=A rclass=IN ttl=60 rdata='81.31.145.164' |> ns=<DNSRR rrname='voipandhack.it.' type=NS
rclass=IN ttl=86400 rdata='dns2.dnshosting.it.' |<DNSRR rrname='voipandhack.it.' type=NS
rclass=IN ttl=86400 rdata='dns1.dnshosting.it.' |>> ar=<DNSRR rrname='dns2.dnshosting.it.'
type=A rclass=IN ttl=10 rdata='81.31.144.37' |<DNSRR rrname='dns1.dnshosting.it.' type=A
rclass=IN ttl=10 rdata='81.31.144.5' |>> |>>>
>>>
>>> p.show()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 161
id= 27375
flags=
frag= 0L
ttl= 48
proto= udp
chksum= 0x1862
src= 212.216.112.112
dst= 192.168.1.10
options= ''
###[ UDP ]###
sport= domain
dport= domain
len= 141
chksum= 0xf6d1
###[ DNS ]###
id= 0
qr= 1L
opcode= QUERY
aa= 0L
tc= 0L
rd= 1L
ra= 1L
z= 0L
rcode= ok
qdcount= 1
ancount= 1
nscount= 2
arcount= 2
qd
|###[ DNS Question Record ]###
| qname= 'www.voipandhack.it.'
| qtype= A
| qclass= IN
an
|###[ DNS Resource Record ]###
| rrname= 'www.voipandhack.it.'
| type= A
| rclass= IN
| ttl= 60
| rdlen= 4
| rdata= '81.31.145.164'
ns
|###[ DNS Resource Record ]###
| rrname= 'voipandhack.it.'
| type= NS
| rclass= IN
| ttl= 86400
| rdlen= 20
| rdata= 'dns2.dnshosting.it.'
|###[ DNS Resource Record ]###
| rrname= 'voipandhack.it.'
| type= NS
| rclass= IN
| ttl= 86400
| rdlen= 20
| rdata= 'dns1.dnshosting.it.'
ar
|###[ DNS Resource Record ]###
| rrname= 'dns2.dnshosting.it.'
| type= A
| rclass= IN
| ttl= 10
| rdlen= 4
| rdata= '81.31.144.37'
|###[ DNS Resource Record ]###
| rrname= 'dns1.dnshosting.it.'
| type= A
| rclass= IN
| ttl= 10
| rdlen= 4
| rdata= '81.31.144.5'
>>>
La famiglia delle funzioni derivate da sr() costituisce il punto di forza di scapy.
Possiamo affermare in generale che tali funzioni restituiscono una coppia di elementi costituiti da liste. Il primo elemento è una lista di coppie formate dal pacchetto inviato e da quello ricevuto in risposta, qualora vi sia stata, mentre il secondo elemento è costituito dalla lista dei pacchetti che non hanno ricevuto risposta).
l’esempio seguente chiarisce quanto appena detto.
>>> sr(IP(dst="151.65.41.0/24")/TCP(sport=RandShort(),dport=[80],flags=”S”))
Begin emission:
.*****.***********Finished to send 256 packets.
*……………………………^C
Received 52 packets, got 17 answers, remaining 239 packets
(<Results: TCP:17 UDP:0 ICMP:0 Other:0>, )
>>>
>>> ans,unans=_
>>> ans.show()
………
0000 IP / TCP 151.65.44.163:3285 > 151.65.41.41:www S ==> IP / TCP 151.65.41.41:www > 151.65.44.163:3285 SA
0001 IP / TCP 151.65.44.163:3285 > 151.65.41.76:www S ==> IP / TCP 151.65.41.76:www > 151.65.44.163:3285 RA
0002 IP / TCP 151.65.44.163:3285 > 151.65.41.112:www S ==> IP / TCP 151.65.41.112:www > 151.65.44.163:3285 SA
0003 IP / TCP 151.65.44.163:3285 > 151.65.41.114:www S ==> IP / TCP 151.65.41.114:www > 151.65.44.163:3285 SA
0004 IP / TCP 151.65.44.163:3285 > 151.65.41.126:www S ==> IP / TCP 151.65.41.126:www > 151.65.44.163:3285 RA
0005 IP / TCP 151.65.44.163:3285 > 151.65.41.123:www S ==> IP / TCP 151.65.41.123:www > 151.65.44.163:3285 SA
0006 IP / TCP 151.65.44.163:3285 > 151.65.41.143:www S ==> IP / TCP 151.65.41.143:www > 151.65.44.163:3285 SA
0007 IP / TCP 151.65.44.163:3285 > 151.65.41.150:www S ==> IP / TCP 151.65.41.150:www > 151.65.44.163:3285 SA
0008 IP / TCP 151.65.44.163:3285 > 151.65.41.174:www S ==> IP / TCP 151.65.41.174:www > 151.65.44.163:3285 SA
0009 IP / TCP 151.65.44.163:3285 > 151.65.41.237:www S ==> IP / TCP 151.65.41.237:www > 151.65.44.163:3285 RA
0010 IP / TCP 151.65.44.163:3285 > 151.65.41.241:www S ==> IP / TCP 151.65.41.241:www > 151.65.44.163:3285 RA
0011 IP / TCP 151.65.44.163:3285 > 151.65.41.245:www S ==> IP / TCP 151.65.41.245:www > 151.65.44.163:3285 RA
>>>
>>> ans[1]
(<IP frag=0 proto=tcp dst=151.65.41.9 |<TCP sport=3285 dport=www |>>, <IP version=4L ihl=5L tos=0×0 len=44 id=0 flags=DF frag=0L ttl=61 proto=tcp chksum=0xbbcf src=151.65.41.9 dst=151.65.44.163 options=” |<TCP sport=www dport=3285 seq=2273485693L ack=1 dataofs=6L reserved=0L flags=SA window=5840 chksum=0×60e7 urgptr=0 options=[('MSS', 1360)] |<Padding load=’jj’ |>>>)
>>>
>>> unans.show()
0000 IP / TCP 151.65.44.163:3285 > 151.65.41.138:www S
0001 IP / TCP 151.65.44.163:3285 > 151.65.41.116:www S
0002 IP / TCP 151.65.44.163:3285 > 151.65.41.227:www S
………………………………………
………………………………………
0240 IP / TCP 151.65.44.163:3285 > 151.65.41.132:www S
0241 IP / TCP 151.65.44.163:3285 > 151.65.41.51:www S
0242 IP / TCP 151.65.44.163:3285 > 151.65.41.229:www S
0243 IP / TCP 151.65.44.163:3285 > 151.65.41.60:www S
Per mostrare solo i dati ai quali siamo interessati:
>>> ans.summary( lambda(s,r): r.sprintf("%IP.src% t %TCP.flags%") )
151.65.41.33 RA
151.65.41.41 SA
151.65.41.49 SA
151.65.41.63 SA
151.65.41.114 SA
151.65.41.118 RA
151.65.41.132 RA
151.65.41.142 SA
151.65.41.146 RA
151.65.41.158 SA
....
>>>
Si è in pratica allestito uno scanner minimale in grado di individuare tutti i server http in ascolto sulla sottorete 151.65.41.0/24, ovvero quelli che hanno risposto con un pacchetto con entrambi i flag SYN e ACK attivi.
Un rudimentale scanner avrebbe anche potuto essere scritto così:
>>> p=IP(dst="62.211.64.0/24")/TCP(dport=25, flags="S")
>>> sr(p,timeout=3)
Begin emission:
.*.Finished to send 256 packets.
..
Received 5 packets, got 1 answers, remaining 255 packets
(<Results: UDP:0 ICMP:0 TCP:1 Other:0>, <Unanswered: UDP:0 ICMP:0 TCP:255 Other:0>)
>>> results = _[0]
>>> for pktout, pktin in results:
if pktin.flags == 2:
print pktout.dst
…
62.211.64.101
>>>
Si può anche generare una tabella utilizzando la funzione make_table() per rappresentare informazioni relative a molteplici destinazioni:
>>> ans,unans = sr(IP(dst=["google.com","yahoo.com","slashdot.org"])/TCP(dport=[22,80,443],flags=”S”))
Begin emission:
.Finished to send 9 packets.
.*******….^C
Received 13 packets, got 7 answers, remaining 2 packets
>>> ans.make_table(
lambda(s,r): (s.dst, s.dport,
r.sprintf("{TCP:%TCP.flags%}")))
74.125.45.100 206.190.60.37 216.34.181.45
22 - - RA
80 SA SA SA
443 SA SA SA
>>>
Si può specificare un intervallo di tempo tra due pacchetti col parametro inter. Se alcuni pacchetti sono persi o se l’ intervallo specificato non è sufficente, si possono reinviare tutti i pacchetti che non hanno ricevuto risposta sia chiamando ancora la funzione, direttamente con la lista dei pacchetti senza risposta, oppure specificando il parametro retry. Se retry vale 3, scapy tenterà di riinviare i pacchetti senza risposta 3 volte. Il parametro timeout specifica quanto tempo attendere dopo che l’ultimo pacchetto sia stato inviato.
>>> sr(IP(dst="172.20.29.5/30")/TCP(dport=[21,22,23]),inter=0.5,retry=2,timeout=1)
Ecco un esempio di traceroute TCP:
>>> ans,unans=sr(IP(dst="212.216.112.112", ttl=(4,25),id=RandShort())/TCP(dport=53,flags=0x2))
Begin emission:
.******************Finished to send 22 packets.
****
Received 23 packets, got 22 answers, remaining 0 packets
>>> for snd,rcv in ans:
... print snd.ttl, rcv.src, isinstance(rcv.payload, TCP)
...
4 151.6.225.65 False
5 151.6.204.41 False
6 151.6.6.146 False
7 151.6.2.182 False
8 151.6.0.18 False
9 151.99.75.245 False
10 80.20.8.254 False
11 151.99.29.208 False
12 85.36.8.138 False
13 217.169.96.22 False
14 62.211.79.66 False
15 62.211.79.1 False
16 212.216.112.112 True
17 212.216.112.112 True
18 212.216.112.112 True
19 212.216.112.112 True
20 212.216.112.112 True
21 212.216.112.112 True
22 212.216.112.112 True
23 212.216.112.112 True
24 212.216.112.112 True
25 212.216.112.112 True
>>>
E’ anche possibile catturare i pacchetti. Se non è indicata espressamente una interfaccia, vengono considerate tutte.
>>> sniff(filter="host 66.35.250.151", count=2)
<Sniffed: ICMP:0 UDP:0 TCP:1 Other:1>
>>> a.nsummary()
0000 Ether / IP / TCP 192.168.1.10:41316 > 66.35.250.151:www S
0001 802.11 Management 0L 44:65:08:00:45:00 > 15:c6:00:15:af:1e / Dot11AssoReq / Dot11Elt / Dot11Elt / Dot11Elt
>>>
>>> a[0]
<Ether dst=00:15:e9:f9:15:c6 src=00:15:af:1e:44:65 type=0×800 |<IP version=4L ihl=5L tos=0×0 len=52 id=51847 flags=DF frag=0L ttl=64 proto=tcp chksum=0×71cf src=192.168.1.10 dst=66.35.250.151 options=” |<TCP sport=35045 dport=www seq=1375571431 ack=0 dataofs=8L reserved=0L flags=S window=5840 chksum=0xf0ba urgptr=0 options=[('MSS', 1460), ('NOP', None), ('NOP', None), ('SAckOK', ''), ('NOP', None), ('WScale', 6)] |>>>
>>>
>>> sniff(iface=”ath0″, prn=lambda x: x.summary())
802.3 00:18:4d:d2:f0:06 > 01:80:c2:00:00:00 / LLC / STP / Raw
802.3 00:18:4d:d2:f0:06 > 01:80:c2:00:00:00 / LLC / STP / Raw
802.3 00:18:4d:d2:f0:06 > 01:80:c2:00:00:00 / LLC / STP / Raw
802.3 00:18:4d:d2:f0:06 > 01:80:c2:00:00:00 / LLC / STP / Raw
802.3 00:18:4d:d2:f0:06 > 01:80:c2:00:00:00 / LLC / STP / Raw
Ether / IP / TCP 209.85.137.19:www > 192.168.1.10:53517 PA / Raw
Ether / IP / TCP 192.168.1.10:53517 > 209.85.137.19:www A
802.3 00:18:4d:d2:f0:06 > 01:80:c2:00:00:00 / LLC / STP / Raw
802.3 00:18:4d:d2:f0:06 > 01:80:c2:00:00:00 / LLC / STP / Raw
Ether / IP / UDP / DNS Qry “mybroadband.co.za.”
Ether / IP / UDP / DNS Ans “41.203.17.1″
Ether / IP / TCP 192.168.1.10:39700 > 41.203.17.1:www S
Ether / IP / TCP 41.203.17.1:www > 192.168.1.10:39700 SA
Ether / IP / TCP 192.168.1.10:39700 > 41.203.17.1:www A
Ether / IP / TCP 192.168.1.10:39700 > 41.203.17.1:www PA / Raw
…
^C
>>>
Il massimo dettaglio possibile durante la cattura dei pacchetti può essere ottenuto con:
>>> sniff(iface="ath0", prn=lambda x: x.show())
###[ 802.3 ]###
dst= 01:80:c2:00:00:00
src= 00:18:4d:d2:f0:06
len= 46
###[ LLC ]###
dsap= 0x42
ssap= 0x42
ctrl= 3
###[ Spanning Tree Protocol ]###
proto= 0
version= 0
bpdutype= 0
bpduflags= 0
rootid= 32768
rootmac= 00:18:4d:d2:f0:06
pathcost= 0
bridgeid= 32768
bridgemac= 00:18:4d:d2:f0:06
portid= 32770
age= 0.0
maxage= 20.0
hellotime= 2.0
fwddelay= 0.0
###[ Raw ]###
load= 'xa5xa5xa5xa5xa5xa5xa5xa5'
###[ Ethernet ]###
dst= 00:15:e9:f9:15:c6
src= 00:15:af:1e:44:65
type= 0x800
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 40
id= 409
flags= DF
frag= 0L
ttl= 64
proto= tcp
chksum= 0xcd1f
src= 192.168.1.10
dst= 213.254.212.102
options= ''
###[ TCP ]###
sport= 38958
dport= www
seq= 1187990480
ack= 4102508167L
dataofs= 5L
reserved= 0L
flags= FA
window= 108
chksum= 0xd522
urgptr= 0
options= {}
[...]
Ecco un invece un esempio di cattura effettuata utilizzando un filtro BPF:
>>> a=sniff(filter="tcp and port 110",
... prn=lambda x: x.sprintf("%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport% %2s,TCP.flags% : %TCP.payload%"))
192.168.1.10:53191 -> 212.25.179.10:pop3 S :
212.25.179.10:pop3 -> 192.168.1.10:53191 SA :
192.168.1.10:53191 -> 212.25.179.10:pop3 A :
212.25.179.10:pop3 -> 192.168.1.10:53191 PA : +OK Hello there.
192.168.1.10:53191 -> 212.25.179.10:pop3 A :
192.168.1.10:53191 -> 212.25.179.10:pop3 PA : CAPA
[...]
192.168.1.10:50284 -> 81.31.145.164:pop3 PA : LIST
212.25.179.10:pop3 -> 192.168.1.10:45937 PA : +OK
81.31.145.164:pop3 -> 192.168.1.10:50284 PA : +OK 10 messages:
[...]
Abbiamo già incontrato la funzione make_table(). Essa prende come parametro una lista, ed una funzione che returna una terna. Il primo elemento è il valore che va posto sull’asse x, il secondo quello che va posto sull’asse y ed il terzo è il valore che vogliamo alle coordinate (x,y). Il risultato è una tabella.
Possiamo verificarlo con questo traceroute in parallelo:
>>> ans,unans=sr(IP(dst="bkom.net/30", ttl=(1,18))/TCP())
Begin emission:
*******.***********************************************************Finished to send 72 packets.
******
Received 73 packets, got 72 answers, remaining 0 packets
>>> ans.make_table( lambda (s,r): (s.dst, s.ttl, r.src) )
82.165.55.164 82.165.55.165 82.165.55.166 82.165.55.167
1 192.168.1.1 192.168.1.1 192.168.1.1 192.168.1.1
2 151.23.227.33 151.23.227.33 151.23.227.33 151.23.227.33
3 10.0.31.1 10.0.31.1 10.0.31.1 10.0.31.1
4 151.6.225.65 151.6.225.65 151.6.225.65 151.6.225.73
5 151.6.204.49 151.6.204.49 151.6.204.49 151.6.204.53
6 151.6.7.146 151.6.7.146 151.6.7.106 151.6.7.146
7 151.6.0.174 151.6.0.174 151.6.0.178 151.6.0.174
8 130.117.15.33 130.117.15.33 130.117.15.81 130.117.15.33
9 130.117.1.113 130.117.1.113 130.117.1.113 154.54.1.237
10 130.117.0.73 130.117.0.73 130.117.0.73 130.117.3.81
11 130.117.0.145 130.117.1.65 130.117.1.162 130.117.0.133
12 130.117.1.110 130.117.1.38 130.117.1.225 130.117.1.206
13 130.117.0.226 130.117.1.202 130.117.0.226 130.117.0.254
14 130.117.240.170 130.117.240.170 130.117.240.170 130.117.240.170
15 212.227.120.25 212.227.120.25 212.227.120.25 212.227.120.25
16 212.227.120.49 212.227.120.49 212.227.120.49 212.227.120.49
17 212.227.121.208 212.227.121.208 212.227.116.208 212.227.116.208
18 82.165.55.164 82.165.55.165 82.165.55.166 82.165.55.167
>>>

