And now for something completely different

In addition to writing nerdy poetry to shill for my business, I occasionally do technical work as well. One of my recent projects involves using the scapy suite. scapy is an interesting package that lets you craft and send frames and packets with a very fine level of control, and receive the responses. Better yet, scapy is implemented as a Python library, making it very easy to incorporate it in scripts, greatly increasing its flexibility.

I started using scapy more than a year ago to answer the following question. When IPv6 was promulgated, rather than continuing to rely on Address Resolution Protocol (ARP) for hardware address discovery, the designers created Neighbor Discovery Protocol (NDP) instead. NDP is certainly more robust, but ultimately the process for discovering hardware addresses is substantively the same for both ARP and NDP. Moreover, ARP was designed to be extensible; if you read the ARP standard, there is no technical reason it cannot be used to resolve IPv6 addresses as well.

So this made me wonder if it would work, which meant I needed to craft an ARP packet on a field by field basis. Intellectually it’s not difficult, but you need the tools to do it, and scapy was up to the job. Of course, it didn’t work, because no other system in the universe is written to resolve IPv6 addresses via ARP, but it was a good introduction to the world of packet crafting.

My current problem is that I want to connect to a web service on a particular device in an automated manner that will let me run many tests. Moreover, I’m interested in having very granular control over the packet contents for the connection. There are other, potentially easier tools that I could use, but the problem with many tools that automate network connections is that they make assumptions about what you’re trying to do, and try to “help”. For what I’m doing, I don’t want help…I want to control exactly what is sent. This makes scapy a great choice.

But the downside of scapy is that it doesn’t try to help, as I discovered when I went to perform the TCP 3-way handshake; there’s no “establish TCP connection” function there. Fortunately Google is my friend, and I found several sites with instructions about how to set this up…most describe similar techniques to this site.

There’s a very interesting wrinkle in the problem: scapy does not use kernel networking functions (and I should mention that I’m running an up-to-date CentOS 6 distro); its access to the wire is truly raw. So think about what happens in the three-way handshake:
1. scapy sends a TCP SYN packet to the target. It lets us pick the source port (say 10000), and we’ll say the destination port is 80.
2. The target receives the TCP SYN and replies with a TCP SYNACK packet to the machine running scapy.
3. The network interface on the machine running scapy gets the SYNACK. scapy sees the SYNACK (since it is sniffing on the interface), but so does the kernel.
4. scapy understands the SYNACK as the second element of the 3-way handshake, and replies with an ACK. But the kernel views the SYNACK as unsolicited (remember that scapy is not opening a socket with the kernel, so the kernel believes there is no process bound to the destination port [10000] of the SYNACK) and does what every good kernel should do in such a situation; it sends a TCP RST to the sender.

This TCP reset packet totally jacks up what scapy is trying to do. This is a known problem; the standard advice is to create an iptables filter rule that drops any TCP RST packets sent to the target host with our chosen source and destination ports. Under normal circumstances, this solves the problem: no TCP RST, scapy finishes the 3-way handshake and then delivers its payload. Life is good.

But there is a problem. In my use case, I want to repeatedly connect to the web service, which means that I want to establish my connection, send my payload, get the response, then tear down the connection. The proper way to tear down the connection is to use the graceful FIN/ACK procedure for TCP, but there are two problems: I’m lazy, and this would require me to implement yet more of the TCP protocol in scapy…remember that scapy is not really helping me with TCP. It is by far easier (and quicker from a testing perspective) to just send a TCP RST to close the connection.

Hopefully you see my problem: TCP RST packets from my chosen source port to the target destination port are exactly the packets that are blocked by iptables. I now want scapy to do exactly what I didn’t want the kernel to do for me a minute ago, and iptables won’t easily let me have it both ways.

Well, the filter table in iptables won’t, but there’s an interesting feature of iptables called the raw table. The raw table is seldom used; I used to teach an introductory module on iptables, and the raw table received only an existential mention. But the beauty of the raw table is that it allows you to drop packets before they are ever seen by the kernel. Of course, there’s a risk that they will be dropped before reaching scapy too, but this turns out not to be the case. Instead of letting the kernel detect the packets from the target and blocking the resets it tries to send, instead I used the raw table to block the return packets from ever being seen by the kernel.

The syntax is pretty straightforward:
iptables -t raw -A PREROUTING -p TCP --dport 10000 -j DROP

Of course, you should probably be more specific in your filter matching, as this blocks all incoming packets to port 10000, which is a bit of overkill, especially when you forget to remove the rule. But in this way, scapy still sees the replies from the target, but the kernel does not, so it doesn’t interfere. Neat, huh?

Comments are closed.