Scapy

Documentation
Login

Documentation

Scripts and info about the Python Scapy tool

Part 1: General commands
Part 2: How to recreate one single MMS packet from two or fragmented packets
Part 3: How to recreate a capture for an entire session: TCP connection establishment, MMS request and response, TCP connection termination
Part 4: Tool: Fix Sequence & Acknowledgment Numbers
Part 5: Tool: Recalculate IP and TCP checksums
Read More

Part 1: General commands top

Start (as root to send/receive packets)

sudo scapy
ls()

Create and show a packet

Create:

>>> myPacket = IP()

Show:

>>> myPacket.show()
    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\
Assign a value - syntax 1
>>> myPacket = IP(src="192.168.0.7")
Assign a value - syntax 2
>>> myPacket.dst="192.168.0.1"
Access packet member
>>> myPacket.src
'192.168.0.7'

Stack packet (create TCP/IP packet based on an IP packet)

>>> packet = myPacket/TCP()
>>> packet.show()
    ###[ IP ]###
        version= 4
        ihl= None
        tos= 0x0
        len= None
        id= 1
        flags= 
        frag= 0
        ttl= 64
        proto= tcp
        chksum= None
        src= 192.168.0.7
        dst= 192.168.0.1
        \options\
     ###[ TCP ]###
         sport= ftp_data
         dport= http
         seq= 0
         ack= 0
         dataofs= None
         reserved= 0
         flags= S
         window= 8192
         chksum= None
         urgptr= 0
         options= {}

Access members in stacked packet

IP member:

>>> packet.ttl = 10

TCP member:

>>> packet[TCP].sport = 1025

Delete a member (does not really delete member, only restores value to default)

>>> a=IP(dst="192.168.0.2")
>>> a.ttl
64
>>> a.ttl=32
>>> a.ttl
32
>>> del(a.ttl)
>>> a.ttl
64

List packet in different ways

All members, value, type, default value:

>>> ls(a)

As a tree:

>>> a.show()

As a tree, with "auto-filled-in" values (such as checksum):

>>> a.show2()

One-line:

>>> a

As a string, hex codes for non-printable bytes:

>>> str(a)
'D1\x92V\xc3\x08\x08\x00\'\x00\xd1\x01\x08\x00E\x00\x00C\x00\x01\x00\x00@\x06 4\x9fk-\xc2\xd8"\xb50\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00c1\x00\x00GET /index.html HTTP/1.0 \n\n'

As a hex dump:

>>> hexdump(a)
0000   44 31 92 56 C3 08 08 00  27 00 D1 01 08 00 45 00   D1.V....'.....E.
0010   00 43 00 01 00 00 40 06  20 34 9F 6B 2D C2 D8 22   .C....@. 4.k-.."
0020   B5 30 00 14 00 50 00 00  00 00 00 00 00 00 50 02   .0...P........P.
0030   20 00 63 31 00 00 47 45  54 20 2F 69 6E 64 65 78    .c1..GET /index
0040   2E 68 74 6D 6C 20 48 54  54 50 2F 31 2E 30 20 0A   .html HTTP/1.0 .
0050   0A

Create packet with payload

>>> my_new_packet_with_payload = Ether() / IP(dst="www.slashdot.org") / TCP() / "GET /index.html HTTP/1.0 \n\n"

Remove any previous payload from a packet

>>> a[TCP].remove_payload()

Add payload to an existing packet, using a variable

>>> payload = "GET /index.html HTTP/1.0 \n\n"
>>> a = a / Raw(load=payload)

Read packets from a pcap file

>>> b=rdpcap("my_little_capture.pcap")

Note that rdpcap returns an array of packets, even if the pcap file contains a single packet.
To treat a single packet, pick any element from the array:

>>> c = b[0]

Write a packet to a pcap file

>>> wrpcap('my_little_packet.pcap', a)

Note that it is possible to load the packet directly from Scapy into Wireshark, without writing it to a file:

>>> wireshark(a)

Send packets from a pcap file (requires root permissions)

>>> sendp(rdpcap("/tmp/pcapfile"))

Part 2: How to recreate one single MMS packet from two or fragmented packets top

The Ethernet part

>>> mms = Ether()

The TCP/IP part

The IP part:

>>> mms = mms / IP()
>>> mms[IP].src="192.168.80.1"
>>> mms[IP].dst="192.168.80.139"
>>> mms[IP].ttl = 64

The TCP part:

>>> mms = mms / TCP()
>>> mms[TCP].sport = 53965
>>> mms[TCP].dport = 8080

The HTTP/MMS part

Get the HTTP/MMS payload with Wireshark:

  1. Open the PCAP file cdpi-capture/captures/http_and_mms.cap in Wireshark.
  2. Select the second part of the fragmented MMS packet (packet 6).
  3. Select "2 Reassembled TCP Segments (442 bytes)". This should highlight the entire payload from both packets.
  4. Right-click on "2 Reassembled TCP Segments (442 bytes)". Select "Copy... -> ...as Hex Dump". See picture below.
  5. Paste Hex Dump into a text editor, save as payload-mms-hex.txt

Convert hexdump payload to a Python bytearray

printf "%s\n" 'payload = bytearray([' && \
    head -n -1 payload-mms-hex.txt | cut -c 8- | sed 's/\(\S\S\)/0x\1, /g' | sed 's/^\(.\)/    \1/' && \
    tail -1 payload-mms-hex.txt | cut -c 8- | sed 's/\(\S\S\)/0x\1, /g' | sed 's/\(,\)\s*$/]\)/g' | sed 's/^\(.\)/    \1/'

Copy & Paste the result directly into the Scapy shell (or to a Python script)

payload-mms-hex.py

>>> payload = bytearray([
        0x50, 0x4f, 0x53, 0x54, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6d, 0x6d, 0x73, 0x63,
        0x2f, 0x74, 0x65, 0x73, 0x74, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a,
        0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x6d, 0x6d, 0x73, 0x63, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72,
        0x2d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4f, 0x50, 0x57, 0x56, 0x2d, 0x53, 0x44, 0x4b,
        0x20, 0x55, 0x50, 0x2e, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x2f, 0x37, 0x2e, 0x30, 0x2e,
        0x32, 0x2e, 0x33, 0x2e, 0x31, 0x31, 0x39, 0x20, 0x28, 0x47, 0x55, 0x49, 0x29, 0x20, 0x4d, 0x4d,
        0x50, 0x2f, 0x32, 0x2e, 0x30, 0x20, 0x50, 0x75, 0x73, 0x68, 0x2f, 0x50, 0x4f, 0x0d, 0x0a, 0x58,
        0x2d, 0x57, 0x61, 0x70, 0x2d, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x20, 0x22, 0x68,
        0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x67, 0x61, 0x74, 0x65, 0x32, 0x2e, 0x6f,
        0x70, 0x65, 0x6e, 0x77, 0x61, 0x76, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x61, 0x70, 0x72,
        0x6f, 0x66, 0x2f, 0x4f, 0x50, 0x57, 0x56, 0x53, 0x44, 0x4b, 0x37, 0x30, 0x2e, 0x78, 0x6d, 0x6c,
        0x22, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a,
        0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x6e, 0x64,
        0x2e, 0x77, 0x61, 0x70, 0x2e, 0x6d, 0x6d, 0x73, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
        0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
        0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x77, 0x61, 0x70, 0x2e, 0x6d, 0x6d,
        0x73, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65,
        0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x35, 0x32, 0x0d, 0x0a,
        0x0d, 0x0a, 0x8c, 0x80, 0x98, 0x37, 0x34, 0x00, 0x8d, 0x92, 0x85, 0x04, 0x4a, 0xfa, 0xc6, 0x68,
        0x89, 0x17, 0x80, 0x31, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x2f, 0x54,
        0x59, 0x50, 0x45, 0x3d, 0x50, 0x4c, 0x4d, 0x4e, 0x00, 0x97, 0x33, 0x35, 0x30, 0x30, 0x2f, 0x54,
        0x59, 0x50, 0x45, 0x3d, 0x50, 0x4c, 0x4d, 0x4e, 0x00, 0x82, 0x36, 0x36, 0x36, 0x32, 0x32, 0x32,
        0x32, 0x32, 0x32, 0x2f, 0x54, 0x59, 0x50, 0x45, 0x3d, 0x50, 0x4c, 0x4d, 0x4e, 0x00, 0x82, 0x32,
        0x35, 0x40, 0x45, 0x52, 0x49, 0x43, 0x53, 0x53, 0x4f, 0x4e, 0x2e, 0x43, 0x4f, 0x4d, 0x00, 0x8a,
        0x80, 0x94, 0x81, 0x84, 0x01, 0xa3, 0x01, 0x29, 0x08, 0x03, 0x83, 0x81, 0xea, 0x43, 0x6f, 0x6e,
        0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x45, 0x6e,
        0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x38, 0x62, 0x69, 0x74, 0x00, 0xc0, 0x22, 0x3c, 0x31,
        0x3e, 0x00, 0x61, 0x73, 0x64, 0x66, 0x61, 0x73, 0x64, 0x66])

Remove any previous payload

>>> mms[TCP].remove_payload()

Add the newly defined payload to packet

>>> mms = mms / Raw(load=payload)

"Auto-fill" other fields (fake Ethernet addresses, IP checksum, etc.)

>>> mms = mms.__class__(str(mms))

Write the packet to a to pcap file

>>> wrpcap('http_and_mms_1_packet_from_scapy.pcap', mms)

Part 3: How to recreate a capture for an entire session: TCP connection establishment, MMS request and response, TCP connection termination top

The following packets are created:

  1. Client: SYN
  2. Server: SYN,ACK
  3. Client: ACK
  4. Client: PSH ACK Payload: MMS-over-HTTP Request: POST with m-send-req (with payload as described in Part 2)
  5. Server: PSH ACK Payload: MMS-over-HTTP Response: 200 OK with m-send-conf and Response-Status: 0x80 (Ok) (get payload in a similar way as described in Part 2)
  6. Client: FIN
  7. Server: FIN,ACK
  8. Client: FIN,ACK
  9. Server: FIN

All client packets have some fields in common (Ethernet src & dst address, IP src & dst address, TCP src & dst port).
The same is true for the server packets, only that the src and dst values are swapped.
The easiest way is to create an "template packet" with the common fields, and then forge each packet using the template.
The payload After forging each packet, "auto-fill" is done to complete fields:

    a[i] = a[i].__class__(str(a[i]))

NOTE:
Scapy does not "auto-fill" Sequence & Acknowledgment Numbers.
The script scapy-utils.py includes the function calc_seq_and_ack_numbers() to calculate the numbers for an array of packets.
The script scapy-fix-seq-ack-numbers.py takes a pcap file as input, and calls calc_seq_and_ack_numbers() to fix any erroneous numbers.
See Part 4: Tool: Fix Sequence & Acknowledgment Numbers for usage of this script.

If the packets are created as an array, it is easy save all packets using Scapy's wrpcap():

>>> wrpcap('http_and_mms_session_from_scapy.pcap', array_of_packets)

To confirm that the PCAP file has been correctly created, we can validate/fix it using WireEdit

Part 4: Tool: Fix Sequence & Acknowledgment Numbers top

When inspecting Sequence & Acknowledgment Numbers in an existing PCAP file, it can be hard to read the sometimes very big numbers.
Wireshark fixes this issue by showing Sequence & Acknowledgment Numbers relative to the first packet, so the numbers are easier to read for the user.
The script scapy-fix-seq-ack-numbers.py actually modifies the Sequence & Acknowledgment Numbers.
The script takes a PCAP file as input, and calls calc_seq_and_ack_numbers() in scapy-utils.py to modify the numbers, which are saved in the output PCAP. In the output PCAP, the initial TCP handshake always contains the following S & A numbers:

S=0      A=0 <-- Client SYN
S=0      A=1 <-- Server SYN ACK
S=1      A=1 <-- Client ACK

Sample usage and output:

cd utils
./scapy-fix-seq-ack-numbers.py -i syn-synack-ack.pcap -o syn-synack-ack-fixed.pcap

--------------------------------------------------------------------------
INFO: Unmodified Sequence & Acknowledge Numbers from 'syn-synack-ack.pcap'
--------------------------------------------------------------------------
INFO: LEGEND: TTL=Time-To-Live, UL=UpLoad, DL=DownLoad, SS=Same Source, DS= Different Source, S=Sequence Number, A=Acknowledgement Number
INFO: Packet 0: IP: 192.168.80.1:53965 -> IP: 192.168.80.139:8080	(UL)(DS) S=2624980521	 A=0	 TCP segment length: 0	 SYN
INFO: Packet 1: IP: 192.168.80.139:8080 -> IP: 192.168.80.1:53965	(DL)(DS) S=3526335138	 A=2624980522	 TCP segment length: 0	 SYN ACK
INFO: Packet 2: IP: 192.168.80.1:53965 -> IP: 192.168.80.139:8080	(UL)(DS) S=2624980522	 A=3526335139	 TCP segment length: 0	 ACK
INFO: Total Client Payload 0
INFO: Total Server Payload 0
------------------------------------------------------------------------------------------
INFO: Modified Sequence & Acknowledge Numbers to be written to 'syn-synack-ack-fixed.pcap'
------------------------------------------------------------------------------------------
INFO: LEGEND: TTL=Time-To-Live, UL=UpLoad, DL=DownLoad, SS=Same Source, DS= Different Source, S=Sequence Number, A=Acknowledgement Number
INFO: Packet 0: IP: 192.168.80.1:53965 -> IP: 192.168.80.139:8080	(UL)(DS) S=0	 A=0	 TCP segment length: 0	 SYN
INFO: Packet 1: IP: 192.168.80.139:8080 -> IP: 192.168.80.1:53965	(DL)(DS) S=0	 A=1	 TCP segment length: 0	 SYN ACK
INFO: Packet 2: IP: 192.168.80.1:53965 -> IP: 192.168.80.139:8080	(UL)(DS) S=1	 A=1	 TCP segment length: 0	 ACK
INFO: Total Client Payload 0
INFO: Total Server Payload 0
INFO: Wrote 3 packets to 'syn-synack-ack-fixed.pcap'

Part 5: Tool: Recalculate IP and TCP checksums top

scapy-utils.py also contains other functions, for example the function recalculate_checksums(), which is handy to call when one ot more packet fields have been modified:

# Recalculate checksums for all packets in an array
def recalculate_checksums(conn):
    for i in range(len(conn)):
        if conn[i] is not None:
            del conn[i][IP].chksum
            del conn[i][TCP].chksum
            # Autofill
            conn[i] = conn[i].__class__(str(conn[i]))
    return conn

The trick is to use the Scapy "autorefill", which automatically recalculates (possibly erroneous) checksums:

            del p[IP].chksum
            del p[TCP].chksum
            # Autofill
            p = p.__class__(str(p))

Read More top

Scapy Home Page
Scapy Scripting
Scapy Tutorial: Scapy - Decode and forge your own packet
Scapy Docs
Scapy: Demo - examples
WireEdit - A commercial editor for Wireshark
SSL/TLS layer for scapy: scapy-ssl_tls
SSL/TLS layer for scapy: scapy-ssl_tls (GitHub)
SSL/TLS layer for scapy: sample PCAP