Scapy Introduction

# Scapy live presentation script:

# run sudo scapy
>>> from pprint import pprint as pp

>>> #######################################
>>> ###        Single packets           ###
>>> #######################################
>>> a=IP(dst="131.188.24.11")
>>> a
<IP  dst=131.188.24.11 |>
>>> a.dst
'131.188.24.11'
>>> a.src
'192.168.214.123'
>>> # Unset fields will be filled with useful and dynamic defaults
>>> ls(a)
version    : BitField             = 4               (4)
ihl        : BitField             = None            (None)
tos        : XByteField           = 0               (0)
len        : ShortField           = None            (None)
id         : ShortField           = 1               (1)
flags      : FlagsField           = 0               (0)
frag       : BitField             = 0               (0)
ttl        : ByteField            = 64              (64)
proto      : ByteEnumField        = 0               (0)
chksum     : XShortField          = None            (None)
src        : Emph                 = '192.168.214.123' (None)
dst        : Emph                 = '131.188.24.11' ('127.0.0.1')
options    : PacketListField      = []              ([])
>>> # Detailed view of packet
>>> a.<TAB><TAB>
[...]
>>> # Autocompletion for the win!
>>> a.display()
###[ IP ]###
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags= 
  frag= 0
  ttl= 64
  proto= ip
  chksum= None
  src= 192.168.214.123
  dst= 131.188.24.11
  \options\
>>> # yet another view...
>>> 
>>> # But networks and scapy is more then just one protocol at a time:
>>> ls()
[...]
>>> # Gives a list of supported protocols
>>> Ether()/IP()/UDP()/DNS()
<Ether  type=0x800 |<IP  frag=0 proto=udp |<UDP  sport=domain |<DNS  |>>>>
>>> # lower layers get automatically effected. can be overwritten
>>> p = _
>>> # _ is the last result
>>> p.haslayer(IP)
1
>>> p.proto
17
>>> # all attributes are available, if not masked
>>> p.dst
WARNING: Mac address to reach destination not found. Using broadcast.
'ff:ff:ff:ff:ff:ff'
>>> # Two things: 1. if accessed, defaults are resolved
>>> #             2. the "lowest" layer wins by overlapping
>>> #                (Ether and IP have a dst field)
>>> p.payload
<IP  frag=0 proto=udp |<UDP  sport=domain |<DNS  |>>>
>>> p.payload.dst
'127.0.0.1'
>>> str(p)
'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00(\x00\x01
\x00\x00@\x11|\xc2\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x14\x01Z\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> # Byte-wise representation of packet
>>> Ether(_)
<Ether  dst=ff:ff:ff:ff:ff:ff src=00:00:00:00:00:00 type=0x800 |<IP  version=
4L ihl=5L tos=0x0 len=40 id=1 flags= frag=0L ttl=64 proto=udp chksum=0x7cc2 
src=127.0.0.1 dst=127.0.0.1 options=[] |<UDP  sport=domain dport=domain len=20
chksum=0x15a |<DNS  id=0 qr=0L opcode=QUERY aa=0L tc=0L rd=0L ra=0L z=0L 
rcode=ok qdcount=0 ancount=0 nscount=0 arcount=0 |>>>>
>>> # Each protocol can parse raw byte-streams. Every field is now set
>>> 
>>> #######################################
>>> ###       Multiple packets          ###
>>> #######################################
>>> # "Advanced" magic:
>>> IP(dst="192.168.1.*")
<IP  dst=Net('192.168.1.*') |>
>>> IP(dst="192.168.1.0/24")
<IP  dst=Net('192.168.1.0/24') |>
>>> IP(dst="192.168.1.0-255")
<IP  dst=Net('192.168.1.0-255') |>
>>> # This is all the same
>>> IP(dst="www.ccc.de/30")
<IP  dst=Net('www.ccc.de/30') |>
>>> # even this works!
>>> _/TCP(dport=[22,80])
<IP  frag=0 proto=tcp dst=Net('www.ccc.de/30') |<TCP  dport=[22, 80] |>>
>>> pp(list(_)) # pp is used to get a nicer output
[<IP  frag=0 proto=tcp dst=213.73.89.120 |<TCP  dport=ssh |>>,
 <IP  frag=0 proto=tcp dst=213.73.89.120 |<TCP  dport=http |>>,
 <IP  frag=0 proto=tcp dst=213.73.89.121 |<TCP  dport=ssh |>>,
 <IP  frag=0 proto=tcp dst=213.73.89.121 |<TCP  dport=http |>>,
 <IP  frag=0 proto=tcp dst=213.73.89.122 |<TCP  dport=ssh |>>,
 <IP  frag=0 proto=tcp dst=213.73.89.122 |<TCP  dport=http |>>,
 <IP  frag=0 proto=tcp dst=213.73.89.123 |<TCP  dport=ssh |>>,
 <IP  frag=0 proto=tcp dst=213.73.89.123 |<TCP  dport=http |>>]
>>> # We have now generated a list with all possible permutations
>>>
>>> # Going into the world and ...
>>> #######################################
>>> ###  Sending and receiving packets  ###
>>> #######################################
>>> sendp("Hello World! Is any one there?")
.
Sent 1 packets.
>>> # A layer 2 packet has been set free
>>> # (check out with wireshark)
>>> send(IP(dst="google.com/28")/ICMP())
................
Sent 16 packets.
>>> # Ping scanning google.com...
>>> # send supports automatic routing on layer 3
>>> # But how do we know if there was a reply?
>>> sr(IP(dst="google.com/28")/ICMP(), timeout=1)
Begin emission:
............*...Finished to send 16 packets.
.************
Received 29 packets, got 13 answers, remaining 3 packets
(<Results: TCP:0 UDP:0 ICMP:13 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:3 
Other:0>)
>>> # Now we have 13 replied and 3 unanswered packages, lets have a look
>>> ans, uans = _
>>> ans.summary()
IP / ICMP 192.168.214.123 > 74.125.113.96 echo-request 0 ==> IP / ICMP 74.125.
113.96 > 192.168.214.123 echo-reply 0
IP / ICMP 192.168.214.123 > 74.125.113.98 echo-request 0 ==> IP / ICMP 74.125.
113.98 > 192.168.214.123 echo-reply 0
[...]
>>> uans.summary()
IP / ICMP 192.168.214.123 > 74.125.113.111 echo-request 0
IP / ICMP 192.168.214.123 > 74.125.113.110 echo-request 0
IP / ICMP 192.168.214.123 > 74.125.113.107 echo-request 0
>>> # srp() for layer 2 exists as well
>>> quit()