Ring out the old, RING in the New:
Plan B SecurityTod Beardsley, May 8, 2002
By measuring the behavior of various operating systems' TCP retransmission timeout lengths (or RTOs), it is possible to distinguish between OSes on a network. Franck Veysset, Olivier Courtay, and Olivier Heen of the Intranode Research Team first published this concept in April, 2002, and their paper goes into appreciable detail in its discussion of this technique, the mechanisms by which TCP retransmission timers are computed, and OS fingerprinting in general.
To demonstrate this concept, the researchers simultaneously released a proof-of-concept tool which leverages this specific exposure: Remote Identification, Next Generation, or RING.
The goal of my work here is to explore RING's effectiveness as stand-alone OS fingerprinting tool, and offer suggestions of how an organization can protect themselves against RING specifically as well as future implementations of this concept.
The Intranode paper documents the usefulness for security researchers and engineers to be able to remotely detect particular operating systems, apparently without any sort of authentication or provable authorization. The paper, while otherwise thorough, neglects to mention the obvious value these methods also have for unauthorized attackers in assessing potential targets for compromise. Perhaps this counterpoint was left out in hopes of avoiding legal or ethical responsibility for the misuse of their research -- I'm not sure what the legal climate in France is like these days (come to think of it, I'm not sure what it's like in the USA, either.)
For the purposes of this paper, I will be treating RING as purely an attacker's tool. While I agree that remote OS fingerprinting is certainly part of legitimate "white hat" security work, I believe RING and future derivatives will be used at least as often by the unscrupulous and the criminal.
RING is only semi-standalone, since it also requires libnet (by Mike D. Schiffman) and libdnet (by Dug Song) for packet construction and filtering capabilities. And like most other useful Ethernet packages, it requires libpcap for packet capturing. The INSTALL instructions mention libdnet 1.2 specifically, but 1.4 seems to work fine.
RING is comes in two flavors: source for Linux 2.4, and source for everyone else. I fall into the "everyone else" category, since I'm on the Linux 2.2.17 kernel, running the Debian Potato (testing) Linux distribution.
RING is also available as an patch for nmap, and installation instructions are provided in a later nmap-hackers post. I haven't experimented with either of these versions; according to the authors, they are all functionally identical.
RING relies on intentionally creating a half-open connections with a target. This concept is not new in and of itself; it is the basis of all "SYN Flooding" denial-of-service attacks. In a September, 1996 advisory, CERT/CC illustrated a normal TCP three-way handshake:
Client and server can now
send service-specific data
By silently dropping the target's SYN-ACK, RING never completes the handshake, and never sends the final ACK. While the goal of SYN flooding is to exhaust the target's network resources by sending many SYNs and never ACKing them, RING's goal is to measure the time between each successive SYN-ACK retransmission.
None of the RFCs regarding TCP's retransmission timeout explicitly state what it must be set at and how it increases over successive attempts**. So, each TCP stack has its own implementation. By building a fingerprint list of known timeouts associated with particular operating systems, then, it gets very easy to start guessing operating systems.
It's important to stress that this information is gathered by sending only a single, normal SYN packet. This is what makes RTO measuring stand out from other means of guessing OSes, since it is both efficient and difficult to detect. In his seminal 1999 paper, Fyodor discusses many methodologies for OS fingerprinting, but all rely on either a sequence of packets (as in TCP Initial Sequence Number sampling), or abnormally formed packets (such as the various scans involving TCP flags and options).
I have selected a variety of targets to represent a rich (if Windows-heavy) environment, and I plan to run RING against these devices, three times each, using an empty fingerprint list. After completing this first pass, I will average and smooth out the measured RTOs, and use those values to build a new fingerprint list. Finally, I will run RING against a second, identical set of devices.
The expectation is RING will correctly identify the devices on the second pass by using the results of the first. To monitor the test and measure RING's stealth, I will be logging with the ever popular packet analyzer, tcpdump.
As a control, I will be using netcat (the "TCP/IP Swiss army knife" by Hobbit) to establish connections to the same devices on the same ports, and logging these sessions as well. The goal here is to compare normal SYN packets with RING's, and detect any indicators which could be used as an IDS signature.
IP Address Port OS / Device
10.1.220.31 23 SunOS 5.6
10.1.251.12 23 Red Hat 7.0 (Linux 2.2.16-22enterprise)
10.2.27.12 23 Cisco Catalyst 2948G Router
10.2.27.14 23 Extreme Networks Summit 48 Router
10.2.108.16 23 Dell TrueMobile Wireless Access Point
10.2.11.203 80 LexMark Optra S2450 Printer
10.1.21.167 139 Windows NT 4.0 Server SP5
10.1.68.160 139 Windows NT 4.0 Server SP6
10.3.9.248 139 Windows 2000 Advanced Server (SP1)
10.1.54.211 139 Windows 2000 Advanced Server (SP2)
10.1.54.199 139 Windows XP Professional
IP Address Port OS / Device
10.1.220.30 23 SunOS 5.6
10.1.251.25 23 Red Hat 7.0 (Linux 2.2.16-22enterprise)
10.2.52.11 23 Cisco Catalyst 2948G Router
10.2.27.15 23 Extreme Networks Summit 48 Router
10.2.108.15 23 Dell TrueMobile Wireless Access Point
10.2.11.204 80 LexMark Optra S2450 Printer
10.1.216.126 139 Windows NT 4.0 Server SP5
10.1.158.67 139 Windows NT 4.0 Server SP6
10.1.183.49 139 Windows 2000 Advanced Server (SP1)
10.1.80.172 139 Windows 2000 Advanced Server (SP2)
10.1.54.131 139 Windows XP Professional
I want to capture all TCP traffic to or from my laptop (IP address 10.1.54.38), and write it to tcpdump-ring.log in binary format:
$ tcpdump -i eth0 -w tcpdump-ring.log 'tcp and host 10.1.54.38'
In another command shell, I'll write the OS name to firstpass-results.log, and run RING against the target IP, 10.1.54.28, writing the results also to firstpass-results.log. I'll do this three times for each device on the list.
$ echo 'TEST SunOS 5.6' >> 1pass.log ; ./ring -d 10.1.220.31 -s 10.1.54.38 -p 23 -f /dev/null -i eth0 >> 1pass.log
I'll repeat this for each target. Note that RING requires a fingerprint file, but since I don't care about matching at this point, I gave it /dev/null.
At the end of the first pass, I have a results log that looks like this:
TEST SunOS 5.6
3494016 6399768 12799690 25599038
OS: Nothing Match
3495820 6399831 12799648 25599036
OS: Nothing Match
3497260 6399804 12799659 25599107
OS: Nothing Match
TEST Red Hat 7.0
3148430 6499923 12499495 24499283
OS: Nothing Match
3497575 6499676 12499318 24499545
OS: Nothing Match
3085932 6499852 12499549 24499319
TEST Microsoft Windows XP Professional
OS: Nothing Match
OS: Nothing Match
OS: Nothing Match
Each set of numbers in the above is the time, in milliseconds, each subsequent SYN-ACK is received from the target. For example, my first test was against a SunOS 5.6 server, and it resent its SYN-ACK 3.49~ seconds after the first SYN-ACK, then 6.39~, 12.79~, and finally 25.59~ seconds after that. I can compare this to my tcpdump log to verify RING's times:
12:20:54.736268 10.1.54.38.1451 > 10.1.220.31.23: S 221002:221002(0) win 1024...
First SYN-ACK response:
12:20:54.736749 10.1.220.31.23 > 10.1.54.38.1451: S 1812524980:1812524980(0) ack 221003 win 10136...
Subsequent SYN-ACK responses:
12:20:58.230776 10.1.220.31.23 > 10.1.54.38.1451: S 1812524980:1812524980(0) ack 221003 win 10136...
12:21:04.630542 10.1.220.31.23 > 10.1.54.38.1451: S 1812524980:1812524980(0) ack 221003 win 10136...
12:21:17.430234 10.1.220.31.23 > 10.1.54.38.1451: S 1812524980:1812524980(0) ack 221003 win 10136...
12:21:43.029274 10.1.220.31.23 > 10.1.54.38.1451: S 1812524980:1812524980(0) ack 221003 win 10136...
So, after averaging and smoothing the RTO's reported by RING, I now have a working fingerprint list for a variety of devices:
SunOS_5.6_Server 3490000 6390000 12790000 25590000
Red_Hat_7.0_Server 3000000 6490000 12490000 24490000
Cisco_Catalyst_2948G_Router 5700000 12150000 24320000
Extreme_Networks_Summit48_Router 5400000 11990000 23990000
Dell_TrueMobile_Wireless_Access_Point 5600000 23990000
Lexmark_Optra_S2450_Printer 480000 990000 1990000 3990000 7990000 15980000 19950000
Windows_NT_4.0_Server 3100000 6560000 13120000
Windows_2000_Advanced_Server 3170000 6560000
Windows_XP_Professional 2800000 6010000
I noticed different service pack versions of the same Windows platform did not seem to affect RTO times. This discovery was quite a relief, since patch level information is invaluable to a directed attacker. On the other hand, different Windows platforms do return different RTOs. Compare this with nmap's OS fingerprinting scan results against my first Windows NT 4.0 server on port 139:
$ nmap -O -n 10.1.21.167 -p 139
Starting nmap V. 2.54BETA30 ( www.insecure.org/nmap/ )
Warning: OS detection will be MUCH less reliable because we did not find at
least 1 open and 1 closed TCP port
Interesting ports on (10.1.21.167):
Port State Service
139/tcp open netbios-ssn
Remote operating system guess: Windows NT4 / Win95 / Win98
Nmap run completed -- 1 IP address (1 host up) scanned in 1 second
Nmap lumps together three different Microsoft operating systems in one guess, while RING aims to be much more discriminate. Nmap also complains about having just one open source port to work with, while RING is designed to query just one port. (This is important if the user wanted to fingerprint, say, a secured webserver across the Internet, and all of its ports but 443 (https) were closed).
Using my new fingerprint file, I ran RING against my second, identical set of target devices. Some are on the same subnets as the first set, others are not. Since RING takes care of network variance by first calculating normal round-trip times, their locations shouldn't matter.
$ echo 'RINGing 10.1.220.30 on port 23...' >> 2pass.log; ./ring -d 184.108.40.206 -s 10.1.54.38 -p 23 -f ./tod.fingerprints -i eth0 >> 2pass.log ; tail -n 4 secondpass-results.log
This was repeated for each target device, and the results are below.
3498885 6402668 12796759 25599115
3053171 6498470 12497390 24494537
5739047 12163749 24315294
6175469 11998882 23999106
RINGing 10.2.108.15 on port 23...
RINGing 10.2.11.204 on port 80...
496166 998933 1998414 3996819 7993630 15967284 19984153
RINGing 10.1.216.126 on port 139...
3223184 6562341 13124753
RINGing 10.1.158.67 on port 139...
2950250 6006357 12017156
RINGing 10.1.183.49 on port 139...
RINGing 10.1.80.172 on port 139...
RINGing 10.1.54.131 on port 139...
RING successfully identified the device in ten of the eleven cases. The only miss was 10.1.183.49, and even then, RING was close -- it's really Windows 2000 Advanced Server (SP1), but RING reported it as a Windows XP Professional workstation.
Looking at these results, RING has proved to be very accurate for at least these sample targets, and I have no doubt that, given enough fingerprint samples, RING can be an effective tool for remotely fingerprinting virtually any TCP device with at least one open port.
True, RING does send only a single SYN packet, and is necessarily slow enough to evade standard portscan-detecting thresholds, there is some good news for network defenders. RING has a flaw in its implementation which gives away its presence. Below is the tcpdump log of a number of RING's initial SYNs:
$ tcpdump -n -r tcpdump-ring.log 'src host 10.1.54.38 and tcp & 2 != 0"
12:20:54.736268 10.1.54.38.1451 > 10.1.220.31.23: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
12:22:03.222172 10.1.54.38.1452 > 10.1.220.31.23: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
12:23:17.248246 10.1.54.38.1457 > 10.1.220.31.23: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
12:25:28.561043 10.1.54.38.1459 > 10.1.251.12.23: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
12:27:45.208158 10.1.54.38.1469 > 10.1.251.12.23: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
12:28:52.117392 10.1.54.38.1470 > 10.1.251.12.23: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
13:02:00.928313 10.1.54.38.1712 > 10.1.54.199.139: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
13:03:05.803371 10.1.54.38.1716 > 10.1.54.199.139: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
13:04:11.994878 10.1.54.38.1731 > 10.1.54.199.139: S 221002:221002(0) win 1024 <mss 1460,sackOK,timestamp 79877292 0,nop,wscale 0>
I noticed immediately that the initial sequence number and the timestamp are identical for every scan. Exploiting this, I can write a pair of Snort rules to detect RING activity, like so:
alert tcp $EXTERNAL_NET any -> $HOME_NET any (flags: S; seq: 221002;
msg:"SCAN RING v.0.0.1 scan"; classtype: attempted-recon;)
alert tcp $HOME_NET any -> $EXTERNAL_NET any (flags: SA; ack:221003;
msg:"SCAN RING v.0.0.1 scan response."; classtype: successful-recon-limited;)
Big caveats: These rules are strictly tentative. Since I haven't compiled RING anywhere else, I can't verify that the initial sequence numbers or timestamp values don't vary from platform to platform, or even machine to machine. To be fair, the authors released RING as version 0.0.1, and merely as a proof-of-concept tool. Though I'm certainly no C debugger, I suspect that adding some randomness to fix this predictable behavior would require only trivial effort.
The one type of service which is most impacted by RING and the concepts behind it is the virtual honeypot/honeynet. These systems rely on misleading attackers into believing they are attacking, say, a Windows server, when the real machine is in fact a Linux box. Reliably impersonating other OSes, in light of this exposure, will be much more difficult to pull off. Fortunately, it appears that some of the concepts presented by Rob Beck may help - specifically, using a user-space packet mangler like Netfilter/IPTables to enforce a misleading RTO on all outgoing packets.
As for everyone else, the most direct way to defend against this exposure would be for vendors to reimplement their TCP stacks and force the RTO to conform to some uniform standard. In the meantime, engineers can rearchitect their networks to place important, hardened servers behind intermediate devices -- this solution already foils Netcraft's proprietary OS fingerprinting methods, since the proxy, not the "real" server, does all the talking to the outside world.
But, these are fairly extreme and expensive countermeasures in the face of this information leakage. Obfuscating this information only fools attackers who actually care what OS is running. Most automated attacks don't look before they leap -- Nimda, for example, is perfectly happy trying IIS vulnerabilities against Apache servers. To quote Elizabeth Zwicky, "you get rid of attackers by getting rid of vulnerabilities,"* not by hiding them.