Creating ACK-GET packets with scapy
During the recent Defcon 19 CTF pre-qualifications, one of the challenges included to connect ‘quicker’ to a web server. While figuring out what the solution was for this challenge one of the things I tried was to send the HTTP GET request already in the TCP handshake stage. Sadly enough this had nothing to do with the real solution of this case, the real solution was making use of SPDY to connect to the server, this is also explained by one of the other teams in a write up here. However it was a nice exercise to create packets with scapy again and since I could not find a lot of information on the topic I decided to create this short write up on the topic.
Normal TCP Handshake
A normal TCP handshake contains three packets, a SYN packet which is being sent from the client to the server, on which the server replies with a SYN,ACK packet, on which the client on its turn replies with an ACK packet. This handshake is shown in the schematic overview below. More information on the TCP handshake can be found on Wikipedia here.
Normal TCP handshake overview:
Normal TCP handshake in Wireshark
We can create our own packet for this TCP handshake, to do this we will use scapy. A normal TCP handshake in scapy:
# scapy Welcome to Scapy (2.1.0) >>> ip=IP(dst="www.google.com") >>> port=RandNum(1024,65535) >>> SYN=ip/TCP(sport=port, dport=80, flags="S", seq=42) >>> SYNACK=sr1(SYN) Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets >>> ACK=ip/TCP(sport=SYNACK.dport, dport=80, flags="A", seq=SYNACK.ack, ack=SYNACK.seq + 1) >>> send(ACK) . Sent 1 packets. >>>
To make it a bit more easy to read and alter we can also build a Python script which makes use of scapy. Normal TCP handshake in a Python script making use of scapy:
#!/usr/bin/python # Change log level to suppress annoying IPv6 error import logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) # Import scapy from scapy.all import * # Print info header print "[*] TCP Handshake example -- Thijs 'Thice' Bosschert, 06-06-2011" # Set up target IP ip=IP(dst="www.google.com") # Generate random source port number port=RandNum(1024,65535) # Create SYN packet SYN=ip/TCP(sport=port, dport=80, flags="S", seq=42) # Send SYN and receive SYN,ACK print "\n[*] Sending SYN packet" SYNACK=sr1(SYN) print "\n[*] Receiving SYN,ACK packet" # Create ACK packet ACK=ip/TCP(sport=SYNACK.dport, dport=80, flags="A", seq=SYNACK.ack, ack=SYNACK.seq + 1) # SEND our ACK packet print "\n[*] Sending ACK packet" send(ACK) print "\n[*] Done!"
The Python script above gives the following output:
python TCP_handshake.py [*] TCP Handshake example -- Thijs 'Thice' Bosschert, 06-06-2011 [*] Sending SYN packet Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets [*] Receiving SYN,ACK packet [*] Sending ACK packet . Sent 1 packets. [*] Done!
ACK-GET TCP Handshake
Normally your system will send a GET request after the TCP handshake has been finished, which is shown in the schematic overview below.
However, the GET request can actually be send together with the ACK request, without completing the handshake first. Which means the network traffic will look like this:
Since the GET request will be send in the same packet as the ACK request, we will call this packet an ACK-GET packet (by lack of a better name).
After successfully sending the ACK-GET packet, the server will respond with the requested data as can be seen in the network traffic in Wireshark below.
And of course once again we can use scapy to create out own packets for the ACK-GET request. This is actually pretty simple by just placing a GET request after the normal ACK packet.
# scapy Welcome to Scapy (2.1.0) >>> get='GET / HTTP/1.0\n\n' >>> ip=IP(dst="www.google.com") >>> port=RandNum(1024,65535) >>> SYN=ip/TCP(sport=port, dport=80, flags="S", seq=42) >>> SYNACK=sr1(SYN) .Begin emission: .Finished to send 1 packets. * Received 3 packets, got 1 answers, remaining 0 packets >>> ACK=ip/TCP(sport=SYNACK.dport, dport=80, flags="A", seq=SYNACK.ack, ack=SYNACK.seq + 1) / get >>> reply,error=sr(ACK) Begin emission: Finished to send 1 packets. * Received 1 packets, got 1 answers, remaining 0 packets >>> print reply.show() 0000 IP / TCP 192.168.1.20:52240 > 74.125.77.147:www A / Raw ==> IP / TCP 74.125.77.147:www > 192.168.1.20:52240 A / Padding None
Since you are not completing the full TCP handshake your operating system might try to take control and can start sending RST (reset) packages, to avoid this we can use iptables:
iptables -A OUTPUT -p tcp --tcp-flags RST RST -s 192.168.1.20 -j DROP
The best way to see what is happening in the background is to have Wireshark running at the same time. You might want to set a filter in Wireshark to only show the packets involved in our request. Since my script uses the Google website I use the following filter:
ip.addr == 74.125.77.0/16
To make it a bit more easy to read and alter we can also build a Python script of the ACK-GET request.
TCP handshake with ACK-GET in a Python script making use of scapy:
#!/usr/bin/python # Change log level to suppress annoying IPv6 error import logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) # Import scapy from scapy.all import * # Print info header print "[*] ACK-GET example -- Thijs 'Thice' Bosschert, 06-06-2011" # Prepare GET statement get='GET / HTTP/1.0\n\n' # Set up target IP ip=IP(dst="www.google.com") # Generate random source port number port=RandNum(1024,65535) # Create SYN packet SYN=ip/TCP(sport=port, dport=80, flags="S", seq=42) # Send SYN and receive SYN,ACK print "\n[*] Sending SYN packet" SYNACK=sr1(SYN) # Create ACK with GET request ACK=ip/TCP(sport=SYNACK.dport, dport=80, flags="A", seq=SYNACK.ack, ack=SYNACK.seq + 1) / get # SEND our ACK-GET request print "\n[*] Sending ACK-GET packet" reply,error=sr(ACK) # print reply from server print "\n[*] Reply from server:" print reply.show() print '\n[*] Done!'
The Python script above gives the following output:
# python ACK-GET.py [*] ACK-GET example -- Thijs 'Thice' Bosschert, 06-06-2011 [*] Sending SYN packet Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets [*] Sending ACK-GET packet Begin emission: Finished to send 1 packets. * Received 1 packets, got 1 answers, remaining 0 packets [*] Reply from server: 0000 IP / TCP 192.168.1.20:12511 > 74.125.79.99:www A / Raw ==> IP / TCP 74.125.79.99:www > 192.168.1.20:12511 A / Padding None [*] Done!
This is all it takes to create a HTTP GET request inside a TCP Handshake with scapy. Of course this is just a small proof of concept and it needs some more tweaking to use it to communicate correctly with a HTTP server.
For another example of the usage of scapy see here my write up on one of the Swiss Cyber Storm challenges.
Leave a Reply