Passive OS Fingerprinting with p0f

Given psad's propensity for passive detection versus actively generating network traffic, active OS fingerprinting is not used. We will continue the discussion from the perspective of what is possible with strictly passive means.

One of the most well-known and successful passive operating system fingerprinting implementations is p0f, developed by Michal Zalewski (http:// As it turns out, if you can passively intercept raw TCP packet data, either because you have access to a network segment over which packets are flowing or because packets are directed at or originate from a system that you control, you can glean a lot of interesting information that is useful for OS fingerprinting. TCP SYN and SYN/ACK packets contribute the most information, because they define the parameters under which TCP connections are supposed to behave and because different TCP stacks negotiate these parameters with some distinction.

In the p0f incarnation of OS fingerprinting, a remote operating system is identified by examining several fields within the IP and TCP headers of TCP SYN or SYN/ACK packets that originate from the system. These fields include the following:

Fragmentation bit Initial TTL value Maximum Segment Size (MSS) Overall SYN packet size TCP option values and order TCP window size p0f uses a custom signature format to store the specific parameters mentioned above for each OS. For example, here's a fingerprint for a Linux system running the 2.5 kernel (the signature needs to be updated because it really refers to the stable 2.6 kernel instead of the 2.5 development kernel, and an allowance is made within the fingerprint for the 2.4 kernel as well):

S3:64:1:60:M*,S,T,N,W1:.:Linux:2.5 (sometimes 2.4) (1)

The p0f signature format has several fields separated by colon (:) characters:

• Reading from left to right, the first field, S3, refers to the TCP window size. This field instructs p0f to look for TCP SYN packets with a window size that is a multiple of three times the value of the Maximum Segment Size (MSS).

• The second field, 64, refers to the TTL value in the IP header; in this case a TTL of 64. Because TTL values are decremented as packets traverse the Internet, this field refers to the initial TTL value, and p0f allows the actual TTL value in the packet to be significantly less.

• The third field, 1, refers to the Don't Fragment (DF) bit in the IP header. Because the signature has the value 1 in this field, it is looking for the DF bit to be set.

• The fourth field, 60, is the overall packet size. In this example, the signature requires the size to be 60 bytes.

• The fifth field, S,T,N,Wi, describes the options portion of the TCP header. In this example, the signature is looking for any MSS, followed by the Selective Acknowledgment (S), Timestamp (T), NOP (N), and Window Scaling Factor (Wi) options.

NOTE A comprehensive treatment of passive OS fingerprinting (and other passively collected information) can be found in Michal Zalewski's Silence on the Wire (No Starch Press, 2005).

Emulating p0f with psad

In order to run its fingerprinting algorithm over packet headers, p0f uses libpcap to sniff packets directly off the wire. By contrast, psad contains code that implements OS fingerprinting based around p0f signatures but only requires iptables log messages as the data input. This is possible because every header value examined by p0f (TCP window size, TTL value, TCP options, and so on) is also available in iptables log messages as long as the --log-tcp-options argument is used to build the LOG rule. Here's an example LOG message in which the options portion of the TCP header is shown in bold:

Jul 14 22:03:42 iptablesfw kernel: DROP IN=ethi OUT= MAC=00:i3:46:3a:4i:4b: 00:a0:cc:28:42:5a:08:00 SRC= DST= LEN=60 TOS=0xi0 PREC=0x00 TTL=64 ID=37356 DF PROTO=TCP SPT=54423 DPT=23 WINDOW=5840 RES=0x00 SYN URGP=0 OPT (020405B40402080A0B00CE790000000001030302)

Decoding TCP Options from iptables Logs

The only tricky part to implementing p0f OS fingerprinting with log messages like the one shown above is that the long OPT hex dump has to be decoded in order to match up against a p0f signature. The OPT string represents a hex dump of the TCP options portion of the TCP header, and by examining this string one byte at a time and matching it against the set of possible options values in the TCP header (, the options used in a SYN packet become clear. Except for the End of Option List and No Operation (NOP) options which are each only one byte wide, every option is designated by a type, is followed by the length, and ends with the value. This is called Type-Length-Value (TLV) encoding.

For example, the beginning of the hex string above, 020405B4, decodes as 02 = Maximum Segment Size, 04 = Length (including the type byte), 05B4 = 1460 (decimal value). Continuing this analysis similarly for the entire hex dump yields the following:

• Maximum Segment Size is 1460 NOP

• Selective Acknowledgment is OK

• Timestamp is 188338970 Window Scaling Factor is 2

This set of options matches the p0f fingerprint S4:64:1:60:M*,S,T,N,W2: Linux:2.5::Linux 2.5 (sometimes 2.4), which is indeed correct, because I generated the connection attempt to TCP port 23 from a machine running the 2.6.11 kernel, and the 2.5 series was the development series for the 2.6 kernel.

By matching the TCP options in SYN packets against p0f signatures, psad can often identify the specific remote operating system that is poking at your iptables firewall. This functionality is only made possible, however, through the use of the --log-tcp-options argument, so I highly recommend that you use this option when adding your default LOG rule to your iptables policy.

Was this article helpful?

0 0

Post a comment