OpenBSD Timestamps

It’s not breaking news that OpenBSD strives to be at the forefront of security. It appears that their TCP timestamp option behavior reflects this as well. The purpose of the TCP timestamp is to measure roundtrip times as well as to act as an extension to the TCP sequence numbers by disambiguating two segments that arrive simultaneously, have identical TCP sequence numbers, but contain different TCP timestamps such as in the case of wrapping TCP sequence numbers. The likelihood of this happening is slim to none, however, if it does, the segment with the later timestamp is favored while the one with the older timestamp is dropped. I’ve found a variety of different OS-specific behaviors regarding old TCP timestamps that offer a wealth of opportunities for IDS/IPS evasion.

Most operating systems employ a TCP timestamp behavior where the sender’s value reflects the number of clock ticks since the last reboot. A clock tick is a vague concept described in RFC 1323 as “The timestamp value to be sent is to be obtained from a (virtual) clock that we call the "timestamp clock". Its values must be at least approximately proportional to real time…”. Now, think about this in terms of security. A high TCP timestamp divulges that the host has not been rebooted in a while. Now, let’s say there is a patch released on MS Tuesday for a new Windows remote exploit. Further, installation of this patch requires a reboot. An attacker searching for victim hosts could gain some valuable reconnaissance just from a TCP timestamp if he/she could communicate with a potential target host via TCP.

OpenBSD must have seen the possible leakage of information from the TCP timestamp so they now randomize the first timestamp on every new TCP/IP session. They increment the timestamp in subsequent segments in the session to meet the standard of approximating the passage of time for accurate round trip time measurement (RTTM). But, they eliminate the possibility for the TCP timestamp to be used to reflect the uptime since last reboot.

Let’s take a look at the difference between the more conventional incremental Windows TCP timestamp and the randomized OpenBSD timestamp. I sent five SYNs in rapid succession to a Windows host first and then to an OpenBSD host to examine the TCP timestamp values in the returned SYN/ACK. I’ve edited the tcpdump output to contain only the pertinent information to save space. As you can see the Windows host generates incremental TCP timestamp values.

Now, look at the OpenBSD random timestamp values from host

While this is impressive in itself, I believe OpenBSD has relegated the use of TCP timestamps exclusively for RTTM and not for the almost useless Protection of Wrapped Sequence Numbers (PAWS) that distinguishes between old and new timestamps. As far as I’m concerned the RTTM is the only reason to support TCP timestamps so I’m pleased if this is the case. I say “if this is the case” since I don’t have an accessible OpenBSD to send my Scapy generated traffic so I have to believe that the Netcraft association of www. openbsd.org and www.bsd.org with OpenBSD/NetBSD OS is correct. My only hesitation is that there may be some kind of device that exists between the sending and receiving boxes that acts as a proxy or somehow permutes the TCP timestamp from the actual host. But, I tried two different hosts that reside on different networks to help bolster my confidence that I’m communicating with actual OpenBSD hosts.

My experiment is to try to get the receiving host to drop all segments after the initial SYN by assigning them a lower timestamp. Using Scapy, I place a timestamp value of 100 on the SYN and 0 on all subsequent ones. First, let’s look at how Windows handles this. It never acknowledges any data I send to it, echoes the expecected timestamp of "ecr 100", and even attempts to resend the SYN/ACK in the final segment to give me another chance to send an appropriate higher timestamp in my initial ACK. This is the expected behavior and conforms to RFC 1323 specification.

Conversely, take a look at the same traffic sent to an OpenBSD host It happily accepts and echoes the old timestamp value of 0 and returns the requested web page, albeit in reverse order, meaning it accepted the segment with the old timestamp carrying the web request.

In an effort to corroborate the behavior above, I wanted to test another OpenBSD host – www.bsd.org - that belongs to a totally different network address block with different devices standing between my network and the web server. As you can see host parrots this same behavior of ignoring the old timestamp.

Initially, I thought there might be something wrong with the traffic I was sending. I know some operating systems require you to echo the timestamp they issue in order to receive subsequent segments. I typically don’t do this because it’s a few more lines of code, but I added the code to echo the server’s timestamps and I still witnessed the behavior of OpenBSD ignoring old timestamps. If this is indeed the case, I think OpenBSD has made a good, and as usual, innovative decision to support only the useful features of the TCP timestamp.


My Mistake - Suricata Reassembles on All TCP Ports

Yesterday I erroneously blogged that Suricata does not perform TCP stream reassembly on non-standard ports. The script I used to test this in live mode had an incorrect TCP reset sequence number on it and an alert was not generated. When I corrected this, I did indeed see an alert.

My apologies for reporting this and my apologies to the Suricata team. To be clear - Suricata performs TCP stream reassembly on all TCP ports. I am about to correct yesterday's blog by deleting the misinformation.


Suricata TCP Evasions

If you’re currently using the Open Source intrusion detection/prevention system Suricata, I urge you to upgrade to the recently released version 1.0.2 as soon as possible. In my recent research, I've found more than half a dozen TCP evasions in code from prior versions.

In my experience with Open Source intrusion detection systems like Snort, accurate and thorough TCP reassembly is extremely difficult. Snort's TCP stream reassembly code has transitioned from being pretty lame and rudimentary (see Stick and Snot attacks) into to a flawed stream4 implementation, finally evolving into a solid and comprehensive stream5 module--over a decade later.

I've been interested in Suricata's stream reassembly code since they offered their first beta release over a year ago. When they released their first non-beta stable 1.0 release this past July, I felt it was finally fair game to try some evasions. Unfortunately, it was fairly easy to find them. Some, like the failure to properly perform TCP checksum validation, were trivial; others like the one discussed below took more poking.

My intent was to find evasions that were more or less universal – not affiliated with a particular operating system (AKA target-based). I crafted the evasion techniques with Scapy and sent them to a destination operating system running Linux first and later Windows Vista. Obviously, TCP evasions are most dangerous when Windows is the destination host since Windows is still the most prevalent OS. In all fairness, I ran any TCP evasion that I discovered against Suricata later against a current version of Snort – Snort alerted on all of the techniques that successfully evaded Suricata.

Let’s examine one of the several Suricata TCP evasions I was able to uncover in versions prior to 1.0.2. Suricata uses Snort rules. Assume that the following Snort rule exists and is configured for use:

alert tcp any any -> any 80 (msg:"EVILSTUFF alert"; flow:to_server, established; content: "EVILSTUFF"; http_uri; sid:1234; )

This rule looks for the content of "EVILSTUFF" in an HTTP URL.

First, here is the Scapy code that I used. I'm not going to go over the specifics of the code since I covered how to create the client side of the code in the blog post on an unusual response from a bad acknowledgement on the last segment of the three-way handshake. The code for this evasion shares many of the concepts and some of the code.

Now, let's take a look at the client side traffic that the the Scapy code generated.

Segment 1 represents a SYN with a crafted Initial Sequence Number (ISN) of 10. The next crafted segment 2 shows the acknowledgement of the server's SYN/ACK. Next, I reset the connection in segment 3. This should be the end of tracking this session.

Segment 4 is a SYN sent about 5 seconds after the reset and is identical to the first SYN, except it uses an ISN of 11. Segment 5 completes the acknowledgement of the three-way handshake from the new session and segments 6 and 7 contain the malicious split content of "EVILSTUFF". Suricata fails to alert here; we assume this is because it gets confused after improperly closing/cleaning up after the first session and mistakenly considers the new session as a continuation of old one. It doesn't track the new session SYN with the ISN of 11 and when the malicious content is sent later—with a sequence number one more than Suricata expects—it creates a gap in sequence numbers for Suricata. Suricata cannot properly reassemble the malicious content since there is a missing sequence number before it, causing the evasion.

The failure to properly close one session and distinguish it from a new one is neither a trivial nor a target-based issue. So please make sure that you upgrade right away if you are currently using Suricata.

Additionally, if you have or are considering dumping Snort in favor of Suricata, my advice is: Don't do it yet! Suricata is a work in progress. I don't mean to diminish the hard work of some very talented people, but the product needs to mature before I would consider using it as my exclusive means of IDS/IPS. And please note that I've scrutinized only a specific aspect of the product and have found what I consider to be substantial issues. The Suricata team has fixed the issues I reported, so the stream reassembly has improved.

In the interest of full disclosure, I worked at Sourcefire on the Vulnerability Research Team for about 5 years. Specifically, I did research on Snort's current stream reassembly preprocessor known as stream5. Steve Sturges translated the research and improved stream4 to produce stream5. It was at Sourcefire that I learned Scapy and began to use it to try to evade other companies' TCP stream reassembly as well as help build a regression suite to ensure that code changes to stream5 or other modules didn't accidentally introduce unintended consequences. So, yes – I am somewhat biased in my preferences for Snort; I very much appreciate the opportunities presented to me at Sourcefire and I have an abundance of loyalty and allegiance to this day. But, I've got no current financial or other investment in Sourcefire, so I've nothing to gain monetarily from exposing Suricata's TCP reassembly issues.

I'd like to thank Todd Wease, who used to work for Sourcefire on the Snort team, for validating the evasions I found and for examining the code to help me understand the logic issues. And, I'd like to thank Jen Harvey, Co-Founder at Voxilate, for reviewing this post and cleaning it up so it is coherent.

As a final note: You, too, can attempt to find evasions such as these in your IDS/IPSes packet-crafting with Scapy. There’s still time and room to sign up for my Power Packet Crafting Using Scapy course in Las Vegas at the end of September. Please come join me in Las Vegas in September!