A network is an essential part of any cyber infrastructure. There are various tools available for the networking part of pentesting and other security assessment tasks like Nmap, tcpdump, arpspoof, etc., but one tool which stands out of all is Scapy.

Scapy is a powerful interactive packet manipulation tool written in Python, and the best part is that it can also be utilized as a library in Python programs, which provides the pentester the ability to create his/her own tool based on the requirement. In this article we will discuss how we can use Scapy as an interactive tool as well as a library in our programs (Python). It allows us to sniff, create, send and slice packets for analysis.

Most of the tools are built with something specific in mind, like Nmap for network scanning or Wireshark for sniffing, but Scapy allows us to build something new utilizing its functionalities and hence opens up a whole new world of networking applications. Unlike other tools which provide an interpreted output of the query, Scapy will present a raw output of any query that we make and let us decide what we need out of it and how to interpret it. This specific advantage of the tool is very helpful during the advanced analysis of the network. Using Scapy we can create and send custom packets over the network and analyze the raw output received with a minimal amount of lines of code, and it supports a wide range of protocols for the purpose.

Before going into the details of Scapy, here are few terminologies that need to be discussed:

  • Scanning: The act of probing a host machine to identify any specific detail about it. Eg. Port scanning.
  • Sniffing: The act of intercepting and logging the packets which flow across the network.
  • Fuzzing: A software testing technique in which random data is passed as input to a computer application to check its stability.

Scapy provides various commands from basic to advanced level for probing a network. Let’s start with some basic commands for interactive usage:

  • >>> ls(): Displays all the protocols supported by Scapy, as shown in figure 1.
  • >>> lsc(): Displays the list of commands supported by Scapy, as shown in figure 2.
  • >>> conf: Displays configurations options.
  • >>> help(): Display help on a specific command. Usage example: help(sniff)
  • >>> show(): Display the details about a specific packet. Usage example: Newpacket.show()

Using the above mentioned command would be helpful to further explore the tool.

Figure 1. Output of commands ls() and conf

Figure 2. Output of command lsc()

Scapy allows us to create custom packets based on the huge set of protocols that it supports. Let us see how we can create simple packets:

>>> Newpacket=IP(dst=’google.com’)

>>> Newpacket.ttl=10

>>> Newpacket.show()

We can also create sets of packets based on our requirements. Here is an example of simple IP packets for different port addresses.

>>> basepkt=IP(dst= “www.google.com”)

>>> pktport=TCP(dport=[80,443])

>>> [p for p in basepkt/pktport]

Now when we have created packets we need to send these packets over the network. We have two options for this purpose:

  • send(), which is a layer 3 send. It decides the routing based on local table.
  • sendp(), which is a layer 2 send.

To send our packet we are using send(), as shown in figure 3:

>>> send(Newpacket)

Figure 3. Packet creation and sending

To see if the packet is really sent we can utilize any sniffer like Wireshark or tcpdump. Although Scapy also provides the functionality of sniffing, which we will see later in the article.

Want to learn more?? The InfoSec Institute Ethical Hacking course goes in-depth into the techniques used by malicious, black hat hackers with attention getting lectures and hands-on lab exercises. While these hacking skills can be used for malicious purposes, this class teaches you how to use the same hacking techniques to perform a white-hat, ethical hack, on your organization. You leave with the ability to quantitatively assess and measure threats to information assets; and discover where your organization is most vulnerable to black hat hackers. Some features of this course include:

  • Dual Certification - CEH and CPT
  • 5 days of Intensive Hands-On Labs
  • Expert Instruction
  • CTF exercises in the evening
  • Most up-to-date proprietary courseware available

We can create a ping echo request packet by simply adding the ICMP protocol after our previous packet.

>>> Newpacket=IP(dst=”google.com”)/ICMP()

The operator ‘/’ is used as a composite operator between two layers.

We can send this packet similar to our previous packet. To send the same packet again and again we can simply add the loop=1 argument with the send packet.

>>> send(Newpacket, loop=1)

As we have seen how to create simple packets and send them, now we should see how to send and also receive packets. This functionality is very useful when we need to send some packets and we expect a response for those packets, like an ARP request. Again there are two types based on the layers the packets are sent and received:

Layer3:

  • sr(): It returns the answered and unanswered packets
  • sr1(): It returns only answered and sent packets

Layew2:

  • srp():It returns the answered and unanswered packets
  • srp1(): It returns only answered and sent packets

Let’s see an example of the sr function.

>>> output=sr(IP(dst=”google.com”)/ICMP())

output

We see that the ‘output’ contains two different results, ‘Results’ and ‘Unanswered’. The first part contains the packets received as response and the second part contains the packets which were not answered. So we can divide it into two parts:

>>> result, unanswered=output

>>> result

The output of the result shows that we got one ICMP packet as a reply, so we can see the raw packet we got in response by using the following command, as shown in figure 4.

>>> result[0]

Figure 4. Sending and receiving packets

If we look closely we can see that this is an echo reply packet for our echo request. Now if we want to see the current routing table of our machine, we can use the command:

>>> conf.route

Scapy allows us to include user specified routes to this table, without affecting the original table, this can be done by using the add function.

>>> conf.route.add(host=”192.168.118.2″, gw=”192.168.118.25″)

Now any packet intended for the host 192.168.118.2 would go through 192.168.118.25

After we are done using this table we can get back to the original table simply by using the resync function, as displayed in figure 5.

>>> conf.route.resync()

Figure 5. Configuring the routing table

Now that we have seen how to create simple packets, send them and receive them, let’s move forward to packet sniffing, so that we can analyze what is happening over the network . Packet sniffing can be done by the simple function sniff:

>>> a=sniff(filter=”icmp”, iface=”eth1″, timeout=10, count=3)

>>> a.summary()

>>> a[1]

As demonstrated in the example, the sniff function can sniff the packets and can also filter them based on the user requirements. Now to see the output in real time we can use the lambda function along with the show or summary function based on the amount of detail we require.

>>> a=sniff(filter=”icmp”, iface=”eth1″, count=3, timeout=10, prn=lambda x:x.summary())

Now as we have seen how easily we can sniff packets using Scapy, we also need to learn how to save these packets for later analysis and also how to read those saved files.

To save packets we can use the function wrpacp as shown below:

>>> wrpcap(“mypackets.pcap”, a)

Now if we need to read these packets we can simply use the function rdpcap, as shown in figure 6. As pcap format is supported by many sniffers like Wireshark, tcpdump etc., we can also analyze these files using them.

>>> rdpkt=rdpcap(“mypackets.pcap”)

>>> rdpkt.show()

>>> rdpkt[1]

Figure 6. Sniffing, writing and reading packets

As Scapy allows us to create custom packets, we can utilize this functionality to perform port scanning. Here is an example of how to perform some simple port scanning using the interactive interface. We will create a TCP/IP packet with the TCP flag set as ‘S’ (SYN) for port 1-1024.

>>> res,unans = sr( IP(dst=”192.168.118.1″)/TCP(flags=”S”, dport=(1,1024)))

The output can be analyzed by using the command

>>> res.summary()

Apart from packet creation, Scapy can also perform simple networking functions such as ping, traceroute etc. Example of a simple traceroute of google.com is shown here:

>>> traceroute(“www.google.com”)

Scapy also contains commands for some network based attacks such as arpcachepoison, etherleak, srpflood etc. These commands can be very useful during a network security analysis. If we need to discover the hosts on the local Ethernet we can use the command arping.

>>> arping(192.168.118.*)

Scapy also provides the functionality of fuzzing, utilizing the function fuzz, here is the example of a simple DNS fuzzer:

>>> send(IP(dst=”192.168.118.1″)/UDP()/fuzz(DNS()), inter=1,loop=1)

We have seen how we can use Scapy as a tool and use its various functions interactively. Now let’s see how to use Scapy in Python programs, through simple example codes. The example codes demonstrate how easily we can create programs in Python using the Scapy library and create powerful tools with minimum amount of coding.

The code shown below is a simple Python program which sends ARP requests and waits for response and displays the response.

#!/usr/bin/python

#import sys module for command line argument

import sys

#import scapy as a library

from scapy.all import *

print “Usage: scapy-arping eg: ./scapy-arping.py 192.168.1.0/24″

#create and send ARP request packets

rec,unans=srp(Ether(dst=”ff:ff:ff:ff:ff:ff”)/ARP(pdst=sys.argv[1]),timeout=2)

#print the result

for send,recv in rec:

print recv.sprintf(r”MAC: “+”%Ether.src%”+” <–> IP: “+” %ARP.psrc%”)

The example output of this program is shown below:

root@bt:~/Desktop# ./scapy-arping.py 192.168.118.0/24

WARNING: No route found for IPv6 destination :: (no default route?)

Usage: scapy-arping eg: ./scapy-arping.py 192.168.1.0/24

Begin emission:

**Finished to send 256 packets.

*

Received 3 packets, got 3 answers, remaining 253 packets

MAC: 00:50:56:f5:48:7a <–> IP: 192.168.118.2

MAC: 00:50:56:c0:00:08 <–> IP: 192.168.118.1

MAC: 00:50:56:f8:5e:b3 <–> IP: 192.168.118.254

Another example code for a simple ARP monitor is shown below (source: http://www.secdev.org/projects/scapy/doc/usage.html#recipes). The program simply monitors for any ARP request or reply and prints the associate MAC and IP address.

#! /usr/bin/env python

from scapy.all import *

def arp_monitor_callback(pkt):

if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at

return pkt.sprintf(“%ARP.hwsrc% %ARP.psrc%”)

sniff(prn=arp_monitor_callback, filter=”arp”, store=0)

Example output for the program is shown below:

root@bt:~/Desktop# ./arpmonitor.py

WARNING: No route found for IPv6 destination :: (no default route?)

00:50:56:c0:00:08 192.168.118.1

00:0c:29:d8:b6:4d 192.168.118.130

00:0c:29:d8:b6:4d 192.168.118.130

00:50:56:c0:00:08 192.168.118.1

Let’s see how we can create a simple DNS fuzzer using the fuzz function demonstrated in the description above.

#!/usr/bin/env python

#import module sys for command line argument

import sys

#import scapy as a library

from scapy.all import *

#fuzz dns

while True:

sr(IP(dst=sys.argv[1])/UDP()/fuzz(DNS()),inter=1,timeout=1)

Sample output of the DNS fuzzer created using scapy.

root@bt:~/Desktop# ./dnsfuzzer.py 192.168.118.1

WARNING: No route found for IPv6 destination :: (no default route?)

Begin emission:

.Finished to send 1 packets.

Received 1 packets, got 0 answers, remaining 1 packets

Begin emission:

Finished to send 1 packets.

Received 0 packets, got 0 answers, remaining 1 packets

Begin emission:

Finished to send 1 packets.

Received 0 packets, got 0 answers, remaining 1 packets

[...]

The output of all the sample programs is shown in figure 7.

Figure 7. Programs using Scapy

There are many other third party libraries available for packet manipulation in Python, like Pycapy, pypcap, dpkt, etc., yet Scapy turns out be one of the simplest to use and integrate into Python code and hence is widely used. There are many other functionalities provided by Scapy, which individually might seem very simple, but once they all are weaved together, they have the capabilities which no other tool provides.

Conclusion

We saw that Scapy is very powerful yet easy to use. Scapy is actually not a replacement for tools like Nmap, tcpdump or p0f. These tools are developed for specific needs and they all perform their functions very well. During a quick security assessment they come in handy and provide us the desired result, but sometimes we need the raw outputs, without any interpretation so that we can analyze and make decisions for ourselves. For example if we need to check if the system we are trying to parse is actually a honeypot or not, another example would be to test how a firewall/ IDP/ IPS behaves for different types of custom packets, then tools like Scapy are very useful.

The best thing about Scapy is that we can also use it as a Python library, which allows us to create networking tools very quickly without going into the details of creating raw packets from scratch, which considerably reduces the size of the code. It simply allows us try anything we can imagine over a network. The inbuilt functions like fuzz, sniff, traceroute, arping, etc. wipe out the need of different tools for different functions and integrate it all into a single package.