Scapy

Suraj Deshmukh ([email protected])
http://deshmukhsuraj.wordpress.com/

@surajssd009005

What is Scapy?

  • An interactive tool; that lets you send, sniff, craft and manipulate packets
  • Craft and decode packets of wide number of protocols
  • You can do network scans, traceroutes, arpspoofs, so almost all tasks of nmap and tcpdump

Why use Scapy(and not other tools)?

  • Scapy is a very flexible tool to use(you'll know when you do hands-on).
  • Uses Python as a tool usage language, hence easier to use.
  • Break open tool designer's perspective.

Tool designer's perspective

  • Computers are good at decoding but not so good at interpreting.
  • So a programmer while designing tool makes his tool mimic interpretation.
  • e.g. When a packet is sent on a TCP port with SYN flag and response comes back as SYN-ACK then port is open
  • They show the result in a way what original author thought was appropriate, though it is helpful for beginners.
  • So much information is lost in the process.
  • But computer networks has so many protocols, so permutation of doing things increase
  • So somebody would like to play in some unique way with protocols what nobody has ever thought about
  • But playing with networks is not as easy job
  • Need to write 100's of lines of C code

enter Scapy...

  • just a single line to send packet of your choice

In a Nutshell

Lets start some packets rolling...

In [7]:
from scapy.all import *
WARNING: No route found for IPv6 destination :: (no default route?)
WARNING:scapy.runtime:No route found for IPv6 destination :: (no default route?)

Basics

In [8]:
ip = IP()
ip.show()
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     = 
  frag      = 0
  ttl       = 64
  proto     = hopopt
  chksum    = None
  src       = 127.0.0.1
  dst       = 127.0.0.1
  \options   \
In [9]:
ip.ttl = 100
ip.show()
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     = 
  frag      = 0
  ttl       = 100
  proto     = hopopt
  chksum    = None
  src       = 127.0.0.1
  dst       = 127.0.0.1
  \options   \
In [10]:
ip.dst = '192.168.0.1'
ip.show()
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     = 
  frag      = 0
  ttl       = 100
  proto     = hopopt
  chksum    = None
  src       = 192.168.0.60
  dst       = 192.168.0.1
  \options   \

Encapsulating Layers

In [11]:
ip = IP(dst='192.168.0.1')
icmp = ICMP()
pkt = ip/icmp
pkt
Out[11]:
<IP  frag=0 proto=icmp dst=192.168.0.1 |<ICMP  |>>
In [12]:
pkt = IP(dst='www.google.com')/TCP(dport=80)
pkt
Out[12]:
<IP  frag=0 proto=tcp dst=Net('www.google.com') |<TCP  dport=http |>>
In [13]:
pkt = Ether()/IP()/TCP()
pkt
Out[13]:
<Ether  type=0x800 |<IP  frag=0 proto=tcp |<TCP  |>>>

Send and Receive Packets

In [14]:
send(IP()/ICMP())
Sent 1 packets.
In [18]:
p = sr1(IP(dst='192.168.0.1')/ICMP())
p
Received 21 packets, got 1 answers, remaining 0 packets
Begin emission:
Finished to send 1 packets.
Out[18]:
<IP  version=4L ihl=5L tos=0x0 len=28 id=1 flags= frag=0L ttl=30 proto=icmp chksum=0x1b53 src=192.168.0.1 dst=192.168.0.60 options=[] |<ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |>>
In [19]:
ans, unans = sr(IP(dst='192.168.0.1')/TCP(dport=[53, 80]))
ans.show() 
Received 29 packets, got 2 answers, remaining 0 packets
0000 IP / TCP 192.168.0.60:ftp_data > 192.168.0.1:domain S ==> IP / TCP 192.168.0.1:domain > 192.168.0.60:ftp_data RA
0001 IP / TCP 192.168.0.60:ftp_data > 192.168.0.1:http S ==> IP / TCP 192.168.0.1:http > 192.168.0.60:ftp_data SA
Begin emission:
Finished to send 2 packets.

Ping

In [20]:
ip = IP()
ip.show()
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     = 
  frag      = 0
  ttl       = 64
  proto     = hopopt
  chksum    = None
  src       = 127.0.0.1
  dst       = 127.0.0.1
  \options   \
In [21]:
# default gateway
ip.dst = '192.168.0.1'
ip.show()
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     = 
  frag      = 0
  ttl       = 64
  proto     = hopopt
  chksum    = None
  src       = 192.168.0.60
  dst       = 192.168.0.1
  \options   \
In [22]:
icmp = ICMP()
icmp.show()
###[ ICMP ]###
  type      = echo-request
  code      = 0
  chksum    = None
  id        = 0x0
  seq       = 0x0
In [23]:
# send and receive, stops after receiving a single reply
# store the reply in variable named 'gateway'
gateway = sr1(ip/icmp)
gateway
Received 62 packets, got 1 answers, remaining 0 packets
Begin emission:
Finished to send 1 packets.
Out[23]:
<IP  version=4L ihl=5L tos=0x0 len=28 id=1 flags= frag=0L ttl=30 proto=icmp chksum=0x1b53 src=192.168.0.1 dst=192.168.0.60 options=[] |<ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |>>
In [24]:
gateway.show()
###[ IP ]###
  version   = 4L
  ihl       = 5L
  tos       = 0x0
  len       = 28
  id        = 1
  flags     = 
  frag      = 0L
  ttl       = 30
  proto     = icmp
  chksum    = 0x1b53
  src       = 192.168.0.1
  dst       = 192.168.0.60
  \options   \
###[ ICMP ]###
     type      = echo-reply
     code      = 0
     chksum    = 0xffff
     id        = 0x0
     seq       = 0x0

Traceroute

In [25]:
# Traceroute works by sending packets each time with decreasing 'ttl' value
reply = sr1(IP(dst='www.google.com', ttl=1)/UDP())
reply.show()
Received 41 packets, got 1 answers, remaining 0 packets
###[ IP ]###
  version   = 4L
  ihl       = 5L
  tos       = 0x0
  len       = 56
  id        = 0
  flags     = 
  frag      = 0L
  ttl       = 30
  proto     = icmp
  chksum    = 0x1b38
  src       = 192.168.0.1
  dst       = 192.168.0.60
  \options   \
###[ ICMP ]###
     type      = time-exceeded
     code      = ttl-zero-during-transit
     chksum    = 0x9111
     unused    = 8634536
###[ IP in ICMP ]###
        version   = 4L
        ihl       = 5L
        tos       = 0x0
        len       = 28
        id        = 1
        flags     = 
        frag      = 0L
        ttl       = 0
        proto     = udp
        chksum    = 0x5dad
        src       = 192.168.0.60
        dst       = 216.58.196.4
        \options   \
###[ UDP in ICMP ]###
           sport     = domain
           dport     = domain
           len       = 8
           chksum    = 0xa250
Begin emission:
Finished to send 1 packets.
In [26]:
reply.src
Out[26]:
'192.168.0.1'
In [27]:
ans, unans = traceroute(["www.google.com","www.yahoo.com","www.bing.com"],maxttl=20)
Received 33 packets, got 22 answers, remaining 38 packets
   106.10.139.246:tcp80 204.79.197.200:tcp80 216.58.220.4:tcp80 
1  192.168.0.1     11   192.168.0.1     11   192.168.0.1     11 
2  172.31.28.250   11   -                    172.31.28.250   11 
12 -                    204.79.197.200  SA   -                  
13 -                    204.79.197.200  SA   -                  
14 -                    204.79.197.200  SA   -                  
15 -                    204.79.197.200  SA   -                  
16 106.10.139.246  SA   204.79.197.200  SA   -                  
17 106.10.139.246  SA   204.79.197.200  SA   -                  
18 106.10.139.246  SA   204.79.197.200  SA   216.58.220.4    SA 
19 106.10.139.246  SA   204.79.197.200  SA   216.58.220.4    SA 
20 106.10.139.246  SA   204.79.197.200  SA   216.58.220.4    SA 
Begin emission:
Finished to send 60 packets.
In [28]:
ans.show()
   106.10.139.246:tcp80 204.79.197.200:tcp80 216.58.220.4:tcp80 
1  192.168.0.1     11   192.168.0.1     11   192.168.0.1     11 
2  172.31.28.250   11   -                    172.31.28.250   11 
12 -                    204.79.197.200  SA   -                  
13 -                    204.79.197.200  SA   -                  
14 -                    204.79.197.200  SA   -                  
15 -                    204.79.197.200  SA   -                  
16 106.10.139.246  SA   204.79.197.200  SA   -                  
17 106.10.139.246  SA   204.79.197.200  SA   -                  
18 106.10.139.246  SA   204.79.197.200  SA   216.58.220.4    SA 
19 106.10.139.246  SA   204.79.197.200  SA   216.58.220.4    SA 
20 106.10.139.246  SA   204.79.197.200  SA   216.58.220.4    SA 
In [29]:
ans.graph()

Sniff

In [31]:
a = sniff(iface='wlan0', count=10, filter='ip')
a.summary()
Ether / IP / UDP / DNS Qry "fonts.googleapis.com." 
Ether / IP / UDP / DNS Qry "fonts.googleapis.com." 
Ether / IP / UDP / DNS Qry "fonts.googleapis.com." 
Ether / IP / UDP / DNS Qry "fonts.googleapis.com." 
Ether / IP / UDP / DNS Qry "fonts.googleapis.com." 
Ether / IP / UDP / DNS Qry "tiles.services.mozilla.com." 
Ether / IP / UDP / DNS Qry "tiles.services.mozilla.com." 
Ether / IP / UDP / DNS Qry "tiles.services.mozilla.com." 
Ether / IP / UDP / DNS Ans "tiles.r53-2.services.mozilla.com." 
Ether / IP / TCP 192.168.0.60:37661 > 52.25.165.135:https S
In [32]:
sniff(iface="wlan0", prn=lambda x: x.summary(), count=10)
802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:5b:b6:95 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw
Ether / IP / UDP / DNS Ans "216.58.196.3" 
Ether / IP / TCP 192.168.0.60:46113 > 216.58.196.3:http S
Ether / IP / UDP / DNS Ans "216.58.196.3" 
Out[32]:
<Sniffed: TCP:1 UDP:2 ICMP:0 Other:7>

Capture and Store Packets

In [33]:
pkts = sniff(iface="wlan0", prn=lambda x: x.summary(), count=20)
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 14:dd:a9:b0:f9:75 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw
In [34]:
wrpcap('temp.pcap', pkts)
In [35]:
pkts = rdpcap("temp.pcap")