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
[email protected]'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