Scapy Notes

Scapy is an “interactive packet manipulation program” written in python. It basically is a packet workshop framework which allows one to craft their own packets from scratch to match a variety of protocols, then send them on the wire and capture the results for analysis. Since it is written in python, it allows one to essentially create any number of tools, including scanners, fuzzers, DoS tools, etc. More info on it can be found at the scapy home page.

Basic Usage
When scapy is run from the command line, it loads the scapy modules and then drops you at the python shell prompt. This is useful for a number of reasons, but primary among them is that this means anything you can do in python, you can do in scapy as well. For the moment though, we’re going to focus solely on the scapy specific modules.

Building a Packet
Scapy makes it extremely easy to build a packet, here’s what it looks like:

First, we call scapy interactively:

[root@snsvc]# scapy
Welcome to Scapy (v1.1.1 / f88d99910220)
>>>

Next, we create the IP frame, then the TCP packet:

>>> a=IP()
>>> b=TCP()

Now we combine the two to create the TCP/IP datagram:

>>> c=a/b

We can use scapy’s ls command to view the contents of the packet:

>>> ls(c)
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        = 6               (0)
chksum     : XShortField          = None            (None)
src        : Emph                 = '127.0.0.1'     (None)
dst        : Emph                 = '127.0.0.1'     ('127.0.0.1')
options    : IPoptionsField       = ''              ('')
--
sport      : ShortEnumField       = 20              (20)
dport      : ShortEnumField       = 80              (80)
seq        : IntField             = 0               (0)
ack        : IntField             = 0               (0)
dataofs    : BitField             = None            (None)
reserved   : BitField             = 0               (0)
flags      : FlagsField           = 2               (2)
window     : ShortField           = 8192            (8192)
chksum     : XShortField          = None            (None)
urgptr     : ShortField           = 0               (0)
options    : TCPOptionsField      = {}              ({})

Changing Packet Details
Now, if we want to change any of the fields in the packet, we can do so by altering their values. For example, to change the IP destination to 192.168.1.1 and set the TCP destination port to 443, we do the following:

>>> a.dst='192.168.1.1'
>>> b.dport=443

Now we recreate the TCP/IP packet again, and view the changes using ls:

>>> c=a/b
>>> ls(c)
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        = 6               (0)
chksum     : XShortField          = None            (None)
src        : Emph                 = '192.168.1.3'   (None)
dst        : Emph                 = '192.168.1.1'   ('127.0.0.1')
options    : IPoptionsField       = ''              ('')
--
sport      : ShortEnumField       = 20              (20)
dport      : ShortEnumField       = 443             (80)
seq        : IntField             = 0               (0)
ack        : IntField             = 0               (0)
dataofs    : BitField             = None            (None)
reserved   : BitField             = 0               (0)
flags      : FlagsField           = 2               (2)
window     : ShortField           = 8192            (8192)
chksum     : XShortField          = None            (None)
urgptr     : ShortField           = 0               (0)
options    : TCPOptionsField      = {}              ({})

Note that even though we didn’t change the IP source, the value has changed. This is because scapy determined which interface would be used to send the packet to the destination we configured, and changed the source to that interface’s address for us. We can override this if desired.

Sending the Packet
We use the sr() function to send the data across the wire. This function sends the packet, sniffs the response, and matches sent packets with the received responses. It works at layer 3, and will return the whole result of a probe.

>>> sr(c)
Begin emission:
...Finished to send 1 packets.
*
Received 4 packets, got 1 answers, remaining 0 packets
(, )

Viewing Results
We can view the results by assigning them to variables:

>>> res,unans=_
>>> res.nsummary()
0000 IP / TCP 192.168.1.3:ftp_data > 192.168.1.1:https S ==> IP / TCP 192.168.1.1:https > 192.168.1.3:ftp_data SA / Padding

Here we see we sent a SYN packet to port 443, and received a SYN/ACK packet back from the destination. We also see there was some Padding added to the SYN/ACK. We can view the information in the padding by accessing the results list directly:

>>> res[0][1]
>>

Scripted Usage
Because scapy is written in python, it can be used from within any python script simply by using the import scapy statement.
For example, here’s a simple script to perform a TCP SYN scan of ports 0-1024 on a given host (provided as a parameter to the script):

#!/usr/bin/env python
import sys
from scapy import sr,IP,TCP,conf
conf.verb = 0
dstip = sys.argv[1]

print "nBeginning scan of "+dstip
res,unans = sr(IP(dst=dstip)/TCP(dport=[(0,1024)]),timeout=1)
if res:
   print "nReceived answers from the following ports:n"
   for s,r in res:
      print r.sprintf("%TCP.sport%")
print "nScan completedn"

And here’s the results of running this:

[root@snsvc]# ./scanner 192.168.1.1

Beginning scan of 192.168.1.1

Received answers from the following ports:

telnet
http
https

Scan completed

Install/Config Notes
“Error during evauluation of config file”
When running scapy from inside other python scripts, you may encounter the following error message:

ERROR: Error during evaluation of config file [None]
Traceback (most recent call last):
File "/usr/lib/python2.4/site-packages/scapy.py", line 12183, in read_config_file
execfile(configfile)

Not very helpful, but easy to fix. The problem is that scapy is looking for a config file which doesn’t exist. The good news is that one just has to be present, no configuration is required. To fix this, simply do the following:

# touch ~/.scapy_startup.py

Using the Loopback Interface
The loopback interface is a special interface, in that packets going through it are not really assembled and dissassembled. The kernel routes the packet to its destination while it is still stored an internal structure.

In order to use the loopback interface, you need to send your packets using PF_INET/SOCK_RAW instead of PF_PACKET/SOCK_RAW. This can be done by changing the supersocket used by scapy, which is accessed via the configuration.

The default scapy values for sockets are as follows:

+------------------------+----------------+
| Configuration Variable | Default Value  |
+------------------------+----------------+
| L2listen               | L2ListenSocket |
+------------------------+----------------+
| L2socket               | L2Socket       |
+------------------------+----------------+
| L3socket               | L3PacketSocket |
+------------------------+----------------+

To use the loopback interface, change the L3socket setting to L3RawSocket.
This can be done using the following command (either via the scapy CLI or inside a script):

conf.L3socket=L3RawSocket