Python Packet Play

As X-mas challenge for the Owasp Appsec Research 2010 conference, me and Mario Heiderich set up a Capture-the-flag at a computer. While .mario did all the tricky and brainteasing stuff with the actual challenge, I created a diversionary target which let me play a bit with low level packet shuffling in Python.

The idea I had in mind was that I wanted the computer to
- Appear as having lots and lots of open ports
- Each open port should appear to run some service
- Each service should be recognised by nmap

- But actually, the incoming packets should be dropped

I got the inspiration for this when reading up on the Sockstress implementation. What we want to avoid is to have any state on the server side, since this is supposed to run on a low-end VPS-machine without affecting the other part of the system. So, how can this be achieved ? This is the basic idea:

1. Using iptables, set up so that packets to the desired ports are put into a netfilter queue.
2. Using python netfilter module, connect the netfilter queue to a python program.
3. In the python program, create a suitable response packet, and send this over a raw socket.
3a. On a SYN-packet, respond with a SYN-ACK. (This makes the port look open). The other part will finish the three-part handshake with an ACK packet.
3b. On the ACK-packet, respond with two piggybacked packets. First, an ACK/PSH with some data that looks like some valid service, e.g “Windows FTP server” or “A-311 Death welcome”. Secondly, close the connection with a FIN/ACK packet.
4. For extra credits, try to answer prudently to any other packets that may come in. Also, try to avoid endless repeating by e.g checking sequence numbers.
5. Remember that the netfilter queue from iptables still waits for a judgement on what to do with this packet? Respond to the kernel with a “DROP” signal. The OS now drops the packet and forgets all about it.

Iptables

To set up so we can fiddle the packets in python, we do this :

#Create a new iptable chain called fuzzywall
iptables -N fuzzywall
#
# Fuzzywall
# We only run it on port 1337 to start off
# - Optionally : send to logging facilties (can be found in syslog)
#iptables -A fuzzywall -p tcp --dport 1337 -j LOG --log-prefix "Sending to queue"
#Add rule to 'fuzzywall', if dest port is 1337, send to netfilter queue
iptables -A fuzzywall -p tcp --dport 1337 -j QUEUE

#Add our chain to INPUT
iptables -A INPUT -j fuzzywall

So, that puts fuzzywall in the INPUT chain. All incoming packets to port 1337 now goes to the netfilter queue.

Python netfilter

We need to get the kernel netfilter queue into our python program. To do this, I used nfqueue library from Pollux (http://www.wzdftpd.net/blog/index.php?2008/06/01/22-nfqueue-bindings ). It is very easy to set up :
q = nfqueue.queue()
q.open()
q.bind(AF_INET)
q.set_callback(callback)
q.create_queue(0)
q.set_queue_maxlen(5000)
print "starting"
try:
q.try_run()
except KeyboardInterrupt, e:
print "interrupted"

print "unbinding"
q.unbind(AF_INET)
print "closing"
q.close()

In addition, a handler needs to be specified which can return the verdict back to iptables :

def callback(dummy, payload):
data = payload.get_data()

handler.handlePacket(data)

payload.set_verdict(nfqueue.NF_DROP)
return

And, of course, it is in handler.handlePacket where the packetcrafting is peformed.

Python Packetcrafting

In order to play with packets, I found two libraries that could be used; impacket (http://pypi.python.org/pypi/Impacket/0.9.5) and dpkt (http://code.google.com/p/dpkt/). They have a few differences, but both can be used to parse and create all kinds of packets. In the end, I used both of them (which maybe was a bit suboptimal – but worth it just to test how they worked). This example uses a dpkt-packet parsed from raw data and generates a default response packet using impacket:

def createReturnPacket(self,packet,seqOffset = 0):

dst = inet_ntoa(packet.dst)
src = inet_ntoa(packet.src)
sport = packet.tcp.sport
dport = packet.tcp.dport

#set the IPs
ip = ImpactPacket.IP()
ip.set_ip_src(dst)
ip.set_ip_dst(src)
#set the ports
tcp = ImpactPacket.TCP()
tcp.set_th_sport(dport)
tcp.set_th_dport(sport)
#Set a window size
tcp.set_th_win(10)
#Bundle the tcp packet inside the ip-packet
ip.contains(tcp)
return (ip,tcp,src)

I can now use this packet to create a SYN/ACK in response to a SYN :
#Is this a SYN or an ACK?
if (in_packet.tcp.flags & dpkt.tcp.TH_SYN) != 0:
out_tcp_packet.set_SYN()
out_tcp_packet.set_ACK()
out_tcp_packet.set_th_seq(0)
out_tcp_packet.set_th_ack(in_packet.tcp.seq+1);
self._send(dst, out_ip_packet)

Once an appropriate return packet is created, it can be sent raw on the socket :

def _send(self, dst, ip):
s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)
s.setsockopt(IPPROTO_IP, IP_HDRINCL, 1)
s.sendto(ip.get_packet(), (dst, 0))
return

Fooling nmap

The desired functinality was that if an nmap service version scan was performed, such as:
nmap -p 1337 -sV

Nmap should score a match. When a service version in nmap is performed, nmap sends so called ‘probes’ to the port and checks what is returned. It has a list of fingerprints for known services. For example, one service probe called “TCP GetRequest” sends “GET / HTTP/1.0\r\n\r\n”, and if the response matches “HTTP/1\.[01] \d\d\d.*\r\nDate: .*\r\nServer: Apache\r\n”, it is identified as an Apache server. If it does NOT match, nmap goes through the rest of the probes – which we definitely want to avoid, as we want as little overhead as possible.

First of all, nmap has the so called ‘Null probe’, which means that it connects to the port and waits for a few seconds to check if the service will volunteer some info without the client sending any data. It is in this segment I wanted to have my matches, since it is the first test. All service fingerprints are located in a file called nmap-service-probes, and look something like this :

match acap m|^\* ACAP \(IMPLEMENTATION \"CommuniGate Pro ACAP (\d[-.\w]+)\"\) | p/CommuniGate Pro ACAP server/ i/for mail client preference sharing/ v/$1/

To match ‘acap’, we must send some data that matches the expression after between the first pipes. It is trivial to write such a match : \* ACAP (IMPLEMENTATION “CommuniGate Pro ACAP 3.3″) would be identified as acap version 3.3. However, there are a couple of thousand fingerprints on the Null probe, and I want to randomly use them all. Therefore, I wrote a tool to reverse the regexps and put them into a python declaration.

Regexp reverse engineering

I was surprised to find that I could not locate any existing library to reverse regular expressions in the way I wanted : from a regexp, create ONE simple string that matches it. The closest I could find was a perl implementation which found as many matches it could given a regexp and a fixed length. Therefore, I wrote my own tool.

Maybe it is pretty simple to reverse a regexp, if you are a great programmer who knows what you are doing. It took me a while, though – and still I used the parts building the object-tree represention of the regexp from the built-in python regexp implementation. Basically, it does this :
* Remove all optional groups (foo)* => gone
* Insert 1 for digits, insert a for alpha, etc…
* Insert the first char in any group-of-characters [abcd] => a
* Insert the first in any OR (abc|def) => abc
… the list goes on a bit. It gets tricky when it comes to group-references and stuff. The source is available at http://martin.swende.se/hgwebdir.cgi/pxeger/file/tip/Revexpy.py

Putting it together

So, did it work? Well, yes :
#: nmap -p 82 66.249.7.26 -sV

PORT STATE SERVICE VERSION
82/tcp open ftp Xitami ftpd

...again...

PORT STATE SERVICE VERSION
82/tcp open smtp (Wanadoo blocks smtp - NOT A REAL smtpd!)

...again...

PORT STATE SERVICE VERSION
82/tcp open nngs No Name Go Server

Alas, also no. After a while, the python process died – probably because it was running on a machine with 128 MB of memory, which it had to share with Apache and Mysql…

Disclaimer

While this was a really fun project which touched on a lot of interesting fields, I did not do it as a security-tool to actually use in a real scenario.

All the codez are available at:
http://martin.swende.se/hgwebdir.cgi/fuzzywall/
http://martin.swende.se/hgwebdir.cgi/pxeger/

2 Responses to “Python Packet Play”

  1. Efraim Xavier Jesus Bordello II Says:

    Pretty cool stuff. Well done.

  2. Jonas Says:

    Va? Fattar ingenting alltsĂ„…

Leave a Reply