Detecting uptime from TCP timestamps

TCP timestamps are commonly used by modern operating systems, however many TCP timestamp implementations will indirectly provide information about a systems uptime. While generally not particularly useful, this information can be used to spot systems with high uptimes that might be missing important kernel updates.

TCP and RFC1323

The TCP protocol guarantees data will not be received out of order over a single connection. To achieve this a 32 bit sequence number is used in the TCP header so segments can be re-ordered if they arrive out of order. A single TCP segment is structured as follows:

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |          Source Port          |       Destination Port        |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                        Sequence Number                        |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                    Acknowledgment Number                      |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |  Data |           |U|A|P|R|S|F|                               |
 | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
 |       |           |G|K|H|T|N|N|                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |           Checksum            |         Urgent Pointer        |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                    Options                    |    Padding    |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                             data                              |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Back when the spec for TCP was originally published, exhausting all possible sequence numbers in a single TCP connection, and having to reuse previous sequence numbers was very uncommon. However over time as TCP became widely used, network bandwidth gradually increased. This is a problem because running TCP over a high throughput, high latency connection significantly increases the chance that a TCP segment with a reused sequence number might be received out of order.

To address this issue RFC 1323 proposed a number of different solutions, including adding an optional timestamp field to TCP segments. This proposal has been widely adopted, and can often be seen in TCP connections when using tools like tcpdump:

$ tcpdump -nn host 192.168.0.1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), capture size 262144 bytes
21:45:37.043329 IP 192.168.0.237.2753 > 192.168.0.1.22: Flags [S], seq 819717776, win 512, options [nop,nop,TS val 1295498450 ecr 0], length 0
21:45:37.044200 IP 192.168.0.1.22 > 192.168.0.237.2753: Flags [S.], seq 1256573498, ack 819717777, win 28960, options [mss 1460,nop,nop,TS val 1503452426 ecr 1295498450], length 0
21:45:37.044221 IP 192.168.0.237.2753 > 192.168.0.1.22: Flags [R], seq 819717777, win 0, length 0

RFC 1323 specifies timestamps must be monotonically increasing, and tick between 1 ms and 1 second. The starting value for the timestamp is not explicitly specified, however many network stack implementations use a systems uptime to calculate the timestamp.

Using hping3

TCP timestamps can easily be checked with hping3. On CentOS hping3 can be installed form EPEL:

yum install -y epel-release
yum install -y hping3

Once hping3 is installed you can extract timestamps by connecting to an open TCP port using the --tcp--timestamp option:

$ hping3 --count 2 --syn --destport 22 --tcp-timestamp 192.168.0.1
HPING 192.168.0.1 (enp0s3 192.168.0.1): S set, 40 headers + 0 data bytes
len=56 ip=192.168.0.1 ttl=64 DF id=0 sport=22 flags=SA seq=0 win=28960 rtt=1.1 ms
  TCP timestamp: tcpts=1503266566

len=56 ip=192.168.0.1 ttl=64 DF id=0 sport=22 flags=SA seq=1 win=28960 rtt=1.1 ms
  TCP timestamp: tcpts=1503266666
  HZ seems hz=100
  System uptime seems: 173 days, 23 hours, 44 minutes, 26 seconds


--- 192.168.0.1 hping statistic ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 1.1/1.1/1.1 ms

The example above suggests the uptime of 192.168.0.1 is roughly 173 days, running uptime on the remote system confirms this:

$ ssh 192.168.0.1 uptime
root@192.168.0.1's password:
 21:14:45 up 173 days, 23:49,  load average: 0.08, 0.03, 0.05

Hiding system uptime

On Linux the TCP timestamp feature can be controlled with the net.ipv4.tcp_timestamp kernel parameter. Normally the option can either be enabled (1) or disabled (0), however more recent kernels also have an option to add a random offset which will effectively hide the systems uptime:

tcp_timestamps - INTEGER
Enable timestamps as defined in RFC1323.
        0: Disabled.
        1: Enable timestamps as defined in RFC1323 and use random offset for
        each connection rather than only using the current time.
        2: Like 1, but without random offsets.
        Default: 1

You can check which mode is being used by reading /proc/sys/net/ipv4/tcp_timestamps:

$ cat /proc/sys/net/ipv4/tcp_timestamps
1

The value can also be dynamically updated:

$ echo 2 > /proc/sys/net/ipv4/tcp_timestamps

Note: you can also use sysctl to read or update kernel parameters at runtime:

$ sysctl net.ipv4.tcp_timestamps
net.ipv4.tcp_timestamps = 1
$ sysctl net.ipv4.tcp_timestamps=2
net.ipv4.tcp_timestamps = 2

Changes can be made persistent by adding a new file under /etc/sysctl.d/:

cat > /etc/sysctl.d/tcp_timestamps.conf << EOF
net.ipv4.tcp_timestamps = 2
EOF