
Reinventing the low-level network tools would never be complete without famous Wireshark and I’m going to fill this gap today. How to perform network analysis with scapy?
Before we begin, I’d like to remind you that you can sign up to the newsletter to get info about latest post and support me on Patreon.
The objective
Let’s say we want to be able to eavesdrop some network communication, maybe to a blog which is not secured by HTTPS. Moreover, we want to be able to perform some analysis of the packets on the fly, so actually using wireshark may not be the perfect choice. At the same time we don’t want to collect rubbish from other communication that we have, so the ability to filter out packets is a must have.
Sniffer
We’re going to need the sniff
function from scapy’s library and two custom functions. The usage is very simple:
class Sniffer: # ... def run(self): sniff( filter='tcp', lfilter=self.my_filter, iface=self.interface, prn=self.display_filtered_packet )
We’re telling it to apply tcp
filter (more can be found in the docs), then also apply our locally defined my_filter
method. The last step is to process packets using display_filtered_packet
method. The filters
argument AFAIK takes exactly the same syntax as the wireshark does and it allows for quite advanced filters.
The my_filter
only checks if packet is sent from the desired host and port. It returns a boolean value so that indicating whether our code should pass or drop from further analysis.
def my_filter(self, packet: Packet) -> bool: return ( packet.getlayer(IP).src == self.host and packet.getlayer(TCP).sport == 80 )
Simple as it seems, this allows to do pattern matching inside packets. For example if I wanted to view only packets that contain information about redirects I could modify the above code and inspect payload with:
def my_filter(self, packet: Packet) -> bool: return ( packet.getlayer(IP).src == self.host and packet.getlayer(TCP).sport == 80 and b'301' in bytes(packet.payload) )
Which would result in displaying:
$ sudo python3 sniffer.py 46.242.253.15 en0
b'E\x02\x01(\x9f\xc2@\x00:\x06\xb1\xfb.\xf2\xfd\x0f\xc0\xa8\x01f\x00P\xc3\x1f\xd0\xfc\x81\xefF\x05\x17\xe1\x80\x18\x00\xeb_\xb2\x00\x00\x01\x01\x08\n\xf4`\x1e\x861\xf8\t\xffHTTP/1.1 301 Moved Permanently\r\nDate: Tue, 17 Mar 2020 06:42:37 GMT\r\nContent-Type: text/html; charset=UTF-8\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nServer: Apache\r\nX-Redirect-By: WordPress\r\nLocation: https://blacksheephacks.pl/\r\n\r\n'
Talking of which brings us to the last point.
Analyze network with bare eye
The above code was displayed with this simple function we passed as prn
argument:
def display_filtered_packet(self, packet: Packet): return packet.payload
It is, however, barely readable. In order to improve it we can modify it the following way:
def display_filtered_packet(self, packet: Packet): return ''.join( chr(ss) for ss in filter( lambda s: chr(s) in string.printable, bytes(packet.getlayer(TCP).payload) ) )
And finally it’ll display:
$ sudo python3 sniffer.py 46.242.253.15 en0
HTTP/1.1 301 Moved Permanently
Date: Tue, 17 Mar 2020 07:03:08 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: Apache
X-Redirect-By: WordPress
Location: https://blacksheephacks.pl/
0
That’s it folks.
As always I encourage you to subscribe to the newsletter, support me on Patreon and make use of the following links:
- Source code on Gitlab – not only you’ll find full example of this network analysis with scapy, but also find an interesting bonus 😉
- Scapy docs
Check out other posts: