"[Scapy](http://www.secdev.org/projects/scapy) is a powerful Python-based interactive packet manipulation program and library. It can be used to forge or decode packets for a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more.\n",
"\n",
"This iPython notebook provides a short tour of the main Scapy features. It assumes that you are familiar with networking terminology. All examples where built using the development version from [https://github.com/secdev/scapy](https://github.com/secdev/scapy), and tested on Linux. They should work as well on OS X, and other BSD.\n",
"\n",
"The current documentation is available on [http://scapy.readthedocs.io/](http://scapy.readthedocs.io/) !"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scapy eases network packets manipulation, and allows you to forge complicated packets to perform advanced tests. As a teaser, let's have a look a two examples that are difficult to express without Scapy:\n",
"\n",
"1_ Sending a TCP segment with maximum segment size set to 0 to a specific port is an interesting test to perform against embedded TCP stacks. It can be achieved with the following one-liner:"
"Now that, we've got your attention, let's start the tutorial !"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Quick setup"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The easiest way to try Scapy is to clone the github repository, then launch the `run_scapy` script as root. The following examples can be pasted on the Scapy prompt. There is no need to install any external Python modules."
"Note: iPython users must import scapy as follows"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from scapy.all import *"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 13
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"First steps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With Scapy, each network layer is a Python class.\n",
"\n",
"The `'/'` operator is used to bind layers together. Let's put a TCP segment on top of IP and assign it to the `packet` variable, then stack it on top of Ethernet. "
"This last output displays the packet summary. Here, Scapy automatically filled the Ethernet type as well as the IP protocol field.\n",
"\n",
"Protocol fields can be listed using the `ls()` function:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
" >>> ls(IP, verbose=True)\n",
" version : BitField (4 bits) = (4)\n",
" ihl : BitField (4 bits) = (None)\n",
" tos : XByteField = (0)\n",
" len : ShortField = (None)\n",
" id : ShortField = (1)\n",
" flags : FlagsField (3 bits) = (0)\n",
" MF, DF, evil\n",
" frag : BitField (13 bits) = (0)\n",
" ttl : ByteField = (64)\n",
" proto : ByteEnumField = (0)\n",
" chksum : XShortField = (None)\n",
" src : SourceIPField (Emph) = (None)\n",
" dst : DestIPField (Emph) = (None)\n",
" options : PacketListField = ([])"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create a new packet to a specific IP destination. With Scapy, each protocol field can be specified. As shown in the `ls()` output, the interesting field is `dst`.\n",
"\n",
"Scapy packets are objects with some useful methods, such as `summary()`."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"p = Ether()/IP(dst=\"www.secdev.org\")/TCP()\n",
"p.summary()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 3,
"text": [
"\"Ether / IP / TCP 172.20.10.2:ftp_data > Net('www.secdev.org'):http S\""
]
}
],
"prompt_number": 3
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are not many differences with the previous example. However, Scapy used the specific destination to perform some magic tricks !\n",
"\n",
"Using internal mechanisms (such as DNS resolution, routing table and ARP resolution), Scapy has automatically set fields necessary to send the packet. This fields can of course be accessed and displayed."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print p.dst # first layer that has an src field, here Ether\n",
"print p[IP].src # explicitly access the src field of the IP layer\n",
"\n",
"# sprintf() is a useful method to display fields\n",
"Scapy uses default values that work most of the time. For example, `TCP()` is a SYN segment to port 80."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print p.sprintf(\"%TCP.flags% %TCP.dport%\")"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"S http\n"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Moreover, Scapy has implicit packets. For example, they are useful to make the TTL field value vary from 1 to 5 to mimic traceroute."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"[p for p in IP(ttl=(1,5))/ICMP()]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 11,
"text": [
"[<IP frag=0 ttl=1 proto=icmp |<ICMP |>>,\n",
" <IP frag=0 ttl=2 proto=icmp |<ICMP |>>,\n",
" <IP frag=0 ttl=3 proto=icmp |<ICMP |>>,\n",
" <IP frag=0 ttl=4 proto=icmp |<ICMP |>>,\n",
" <IP frag=0 ttl=5 proto=icmp |<ICMP |>>]"
]
}
],
"prompt_number": 11
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Sending and receiving"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Currently, you know how to build packets with Scapy. The next step is to send them over the network !\n",
"\n",
"The `sr1()` function sends a packet and return the corresponding answer. `srp1()` does the same for layer two packets, i.e. Ethernet. If you are only interested in sending packets `send()` is your friend.\n",
"\n",
"As an example, we can use the DNS protocol to get www.example.com IPv4 address."
"Sniffing the network is a straightforward as sending and receiving packets. The `sniff()` function returns a list of Scapy packets, that can be manipulated as previously described."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"s = sniff(count=2)\n",
"s"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 52,
"text": [
"<Sniffed: TCP:0 UDP:2 ICMP:0 Other:0>"
]
}
],
"prompt_number": 52
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`sniff()` has many arguments. The `prn` one accepts a function name that will be called on received packets. Using the `lambda` keyword, Scapy could be used to mimic the `tshark` command behavior."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"sniff(count=2, prn=lambda p: p.summary())"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Ether / IP / TCP 172.20.10.2:52664 > 216.58.208.200:https A\n",
"Ether / IP / TCP 216.58.208.200:https > 172.20.10.2:52664 A\n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 53,
"text": [
"<Sniffed: TCP:2 UDP:0 ICMP:0 Other:0>"
]
}
],
"prompt_number": 53
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively, Scapy can use OS sockets to send and receive packets. The following example assigns an UDP socket to a Scapy `StreamSocket`, which is then used to query www.example.com IPv4 address.\n",
"Unlike other Scapy sockets, `StreamSockets` do not require root privileges."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import socket\n",
"\n",
"sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # create an UDP socket\n",
"sck.connect((\"8.8.8.8\", 53)) # connect to 8.8.8.8 on 53/UDP\n",
"\n",
"# Create the StreamSocket and gives the class used to decode the answer\n",
"Scapy has a `traceroute()` function, which basically runs a `sr(IP(ttl=(1..30))` and creates a `TracerouteResult` object, which is a specific subclass of `SndRcvList()`."
"Scapy can be easily extended to support new protocols.\n",
"\n",
"The following example defines DNS over TCP. The `DNSTCP` class inherits from `Packet` and defines two field: the length, and the real DNS message. The `length_of` and `length_from` arguments link the `len` and `dns` fields together. Scapy will be able to automatically compute the `len` value."
"NFQUEUE is an iptables target than can be used to transfer packets to userland process. As a nfqueue module is available in Python, you can take advantage of this Linux feature to perform Scapy based MiTM.\n",
"\n",
"This example intercepts ICMP Echo request messages sent to 8.8.8.8, sent with the ping command, and modify their sequence numbers. In order to pass packets to Scapy, the following `iptable` command put packets into the NFQUEUE #2807:\n",
" s = payload.get_data() # get and parse the packet\n",
" p = IP(s)\n",
"\n",
" # Check if the packet is an ICMP Echo Request to 8.8.8.8\n",
" if p.dst == \"8.8.8.8\" and ICMP in p:\n",
" # Delete checksums to force Scapy to compute them\n",
" del(p[IP].chksum, p[ICMP].chksum)\n",
" \n",
" # Set the ICMP sequence number to 0\n",
" p[ICMP].seq = 0\n",
" \n",
" # Let the modified packet go through\n",
" ret = payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(p), len(p))\n",
" \n",
" else:\n",
" # Accept all packets\n",
" payload.set_verdict(nfqueue.NF_ACCEPT)\n",
"\n",
" # Get an NFQUEUE handler\n",
" q = nfqueue.queue()\n",
" # Set the function that will be call on each received packet\n",
" q.set_callback(scapy_cb)\n",
" # Open the queue & start parsing packes\n",
" q.fast_open(2807, socket.AF_INET)\n",
" q.try_run()"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Automaton"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When more logic is needed, Scapy provides a clever way abstraction to define an automaton. In a nutshell, you need to define an object that inherits from `Automaton`, and implement specific methods:\n",
"- states: using the `@ATMT.state` decorator. They usually do nothing\n",
"- conditions: using the `@ATMT.condition` and `@ATMT.receive_condition` decorators. They describe how to go from one state to another\n",
"- actions: using the `ATMT.action` decorator. They describe what to do, like sending a back, when changing state"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following example does nothing more than trying to mimic a TCP scanner:"
"Pipes are an advanced Scapy feature that aims sniffing, modifying and printing packets. The API provides several buildings blocks. All of them, have high entries and exits (>>) as well as low (>) ones.\n",
"\n",
"For example, the `CliFeeder` is used to send message from the Python command line to a low exit. It can be combined to the `InjectSink` that reads message on its low entry and inject them to the specified network interface. These blocks can be combined as follows:"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"# Instanciate the blocks\n",
"clf = CLIFeeder()\n",
"ijs = InjectSink(\"enx3495db043a28\")\n",
"\n",
"# Plug blocks together\n",
"clf > ijs\n",
"\n",
"# Create and start the engine\n",
"pe = PipeEngine(clf)\n",
"pe.start()"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"Packet can be sent using the following command on the prompt:\n",