TCP hype to rest: the real story (posted 2004-04-24)
I hate to admit it, but I got infected by the hype surrounding the TCP "vulnerability". As it turns out, all of this was pretty much yesterday's news from the start. In a news.com article we can read Paul Watson complain that "it's crazy". No argument there. So here is the real story.
RFC 793 (TCP) clearly indicates that established TCP sessions must be torn down when an RST packet is received with a sequence number that falls within the current window.
Quick detour: TCP is responsible for making sure that all data from the sending application is received once, only once and in the correct order by the receiving application. In order to do this, it numbers every byte of data using a sequence number. At any time, TCP has a specific "window" in the sequence number space for bytes that it is prepared to receive. Bytes that fall before the window have already been received, so if those come in again they are ignored. Bytes that fall beyond the window are too far in the future and are also ignored. A packet or segment of data that starts with the first byte of the current window is processed immediately, and any data that falls further within the window is buffered and will be processed later as this data is received out of order.
RST packets are supposed to be generated in order to reset stale sessions that can for instance occur after one side reboots. So when the other side sends a packet, the system that just booted doesn't know this session and sends back an RST packet in reply to the data packet, copying the sequence number in the process. The sender of the original packet now receives the RST with a sequence number that obviously falls within the window, so the session is torn down.
The important part here is that according to RFC 793 the sequence number doesn't have to be an exact match: as long as it's within the window, it'll be accepted. For some reason, many people assumed there would have to be an exact match. Since the sequence number is 32 bits in size, this means 4.3 billion possibilities. So if an attacker wants to reset a TCP session, assuming he already knows the correct IP addresses and port numbers, he would have to send up to 4.3 billion packets. But in reality the number of packets necessary to reset a session must be divided by the window size.
So what would be the window size for a typical TCP session, or, more importantly, a TCP session used for BGP? Well, if a system implements the RFC 1323 TCP high performance extensions, the window size can be almost a gigabyte. This is where Paul Watson's claim that resetting a TCP session can be done with "as few as four packets" comes in. However, this is pure nonsense as such huge windows are never necessary. Even when moving data across the globe at 10 Gbps a window less than half that size is more than sufficient. BGP isn't exactly in the business of moving data across the globe at high speed over TCP, so it only requires a very modest window size. These are some of the initial packets for two BGP sessions between a Cisco router and a FreeBSD box running Zebra:
09:42:06.537772 IP 184.108.40.206.11164 > 220.127.116.11.179: S 3863598700:3863598
700(0) win 16384 <mss 1460>
The packet from the Cisco router doesn't even have the window scale option. The FreeBSD machine has the option but doesn't bother to actually use it. This means that the maximum window for the subsequent session is limited to 65535 bytes. However, routers almost universally use around 16000 bytes. Bottom line: in order to reset a BGP session using a TCP RST, it is necessary to send between 65 and 268 thousand packets. This takes several minutes at DSL speeds. So if IP addresses and port numbers are known, resetting a BGP session isn't too hard for an attacker. However, if the attacker must also guess the ephemeral port, then the time it takes to reset a session becomes too long to make this an interesting attack vector.
In theory it's even better than this, at least on Cisco routers, because those rate limit the handling of RST packets. Unfortunately, RFC 793 also suggests that TCP sessions should be terminated if an in-window SYN rather than a RST packet is received. SYN packets are normally used to open sessions, so the rationale for terminating sessions when unexpected SYNs come along is murky at best.
Last but not least, there was a bug in several TCP implementations that allows any "left of window" RSTs to be acted upon
without further checks. This bug was fixed in 1998 in FreeBSD. From the FreeBSD 4.9 /usr/src/sys/netinet/tcp_input.c file:
This bug was still present in NetBSD until a few days ago. This led me to believe that the real issue here was that this same bug was also present in the TCP code from one or more major router vendors, and that this was what Paul Watson was talking about. But that didn't turn out to be the case, so essentially the whole story boils down to "router vendors implement TCP according to RFC 793". Big news indeed.