Let’s learn About Scapy
Scapy is a packet manipulation tool for computer networks, originally written in Python by Philippe Biondi. It can forge or decode packets, send them on the wire, capture them, and match requests and replies. It can also handle tasks like scanning, tracerouting, probing, unit tests, attacks, and network discovery.
Stealing Email Credentials
You have already spent some time getting into the nuts and bolts of sniff- ing in Python. So let’s get to know Scapy’s interface for sniffing packets and dissecting their contents. We are going to build a very simple sniffer to cap- ture SMTP, POP3, and IMAP credentials. Later, by coupling our sniffer with our Address Resolution Protocol (ARP) poisoning man-in-the-middle (MITM) attack, we can easily steal credentials from other machines on the network. This technique can of course be applied to any protocol or to sim- ply suck in all traffic and store it in a PCAP file for analysis, which we will also demonstrate.
To get a feel for Scapy, let’s start by building a skeleton sniffer that sim- ply dissects and dumps the packets out. The aptly named sniff function looks like the following:
The filter parameter allows us to specify a BPF (Wireshark-style) filter to the packets that Scapy sniffs, which can be left blank to sniff all packets. For example, to sniff all HTTP packets you would use a BPF filter of tcp port 80. The iface parameter tells the sniffer which network interface to sniff on; if left blank, Scapy will sniff on all interfaces. The prn parameter specifies a callback function to be called for every packet that matches the filter, and the callback function receives the packet object as its single parameter. The count parameter specifies how many packets you want to sniff; if left blank, Scapy will sniff indefinitely.
Let’s start by creating a simple sniffer that sniffs a packet and dumps its contents. We’ll then expand it to only sniff email-related commands. Crack open mail_sniffer.py and jam out the following code:
from scapy.all import * # our packet callback def packet_callback(packet): print packet.show()
We start by defining our callback function that will receive each sniffed packet.
# fire up our sniffer sniff(prn=packet_callback,count=1)
Now we will simply tell Scapy to start sniffing on all interfaces with no filtering.
Now let’s run the script and you should see output similar to what you see below.
$ python2.7 mail_sniffer.py WARNING: No route found for IPv6 destination :: (no default route?) ###[ Ethernet ]### dst = 10:40:f3:ab:71:02 src = 00:18:e7:ff:5c:f8 type = 0x800 ###[ IP ]### version = 4L ihl = 5L tos = 0x0 len = 52 id = 35232 flags = DF frag = 0L ttl = 51 proto = tcp chksum = 0x4a51 src = 184.108.40.206 dst = 192.168.0.198 \options \ ###[ TCP ]### sport = etlservicemgr dport = 54000 seq = 4154787032 ack = 2619128538 dataofs = 8L reserved = 0L flags = A window = 330 chksum = 0x80a2 urgptr = 0 options = [('NOP', None), ('NOP', None), ('Timestamp', (1960913461,¬ 764897985))] None
How incredibly easy was that! We can see that when the first packet was received on the network, our callback function used the built-in function packet.show() to display the packet contents and to dissect some of the proto- col information. Using show() is a great way to debug scripts as you are going along to make sure you are capturing the output you want.
Now that we have our basic sniffer running, let’s apply a filter and add some logic to our callback function to peel out email-related authentication strings.
from scapy.all import * # our packet callback def packet_callback(packet): if packet[TCP].payload: mail_packet = str(packet[TCP].payload) if "user" in mail_packet.lower() or "pass" in mail_packet.lower():
When our callback function is called, we check to make sure it has a data payload and whether the payload contains the typical USER or PASS mail commands.
print "[*] Server: %s" % packet[IP].dst print "[*] %s" % packet[TCP].payload
If we detect an authentication string, we print out the server we are sending it to and the actual data bytes of the packet.
# fire up our sniffer sniff(filter="tcp port 110 or tcp port 25 or tcp port 143",prn=packet_callback,store=0)
Pretty straightforward stuff here. We changed our sniff function to add a filter that only includes traffic destined for the common mail ports 110 (POP3), 143 (IMAP), and SMTP (25). We also used a new parameter called store, which when set to 0 ensures that Scapy isn’t keep- ing the packets in memory. It’s a good idea to use this parameter if you intend to leave a long-term sniffer running because then you won’t be consuming vast amounts of RAM.
Lets Test Our Code
Here is some example output from a dummy email account I attempted to connect my mail client to:
[*] Server: 220.127.116.11 [*] USER thedarktech [*] Server: 18.104.22.168 [*] PASS sudopsxc [*] Server: 22.214.171.124 [*] USER thedarktech [*] Server: 126.96.36.199 [*] PASS test
You can see that my mail client is attempting to log in to the server at 188.8.131.52 and sending the plain text credentials over the wire. This is a really simple example of how you can take a Scapy sniffing script and turn it into a useful tool during penetration tests.
Sniffing your own traffic might be fun, but it’s always better to sniff with a friend, so in next article we will take a look at how you can perform an ARP poisoning attack to sniff the traffic of a target machine on the same network.