The IP identification (ID)
field is a two-byte field contained within the packet. Its sole purpose in life is allow
IP packets to be fragmented: all fragments should contain the same ID as the original
packet so that they can be pasted back together again. Most systems use the concept of a monotonically
increasing ID: for each packet sent, the field is increased by one. There is a
little twist to this scenario. A little-endian machine (like Intel processors) stores
numbers in reverse byte-order than how numbers are represented on the wire. This means
that a monotonically increasing integer from a Wintel box will increment the high-order
byte first, whereas a Sun SPARC box will increment the low-order byte first. Therefore,
lets say that you are being pinged steadily from both a Sun SPARC and a Wintel, you will
see the following sort of progression in the IP ID field:
| SPARC |
Wintel |
| 0x01FD |
0xFD01 |
| 0x01FE |
0xFE01 |
| 0x01FF |
0xFF01 |
| 0x0200 |
0x0002 |
| 0x0201 |
0x0102 |
The above numbers are shown in hexadecimal, which
shows the byte-order problem. However, many firewall logs (stupidly) show these numbers in
decimal. If a firewall system assumes the number is big-endian but the incoming packets
are little endian, then the progression of the numbers is hidden. For example:
| IP ID |
Big-endian |
Little-endian |
| 01 FD |
509 |
64769 |
| 01 FE |
510 |
65025 |
| 01 FF |
511 |
65281 |
| 02 00 |
512 |
2 |
| 02 01 |
513 |
258 |
This entire issue is complicated by the fact that a firewall running on a platform
doesn't have to base its decimal calculation of the IP ID field on the underlying CPU.
What I mean by this is that the C code that interprets the IP ID could be written in two
ways;
/* ID field is a 2-byte number at offset 4 within the IP header */
int ipid_cpu = *(unsigned short*)(iphdr+4);
int ipid_be = iphdr[4] * 256 + iphdr[5];
The first example is CPU dependent: x86 CPUs will pull it out as a little-endian
number, but SPARC CPUs will pull it out as a big-endian number. The second form is CPU
independent: it tells all CPUs to interpret the field as a big-endian number. Note:
ntohs(*(unsigned short*)(iphdr+4)) will crash a SPARC CPU and is not a good
solution
Therefore, if you are running a Linux-based x86 firewall that interprets the IP ID
field as a little-endian number, then a string of packets from a Wintel box will
demonstrate a monotonically increasing number. However, a stream from a SPARC box will
show skipping numbers. Conversely, if the Linux-based firewall uses the (correct) field
parsing method, you'll see the reverse.
Moral of the story: Find out the byte order interpretation of the IP ID field
used within your firewall. Also send your firewall vendor flames telling them to get with
the program and represent the field in hex in the first place.
The Time-to-Live
(TTL) field is decremented by one every time a router forwards a packet. When it
reaches zero, the router discards the packet. Routing loops are a frequent
occurrence on the Internet as routers get confused as to the proper direction in which to
forward packets. The TTL mechanism assures that packet eventually "die" when and
don't get routed in loops forever. It also means that you can tell how far away a
person is from the TTL field, and sometimes what kind of platform they are running. Most
Windows machines send packets with a starting TTL of 128. This means that if your firewall
log shows a TTL=112, then you can make the guess that the sender is 16 hops away, and that
they are using a Windows machine.
Conversely, UNIX machines typically choose 64 as the starting TTL, so a packet when the
TTL is 51, then it probably isn't Windows, but it is probably 13 hops away.
This technique was once used to find the source of nmap decoy scans.
The decoys where given random TTLs, but the real scans were give normal TTLs. This allowed
the astute observer the ability to sift through the incoming decoys and find the real
scan. The nmap program was soon fixed to randomize the TTL of the real scan as
well.