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:
>>> 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(Newpacket)
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:
>>> 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]
>>> 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()
>>> 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]
>>> 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.
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.
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.
- >>> 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()
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.
>>> 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.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
- srp():It returns the answered and unanswered packets
- srp1(): It returns only answered and sent packets
>>> 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.
0 comments:
Post a Comment