25.6.10

Fitting In



It's ironic that I'm writing about fitting in since I never have in my entire life. And while I've been a social misfit, I'm now talking about misfit crafted packets – specifically TCP. This pertains to packets that you craft and that you may observe entering a network near and dear to you. There are certain basic characteristics or behaviors that are expected of well-formed TCP packets. And, that is what this blog is about.

First things first – let's say you want to craft a well-formed SYN packet. The best model for emulation is the SYN of the operating system that you're attempting to emulate – the range of source ports it tends to use, the TCP window size, the IP TTL, IP identification number, IP flags, etc. as well as a randomized 32-bit sequence number.

Those fields and value are pretty obvious and well covered in literature so I won't bother to discuss them, but some crafters I've seen forget about the TCP options. Every packet with the SYN flag set – either a SYN or SYN/ACK packet – should have the Maximum Segment Size set. Sure, it's possible to craft a SYN or SYN/ACK without the MSS option and have the receiver accept it, but it stands out to the trained eye or astute filters looking at the session or traffic in aggregate. If you want to remain stealthy, you'll include the MSS option. There needs to be a value associated with it and that is typically 1460 for an Ethernet network.

If you ever see a packet flying by on your network that has a SYN flag set and does not have a TCP options Maximum Segment Size (MSS) – you can be fairly certain that it was crafted. And, if it was crafted – it probably is for some nefarious purposes. There are other TCP options too that can be included, but most current well-known TCP/IP stacks list the MSS as the first of the TCP options in the TCP header. However, Solaris stacks do not follow this convention and may present some false positives if you include this check.

How would you find SYN packets with no MSS flag? It's not that easy using some of the common tools. Snort cannot discover this with its rule language since it doesn't permit you to examine the TCP header options and the "offset" keyword pertains to the payload portion of the packet – not the TCP header. Tcpdump is not much help unless you assume that the MSS option must be the first. It has a value of 0x02 and it must be found in the 20th byte offset of the TCP header of any SYN segment. The tcpdump filter to discover such a segment would be:

tcp[13] & 2 != 0 and ((tcp[12]/16 == 5) or (tcp[12]/16 > 5 and tcp[20] != 02))

· 'tcp[13] & 2 !=0' looks for the SYN flag set

· 'tcp[12]/16 ==5' looks for a TCP header length of 5. This is fairly convoluted because the value for the TCP header length is found in the high-order nibble so we must first divide by 16 to make it more logical (at least for me). Now, a TCP header length of 5 (32-bit words) means a conventional 20-byte TCP header with no options.

· There can still be TCP options, yet no MSS. The rest of the "and" clause examines that by finding TCP options where the TCP header length is greater than 5, and assumes that the MSS value appears first in the 20th byte offset of the TCP header with a value of 02.

Wireshark is the best tool for finding such traffic using its display filter of:

!(tcp.options.mss) && (tcp.flags.syn == 1)

That finds any packet where the TCP options MSS flag is NOT set, but the segment has the SYN flag set. As you can see in the Wireshark capture that follows, the displayed segment was a SYN flag and TCP options of "SACK permitted", "Timestamps", and "Window scale", yet no MSS.

Once the MSS is set on the SYN or SYN/ACK, it should not appear in any other segment. This is true of two other TCP options – the window scale, and the Selective Acknowledgment acceptable. If any of these three options appears on a non-SYN segment, it is surely crafted. Again, Wireshark can expose these bogus packets using a display filter of:

tcp.flags.syn == 0 && (tcp.options.mss || tcp.options.wscale || tcp.options.sack)

The following Wireshark capture displays some of nmap OS fingerprinting packets that are exposed with the above display filter:

The specific packet that is displayed has the FIN, PUSH, and URG flags set along with the window scale, MSS, and timestamp set.

Another unusual condition that may be a reflection of a crafted packet or a poorly written TCP/IP stack is where a client SYN packet has a non-zero acknowledgement value. After all, the client has received nothing to acknowledge. The tcpdump filter to discover such packets is:

tcp[13] = 2 and tcp[8:4] > 0

The following Wireshark display filter exposes these packets.

tcp.flags.syn == 1 && tcp.flags.ack == 0 && tcp[8:4] > 0

The nmap OS fingerprinting scan creates some of segment with these characteristics as shown in the following Wireshark output:

When I've crafted packets, it is typically in a research environment and I'm not worried about the traffic being uncovered. As I've mentioned before, I use Scapy to craft my packets and the easiest way to close a session is to use a one packet reset. However, if I were trying to fit in, I'd go through the trouble of crafting the 4-way graceful FIN close so as not to attract notice.

That's about it for fitting in.


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!

14.6.10

IDS/IPS Evasion - Step 1. Awareness

Recently, I've been on a campaign to make people aware of TCP evasions of IDS/IPS solutions. Suppose that you could find a way to make an IDS/IPS believe that a TCP session has closed when it really hasn't. After you dupe the IDS/IPS into no longer tracking the session, you send your exploit and it goes undetected.

Sound difficult? Well, it really is not. There are a lot of nuances when dealing with a protocol as complex as TCP. And, many of these nuances are different interpretations made by different OS software. For instance, if you have wholly overlapping TCP seqments where both segments begin on the same sequence number and end on identical sequence numbers, but the payload is different which segment does the destination OS favor and just as important - does the IDS/IPS make the same choice?

I've got an interesting issue I found when doing research at Sourcefire years ago. This is a "nuance" that may not occur with all operating systems, but occurs when Linux runs on the destination host. And, it involves a RESET that appears to close the session, but really doesn't.

First let's take a look at some tcpdump output where I'm crafting the client side of the connection using a wonderful packet crafting tool called Scapy that uses the Python interpreter. The packets I crafted are in yellow. I'll cover how I crafted the session using Scapy in an upcoming blog. The three-way handshake starts on packet 1 where I craft the SYN. The server responds with the conventional SYN/ACK in packet 2. And, then in packet 3, I accidentally ACK the server with a value one more than the expected value. It isn't obvious, but the relative value
ack 2 in tcpdump parlance should have been ack 1 if I got it right.

Now, here's where the bizarre stuff happens. In packet 4, the server resets the connection because of the invalid ack number. Fair enough. But, in packet 5, I send 10 bytes of data "GET /EVIL" and I assign a correct acknowledgement number as tcpdump indicates with
ack 1. Now look at the server's response in packet 6. It acknowledges the 10 bytes of data! How is that possible - didn't it reset the connection? And, in packet 7, I send more data and the server acknowledges this in packet 8.

1. 10.1.3.7.8204 > 10.1.3.45.80: Flags [S], seq 10, win 8192
2. 10.1.3.45.80 > 10.1.3.7.8204: Flags [S.], seq 3506022755, ack 11, win 5840, options [mss 1460]
3. 10.1.3.7.8204 > 10.1.3.45.80: Flags [.], ack 2, win 8192
4. 10.1.3.45.80 > 10.1.3.7.8204: Flags [R], seq 3506022757, win 0
5. 10.1.3.7.8204 > 10.1.3.45.80: Flags [P.], seq 1:10, ack 1, win 8192
GET /EVIL
6. 10.1.3.45.80 > 10.1.3.7.8204: Flags [.], ack 10, win 5840
7. 10.1.3.7.8204 > 10.1.3.45.80: Flags [P.], seq 10:51, ack 1, win 8192
STUFF HTTP/1.1\r\nHost:www.whatever.com\r\n\r\n
8. 10.1.3.45.80 > 10.1.3.7.8204: Flags [.], ack 51, win 5840
9. 10.1.3.7.8204 > 10.1.3.45.80: Flags [R.], seq 51, ack 1, win 8192

You're probably thinking what's going on. Before I explain what I believe occurred, let's look at the implications of what just happened. You'd think that when an IDS/IPS saw the reset, it would stop tracking the session. And, let's say that the string "EVILSTUFF" represents, well - evil stuff. It's in two separate segments requiring reassembly so it's fairly safe to say most IDS/IPS solutions would miss this.

Okay, as for the explanation. My thought is that the server considered the third packet with the bad acknowledgement number a "foreign" or "rogue" segment. It did not consider it a valid segment of the forming session establishment. If that doesn't make sense, what happens if you try to send a PUSH packet with data to a session that hasn't been established? The server resets it. And, I believe that since this was not yet an established session, the server considered the segment with an invalid ACK number as foreign to any session it knew.

This highlights a couple of things. First, it's not hard to cause TCP evasions for an IDS/IPS. And second, there are issues that affect specific operating systems only. Servers on other operating systems than Linux that receive the bad client acknowledgement number may continue to send the SYN/ACK until a valid one arrives. It is extremely difficult for an IDS/IPS solution to have knowledge of all these OS-specific TCP issues and deal with them properly. And, that is why TCP evasions are possible.

12.6.10

Welcome to Packetstan

Ladies and Gentlemen, welcome to Packetstan! To begin, let me explain the origin of our sites name. A few years ago I was at a client trying to explain how an ISP in Pakistan took down most of YouTube. Every time I mentioned the country of Pakistan, I would mispronounce it as Packetstan. At the end of the meeting, I ran out and registered the domain.

The goals of this blog is to have a public outlet to publish research, reviews and analysis as they relate to packets. I have enlisted a few of my friends and Gurus to share their packet kung fu with the community.

This site is about fun, exploration, and learning. We will be publishing:
  • Packet captures
  • Packet analysis
  • Tools
  • Reviews of tools
  • Tutorials
  • Book & paper reviews
  • Rants :-)
On Monday, June 14, 2010, we will be releasing Packetrix, a Linux distro dedicated to packet exploration. We will of course be announcing it here, and on Twitter. If you have any ideas for content, please let us know. Thanks!