As tough as it is to avoid writing about this MongoDB fiasco currently going on, I wanted to take a moment and look at parsing DHCP traffic using Python. If you’ll recall, last week’s FPF post focused on understanding what DHCP packets tell us, and the various options within the data. Today’s post is going to be relatively short, and could easily blend into tomorrow’s script-focused content. Seeing as it’s parsing network traffic, I felt it appropriate to post on a Friday.
dpkt
My go-to module for parsing PCAPs with Python is dpkt. There are certainly other options available, however this is what I’ve used for a while and just stick with it. I won’t go through all the intricacies of dpkt, but rest assured it can handle DHCP traffic.
Parsing the Packet
First, we need to get the packet to a state where we can examine the DHCP data. With dpkt
, our first few lines look something like:
f = open(sys.argv[0])
pcap = dpkt.pcap.Reader(f)for ts, buf in pcap:
eth = dpkt.ethernet.Ethernet(buf)
ip = eth.data
udp = ip.data
Let’s examine the above, step by step. First, we open the PCAP file and pass it to dpkt’s Reader. The for
loop actually takes into account the timestamp of the frame, and then the buffer itself. Here’s an example of what the buffer looks like from our DHCP PCAP:
Within this data, we can see physical source and destination (remember we’re at the MAC layer right now), however I’m more interested in that data
field. Let’s break those out:
Now we’re starting to get to more familiar. We’ve got data we can expand into IP addresses, as well as the UDP datagram. For example:
Now, this is where we rely on dpkt to help us with some DHCP data. Let’s feed the UDP data to dpkt:
dh = dpkt.dhcp.DHCP(udp.data)
Output:
There we go! Remember our DHCP data that was found in Wireshark:
The decoded information includes the transaction ID (xid
) and the opts nested lists (opts
) that contain information about our DHCP content. Let’s bring the parsing together:
f = open(sys.argv[1])
pcap = dpkt.pcap.Reader(f)for ts, buf in pcap:
eth = dpkt.ethernet.Ethernet(buf)
ip = eth.data
udp = ip.data
dhcp = dpkt.dhcp.DHCP(udp.data) print "Source -> Destination"
print "{} -> {}".format(socket.inet_ntoa(ip.src), socket.inet_ntoa(ip.dst))
Output:
Note that in this really simple example, we just break out the IP data. Let’s dig into some of the DHCP opts
.
Defining DHCP Traffic
The first thing I wanted to do was define some of my DHCP traffic and correlation. Refer to the screenshot above for some of the DHCP options. For this example, I’m going to focus on the 53 option, which determines the DHCP Message Type.
dhcp_dict = {
1 : 'Subnet Mask',
50 : 'Requested IP Address',
53 : 'DHCP Message Type',
54 : 'DHCP Server Identifier',
55 : 'Parameter Request List',
58 : 'Renewal Time Value',
59 : 'Rebinding Time Value',
61 : 'Client Identifier',
255 : 'End'
}dict_53 = {
1 : 'Discover',
2 : 'Offer',
3 : 'Request',
5 : 'ACK'
}
I’m interesting in defining those options so I can add some context to my parsed packet. I can now let the opts
values tell me a bit about what’s going on with my packets.
I threw in the hyphens and new line just to help read output, but there we go! This was just a simple example of how to extract DHCP information from a PCAP. You could build in try/except statements to test for DHCP, and if so, move on. That would allow for passing an entire PCAP to the script and then extracting relevant DHCP information.
Until tomorrow, Happy Forensicating!