A Packet Viewer and Manipulator for Scapy
Proudly introducing the Packet Viewer (and Manipulator), a Wireshark-like protocol sniffer and decoder with live packet manipulation, sending and replaying capabilities.
Before diving in, let’s recap some basics first. If you’re familiar with Scapy, feel free to skip to “A TUI for Scapy” right away.
Short introduction to Scapy
Scapy is a framework for network packet manipulation. It is written in Python and supports Python 2 (still) and 3. It is very flexible and can not only be used to receive packets but also to send packets. Simple packet definitions take only a few lines of code which are easily readable.
For example, let’s define a packet which contains only two bytes:
|
|
We define a byte field using ByteField
(wow!) and assign a name (fieldX
) to them. The second parameter (0
) the default value.
Now we can create a packet with the following line:
|
|
Finally, we can check which byte string is generated by this packet.
|
|
Value | Interpretation |
---|---|
B | This is the ASCII representation of the hex value 0x42. |
\x00 | This is the default value of “field2”. The \x__ notation is defined by Python, not by Scapy. |
This is a very simple example.
Let’s look at a more complex, but known example: UDP.
|
|
Instead of ByteField
s, it contains ShortField
s (2 bytes). Additionally, it includes two special kinds of ShortField
: Two ShortEnumField
s and one XShortField
.
UDP_SERVICES
is a dictionary with maps ports to their human-readable names.
Scapy sets the default port to 53 which is the standard port used for DNS.
The value of an XShortField
is represented as hex. So these are just eye candy.
Now some code to see it in action:
|
|
Here, the mapping is visible. The port 80 has been translated to “www_http”.
Unfortunately, len
and chksum
are still None
. For sure, they need to be set for a proper UDS packet before sending it.
But let’s first check the bytes Scapy generates for this packet.
|
|
Value | Interpretation |
---|---|
\xfd\xe8 | This is the sport or the source port[, if you’re not a fan] |
\x00P | The dport or destination port (P = 0x80) |
\x00\x08 | len |
\x00\x00 | chksum |
Scapy has taken some work off our hands. The len is already set properly. The chksum is 0, because the underlayer to UDP is missing (IP) and thus Scapy doesn’t know how to calculate the checksum.
This can be resolved by stacking an IP and a UDP packet. For this, Scapy uses the division operator.
|
|
After building it, this packet shows the following output:
|
|
There are many default values, e.g. the localhost addresses. Furthermore, chksum
has been automatically calculated for us, so no reason to dig into the UDP specification.
We have packets now but we didn’t send them yet. This is a very simple one-liner.
|
|
Note: This requires root privileges on Linux because it uses a RAW socket, a feature which is only accessible for super users.
To receive packets, we can use the sniff
function.
|
|
The count
parameter defines how many packets should be received until this call returns.
A TUI for Scapy
In summary, Scapy offers easy pythonic access to protocols, network layers, sockets, and everything else required for networking tasks. What it lacked - until now - was some sort of Wireshark-like UI, embracing Scapy’s power and flexibility, while offering developers, pentesters and other Scapy users a tool to conveniently view, manipulate and generate traffic.
And that’s exactly what our packet viewer offers. Let’s have a first look at it:
The packet viewer is a terminal-based UI (TUI), inspired in look-and-feel by our old friend htop
. The packet viewer is easily launched right from Scapy’s interactive prompt by a call to the viewer
function. All you need to get started is a socket! By passing the basecls
of the packets you expect to receive and send via the socket, the viewer can derive useful columns to show for the packets that come in.
The viewer, which is based on urwid, can be navigated by keyboard and mouse, both of which work even via SSH! It comes with a few useful features that are always available regardless of the protocol that is being sent/received:
Button | Description |
---|---|
F2 | Replay the packet that is currently selected. |
F3 | Pause and resume the sniffing. |
F4 | Quit the viewer. |
F5 | Filter the received packets by some condition. The condition is given as a Python expression, which is called once for each packet and is expected to return either True or False , which results in the packet being checked or not. |
F6 | Display the currently selected packet as a Python expression, allow to modify that expression, and send the resulting new packet. |
F7 | Edit the currently selected packet in-place. |
The features to provide can be modified, e.g. by passing the views
parameter to the call to viewer
, but it doesn’t stop here: new features can easily be added using our simple plugin API! We will introduce one such plugin in the next section.
When quitting the viewer, the call to viewer
returns both a list of all packets that were received, and the packets that were selected before quitting. That way, the viewer can even be integrated into bigger scripts outside of Scapy’s interactive prompt!
Plugin: CAN Packet Structure Analysis
The first plugin available for the packet viewer is a frontend to our CAN packet structure analysis tool revdbc.
Short Introduction: CAN Packet Structure Analysis
Controller Area Network (CAN) is a message-based protocol used in automotives to connect (some of) the numerous electronic control units (ECUs). While sometimes used directly for communication between ECUs, it’s also the basis for various higher-level protocols built on top of it.
DBC (CAN Database) is a proprietary file format used to describe the structure of CAN packets. The up to eight bytes of data are split into so-called “signals”, which can be simple value signals, enumerations or multiplexers. The specifics are not relevant here, but have a look at that beautiful, amazingly well-readable format!!
|
|
The meaning of these lines is obvious right away, amirite? (Example taken from here)
Automated DBC Reverse-Engineering
Now, as you might have guessed, automotive producers keep the structure of their CAN packets secret. For security analysts, pentesters or evil hackers, however, the structures are important information and need to be reverse-engineered. There are various approaches to do so, from fully manual observation of traffic to active systems that hook into a car and attempt to trigger packets for more targeted information gathering.
This plugin gives a frontend to revdbc, which tries to automatically reverse-engineer CAN packet structures through statistical analysis. That is, the tool takes a (preferably large) log of CAN packets as input, and tries to deduce packet structure information by observing changes in the bits and bytes over time. For more information, refer to revdbc’s documentation.
And this is what it looks like!
As mentioned previously, features can be accessed using keybindings. The default features are bound to the F2 - F7 keys of your keyboard. Features added via the plugin API are automatically bound to the next free F
key (e.g. F8).
The red box contains the fully expanded view of the plugin.
Description of the green boxes:
Number | Description |
---|---|
2 | Status of the analysis. E.g. Analysis Runing/Done/Outdated |
3 | The reverse-engineered packet structure as ASCII-art |
4 | Various actions, e.g. the possibility to save the reverse-engineered structure to a DBC file. |
5 | A table of the reverse-engineered signals. Fully editable! |
6 | Multiple tabs to select which data to show in (7) |
7 | A graph, showing either the interpreted value of the currently selected signal for each packet, the number of bitflips for each bit of the signal, or the correlation between these bitflips. The last two of these are metrics used during analysis to deduce the structure itself. |
All of this was built with urwid and on top of the packet viewer’s plugin API. It runs the analysis in a different process and updates the UI asynchronously, to not block user input.