I’ll consider we are only working with the IPv4 protocol suite for these examples.

In an ideal world, every field would fit inside one byte. This is not the case, of course.

Are IP options set ?
——————–

Let’s say we want to know if the IP header has options set. We can’t just try to filter out the 21st byte
because if no options are set, data start at the 21st byte. We know a “normal” header is usually 20 bytes
(160 bits) long. With options set, the header is longer than that. The IP header has the header
length field which we will filter here to know if the header is longer than 20 bytes.

+-+-+-+-+-+-+-+-+
|Version| IHL |
+-+-+-+-+-+-+-+-+

Usually the first byte has a value of 01000101 in binary.

Anyhow, we need to divide the first byte in half…

0100 = 4 in decimal. This is the IP version.
0101 = 5 in decimal. This is the number of blocks of 32 bits in the headers. 5 x 32 bits = 160 bits or 20 bytes.

The second half of the first byte would be bigger than 5 if the header had IP options set.

We have two ways of dealing with that kind of filters.

1. Either try to match a value bigger than 01000101. This would trigger matches for IPv4 traffic with IP options set,
but ALSO any IPv6 traffic !

Let’s make a filter that will find any packets containing GET requests
The HTTP request will begin by :

GET / HTTP/1.1\r\n (16 bytes counting the carriage return but not the backslashes !)

If no IP options are set.. the GET command will use the byte 20, 21 and 22
Usually, options will take 12 bytes (12nd byte indicates the header length, which should report 32 bytes).
So we should match bytes 32, 33 and 34 (1st byte = byte 0).

Tcpdump is only able to match data size of either 1, 2 or 4 bytes, we will take the following ASCII
character following the GET command (a space)

So what we are doing here is “(IP total length – IP header length – TCP header length) != 0”

We are matching any packet that contains data.

We are taking the IHL (total IP lenght

Matching other interesting TCP things :
—————————————

SSH connection (on any port) :
We will be looking for the reply given by the SSH server.
OpenSSH usually replies with something like “SSH-2.0-OpenSSH_3.6.1p2”.
The first 4 bytes (SSH-) have an hex value of 0x5353482D.

# tcpdump -i eth1 ‘tcp[(tcp[12]>>2):4] = 0x5353482D’

If we want to find any connection made to older version of OpenSSH (version 1, which are insecure and subject to MITM attacks) :
The reply from the server would be something like “SSH-1.99..”

We may want to filter ICMP messages type 4, these kind of messages are sent in case of congestion of the network.
# tcpdump -i eth1 ‘icmp[0] = 4’

If we want to find the ICMP echo replies only, having an ID of 500. By looking at the image with all the ICMP packet description
we see the ICMP echo reply have the ID spread across the 5th and 6th byte. For some reason, we have to filter out with the value in hex.

Which in English would be:
– take the upper 4 bits of the 12th octet in the tcp header ( tcp[12:1] & 0xf0 )
– multiply it by four ( (tcp[12:1] & 0xf0)>>2 ) which should give the tcp header length
– add 8 ( ((tcp[12:1] & 0xf0) >> 2) + 8 ) gives the offset into the tcp header of the space before the first octet of the response code
– now take two octets from the tcp stream, starting at that offset ( tcp[(((tcp[12:1] & 0xf0) >> 2) + 8):2] )
– and verify that they are ” 0″ ( = 0x2030 )

Of course this can give you false positives, so you might want to add a test for “HTTP” and the start of the tcp payload with: