This is a primer for designing capture filters for Ethereal/Wireshark. Designing capture filters for Ethereal/Wireshark requires some basic knowledge of tcpdump syntax. The tcpdump man page is your source for complete information regarding syntax and supported primitives. Tcpdump syntax will be the first section covered in this primer. We will use what is covered in this first section to build our basic filter set. Finally, we will enter our filter strings into Ethereal/Wireshark. If you do not want to cover Tcpdump syntax, you might want to skip to the basic filter set. Or, you might just need to know how to enter the filter strings into Ethereal/Wireshark.
Tcpdump provides several primitives for easy filter design. Think of a primitive as a macro or keyword for a predefined filter.
The syntax for host filtering:
Syntax | Description |
---|---|
host host | host is either the ip address or host name |
src host host | Capture all packets where host is the source |
dst host host | Capture all packets where host is the destination |
Examples | |
host 10.10.10.10 | Capture all packets to and from 10.10.10.10 |
src host 10.10.10.10 | Capture all packets where 10.10.10.10 is the source |
dst host 10.10.10.10 | Capture all packets where 10.10.10.10 is the destination |
Port filtering:
Syntax | Description |
---|---|
port port | Capture all packets where port is either the source or destination |
src port port | Capture all packets where port is the source port |
dst port port | Capture all packets where port is the destination port |
Examples | |
port 80 | Capture all packets where 80 is either the source or destination port |
src port 80 | Capture all packets where 80 is the source port |
dst port 80 | Capture all packets where 80 is the destination port |
Network filtering:
Syntax | Description |
---|---|
net net | Capture all packets to/from net |
src net net | Capture all packets where net is the source |
dst net net | Capture all packets where net is the destination |
Examples | |
net 192.168 | Capture all packets where the network is 192.168.0.0 |
src net 192.168 | Capture all packets where the 192.168.0.0 network is the source |
dst net 192.168 | Capture all packets where the 192.168.0.0 network is the destination |
Ethernet Based:
Syntax | |
---|---|
ether proto \[primitive name] | |
Examples | |
ether proto \ip or just ip | Capture all ip packets |
ether proto \arp or just arp |Capture all address resolution protocol packets | ether proto \rarp or just rarp |Capture all reverse arp packets |
IP Based:
Syntax | |
---|---|
ip proto \[primitive name] | |
Examples | |
ip proto \tcp or just tcp | Capture all TCP segments (packets) |
ip proto \udp or just udp | Capture all UDP packets |
ip proto \icmp or just icmp | Capture all ICMP packets |
You can combine primitive expressions using the following:
Negation: ! or not Concatenation: && or and Alternation: || or or
Examples | |
---|---|
host 10.10.10.10 && !net 192.168 | Capture all packets to/from 10.10.10.10 that are not to/from 192.168.0.0 |
host 10.10.10.10 && port 80 | Capture all packets to/from 10.10.10.10 and are sourced/destined on 80 |
You can build very sophisticated capture filters by combining primitive expressions. You can also build filters that will not work. Ethereal/Wireshark will error on some really obvious filter errors.
Consider this filter for example:
host 192.168.1.10 && !host 192.168.1.10 /* Capture all packets to/from 192.168.1.10 and not to/from 192.168.1.10 Ethereal/Wireshark will error on this filter stating that the expression will reject all packets */
However Ethereal/Wireshark will parse this filter even though it will not capture any packets:
host 192.168.1.10 && not net 192.168 /* Capture all packets to/from 192.168.1.10 and not to/from the 192.168 network */
The point being sanity check your filters!
Filters based on byte offset notation are the most powerful but confusing filters to design. However, once you understand it you will be designing filters to capture ANY kind of packet. Filters based on this notation can capture packets based on any value in any location within the packet. Any of the preceeding filters can be designed with byte offset notation by locating its offset in the appropriate header.
Syntax | |
---|---|
proto [Offset in bytes from the start of the header:Number of bytes to check] | |
Examples | |
ip[8] | Go to byte 8 of the ip header and check one byte (TTL field) |
tcp[0:2] | Go to the start of the tcp header and check 2 bytes (source port) |
Now that we know how to find a value within a packet, we have to do something with the value like compare it to another value.
Tcpdump provides the usual comparison operators:
>, <, >=, <=, =, !=
Examples | |
---|---|
ip[8] = 1 | Capture all IP packets where the TTL is 1 |
tcp[0:2] = 80 | Capture all tcp segments (packets) where 80 is the source port (this is equivalent to the filter: src port 80) |
Tips to help you with byte offset notation:
Note: Ethereal/Wireshark defaults to decimal notation. You specify hexadecimal notation by adding 0x to your hex value. There are several times when hex is easier to use (like masking then comparing). As long as you know the offset in the packet and its length, you will be able to design a filter to capture it.
I have compiled a Table of some common packet offsets with filters
You can isolate bits within a packet by using bit masking. If you don't understand masking please refer to Masking the Easy Way.
To check the ip header length field:
ip[0] & 0x0f /* This instructs Ethereal/Wireshark to look at the first byte */ /*of the ip header and mask the low order nibble (header length field). */
Let's look at this in detail: The first byte of the ip header contains the ip version in the high-order nibble and the ip header length in the low-order nibble. Because we are bringing in a byte, we need to be able to isolate the nibble of interest.
0100 0101 Value of byte 0 in the packet
0000 1111 Our mask
0000 0101 Result
0101b = 5 This tells us that there are 5 32-bit words in the header.
If we wanted to find a packet that had ip options, we could design a filter like:
ip[0] & 0x0f > 5 /* Capture all packets where the ip header length is greater than 5 */
Why would you want to capture this? RFC 791 requires support of ip options. However, most garden variety ip packets do not have ip options. If a packet does have options, it is generally considered suspicious.
Building filters based on tcp flag values can alert you to all sorts of bad or odd traffic. Certain combinations of flags are known to crash some systems. Some recon probes employ illegal/unconventional flag combinations to assist in tcp stack fingerprinting.
The base filter for TCP flags is tcp[13].
The flags (from MSB to LSB) are: Reserved Reserved Urgent Ack Push Reset Syn Fin
A normal syn packet would have a value of 0000 0010b or 0×02.
If you just wanted to capture only syn packets the filter would be:
tcp[13] = 2 /* Capture packets where only the syn bit is set */
A filter for all syn packets would then be:
tcp[13] & 0x02 = 2 /* This will capture all packets where the syn bit is set.*/ /*This includes syn, syn-ack, etc. As long as a syn bit is set, this filter will capture it.*/
This section covers filters designed to match tcp payload. Be careful as libpcap can not guarantee that you are offsetting into the payload of the segment. If tcp options are present in the segment, the offset into the payload will also be off. One thing you can do is add an expression like «or tcp[12] & 0xf0 > 0×50« to the filter. This will bring in tcp segments that have options. It is a trade-off but you will still not have to go through as many packets. The SMTP filter from the basic filter page looks for commands and response codes. This filter is designed to look at the standard offset into the tcp header (tcp[20]) and match the payload with your filter string. You will need to know the hex equivalent of the ascii characters.
The rest of the SMTP commands to be converted are:
Command | String/Hex |
---|---|
HELO | 0x48454C4F |
0x4D41494C | |
RCPT | 0×52435054 |
DATA | 0×44415441 |
RSET | 0×52534554 |
SEND | 0x53454E44 |
SOML | 0x534F4D4C |
SAML | 0x53414D4C |
VRFY | 0×56524659 |
EXPN | 0x4558504E |
NOOP | 0x4E4F4F50 |
QUIT | 0×51554954 |
TURN | 0x5455524E |
Putting all of this together (including packets with tcp options) in a filter string:
port 25 and (tcp[12] & 0xf0>0x50 or tcp[20:4] = 0x48454C4F or tcp[20:4] = 0x4D41494C or tcp[20:4] = 0x52435054 or tcp[20:4] = 0x44415441 or tcp[20:4] = 0x52534554 or tcp[20:4] = 0x53454E44 or tcp[20:4] = 0x534F4D4C or tcp[20:4] = 0x53414D4C or tcp[20:4] = 0x56524659 or tcp[20:4] = 0x4558504E or tcp[20:4] = 0x4E4F4F50 or tcp[20:4] = 0x51554954 or tcp [20:4] = 0x5455524E)
Now the reply/response codes:
221 | 0×32323120 |
214 | 0×32313420 |
220 | 0×32323020 |
221 | 0×32323420 |
250 | 0×32353020 |
251 | 0×32353120 |
354 | 0×33353420 |
421 | 0×34323120 |
450 | 0×34353020 |
451 | 0×34353120 |
452 | 0×34353220 |
500 | 0×35303020 |
501 | 0×35303120 |
502 | 0×35303220 |
503 | 0×35303320 |
504 | 0×35303420 |
550 | 0×35353020 |
551 | 0×35353120 |
552 | 0×35353220 |
553 | 0×35353320 |
554 | 0×35353420 |
SMTP reply filter string (with tcp options):
port 25 and (tcp[12] & 0xf0> 0x50 or tcp[20:4] = 0x32323120 or tcp[20:4] = 0x32323420 or tcp[20:4] = 0x32353020 or tcp[20:4] = 0x32353120 or tcp[20:4] = 0x33353420 or tcp[20:4] = 0x34323120 or tcp[20:4] = 0x34353020 or tcp[20:4] = 0x34353120 or tcp[20:4] = 0x34353220 or tcp[20:4] =0x35303020 or tcp[20:4] = 0x35303120 or tcp[20:4] = 0x35303220 or tcp[20:4] = 0x35303320 or tcp[20:4] = 0x35303420 or tcp[20:4] =0x35353020 or tcp[20:4] = 0x35353120 or tcp[20:4] = 0x35353220 or tcp[20:4] = 0x35353320 or tcp[20:4] = 0x35353420)
By combining the two filter strings above, you will be able to capture SMTP conversations without pulling in the message data. You may also want to capture connection initiation and tear down. This is easy enough by adding an expression for tcp flags to the above strings.
tcp[13] & 0x02 = 0x02 any with the syn flag set tcp[13] & 0x01 = 0x01 any with the fin flag set tcp[13] & 0x04 = 0x04 any with the reset flag set
A more efficient alternative to the three flag filters above is to mask-in the three least significant bits and pull in anything not equal to 0. This is expressed as:
(tcp[13] & 0x07 != 0)
NOTE: Tcpdump does have tcp flag primitives for all but reserved flag bits. I prefer to use the strings above but you could have written the above flag filters using the flag primitives.
Putting all of this together to form a big filter string:
port 25 and (tcp[12] & 0xf0>0x50 or tcp[13] & 0x07 != 0 or tcp[20:4] = 0x48454C4F or tcp[20:4] = 0x4D41494C or tcp[20:4] = 0x52435054 or tcp[20:4] =0x44415441 or tcp[20:4] = 0x52534554 or tcp[20:4] = 0x53454E44 or tcp[20:4] = 0x534F4D4C or tcp[20:4] = 0x53414D4C or tcp[20:4] =0x56524659 or tcp[20:4] = 0x4558504E or tcp[20:4] = 0x4E4F4F50 or tcp[20:4] = 0x51554954 or tcp [20:4] = 0x5455524E or tcp[20:4] =0x32323120 or tcp[20:4] = 0x32323420 or tcp[20:4] = 0x32353020 or tcp[20:4] = 0x32353120 or tcp[20:4] = 0x33353420 or tcp[20:4] =0x34323120 or tcp[20:4] = 0x34353020 or tcp[20:4] = 0x34353120 or tcp[20:4] = 0x34353220 or tcp[20:4] = 0x35303020 or tcp[20:4] =0x35303120 or tcp[20:4] = 0x35303220 or tcp[20:4] = 0x35303320 or tcp[20:4] = 0x35303420 or tcp[20:4] = 0x35353020 or tcp[20:4] =0x35353120 or tcp[20:4] = 0x35353220 or tcp[20:4] = 0x35353320 or tcp[20:4] = 0x35353420)
This section will assist you with building your basic filter set.
The basic filter set should include filters to capture packets on well known service ports.
The table below should get you started.
Filter Name | Filter String |
---|---|
HTTP_80 | port 80 |
DNS_53 | port 53 |
SMTP_25 | port 25 |
FTP_CMD_21 | port 21 |
TELNET_23 | port 23 |
POP3_110 | port 110 |
IMAP_143 | port 143 |
NNTP_119 | port 119 |
LDAP_389 | port 389 |
NCP_524 | port 524 |
SNMP_161_162 | port 161 or port 162 |
Netbios_SMB_137_138_139 | port 137 or port 138 or port 139 |
Host based filtering | host Enter the ip address or hostname after host |
Port based filtering | port Enter the port number after port |
IP Fragmentation | ip[6:2] & 0×2000 = 0×2000 or ip[6:2] & 0x1fff !=0×0000 |
IP_All | ip |
TCP_All | tcp |
UDP_All | udp |
ARP_Ether | arp |
ICMP_ALL | icmp |
ICMP_ping | icmp[0]= 0 or icmp[0]= 8 |
ICMP_noPing | icmp[0]!= 0 and icmp[0]!= 8 |
IGMP | ip[9] = 2 |
EGP | ip[9] = 8 |
Multicast | net 224.0.0 |
Multicast | ip multicast |
Multicast | ether multicast |
SMTP Commands - HELO, MAIL,RCPT,DATA,RSET,SEND,SOML,SAML,VRFY,EXPN,NOOP,QUIT AND TURN
port 25 and (tcp[12] & 0xf0 > 0x50 or tcp[20:4] = 0x48454C4F or tcp[20:4] = 0x4D41494C or tcp[20:4] = 0x52435054 or tcp[20:4] = 0x44415441 or tcp[20:4] = 0x52534554 or tcp[20:4] = 0x53454E44 or tcp[20:4] = 0x534F4D4C or tcp[20:4] = 0x53414D4C or tcp[20:4] = 0x56524659 or tcp[20:4] = 0x4558504E or tcp[20:4] = 0x4E4F4F50 or tcp[20:4] = 0x51554954 or tcp [20:4] = 0x5455524E)
SMTP Reply/response codes - 221,214,220,221,250,251,354,421,450,451,452,500,501,502,503,504,550,551,552,553 and 554
port 25 and (tcp[12] & 0xf0 > 0x50 or tcp[20:4] = 0x32323120 or tcp[20:4] = 0x32323420 or tcp[20:4] = 0x32353020 or tcp[20:4] = 0x32353120 or tcp[20:4] = 0x33353420 or tcp[20:4] = 0x34323120 or tcp[20:4] = 0x34353020 or tcp[20:4] = 0x34353120 or tcp[20:4] = 0x34353220 or tcp[20:4] = 0x35303020 or tcp[20:4] = 0x35303120 or tcp[20:4] = 0x35303220 or tcp[20:4] = 0x35303320 or tcp[20:4] = 0x35303420 or tcp[20:4] = 0x35353020 or tcp[20:4] = 0x35353120 or tcp[20:4] = 0x35353220 or tcp[20:4] = 0x35353320 or tcp[20:4] = 0x35353420)
SMTP Commands and reply (combination of the two above with tcp options, syn, fin, or reset flag set)
port 25 and (tcp[12] & 0xf0 > 0x50 or tcp[13] & 0x07 != 0 or tcp[20:4] = 0x48454C4F or tcp[20:4] = 0x4D41494C or tcp[20:4] = 0x52435054 or tcp[20:4] = 0x44415441 or tcp[20:4] = 0x52534554 or tcp[20:4] = 0x53454E44 or tcp[20:4] = 0x534F4D4C or tcp[20:4] = 0x53414D4C or tcp[20:4] = 0x56524659 or tcp[20:4] = 0x4558504E or tcp[20:4] = 0x4E4F4F50 or tcp[20:4] = 0x51554954 or tcp [20:4] = 0x5455524E or tcp[20:4] = 0x32323120 or tcp[20:4] = 0x32323420 or tcp[20:4] = 0x32353020 or tcp[20:4] = 0x32353120 or tcp[20:4] = 0x33353420 or tcp[20:4] = 0x34323120 or tcp[20:4] = 0x34353020 or tcp[20:4] = 0x34353120 or tcp[20:4] = 0x34353220 or tcp[20:4] = 0x35303020 or tcp[20:4] = 0x35303120 or tcp[20:4] = 0x35303220 or tcp[20:4] = 0x35303320 or tcp[20:4] = 0x35303420 or tcp[20:4] = 0x35353020 or tcp[20:4] = 0x35353120 or tcp[20:4] = 0x35353220 or tcp[20:4] = 0x35353320 or tcp[20:4] = 0x35353420)
NOTE: These SMTP filters will also capture any packets to/from port 25 with tcp options. If you want to see how to build these filters, please refer to payload filtering.
Field | Length(bits) | Tcpdump Filter |
---|---|---|
IP Header Length | 4 | ip[0] & 0x0f |
IP Packet Length | 16 | ip[2:2] |
IP TTL | 8 | ip[8] |
IP Protocol | 8 | ip[9] |
IP Address Source | 32 | ip[12:4] |
IP Address Destination | 32 | ip[16:4] |
IP Fragmentation | flag = 3 and Offset = 13 | ip[6:2] & 0×2000 = 0×2000 or ip[6:2] & 0x1fff !=0×0000 |
TCP Source Port | 16 | tcp[0:2] |
TCP Destination Port | 16 | tcp[2:2] |
TCP Header Length | 4 | tcp[12] & 0xf0 |
TCP Flags | 8 | tcp[13] |
TCP Window Size | 16 | tcp[14:2] |
ICMP Type | 8 | icmp[0] |
ICMP Code | 8 | icmp[1] |
Masking allows us to isolate individual bits within the packet. It is based on an AND operator. The way an AND operator works is very simple so let's start with it. If you are comparing two bits together (A and B) with an AND operator. You can have four possible results or states. Here is the truth table so you can see this:
A | B | Result | Explanation |
---|---|---|---|
0 | 0 | 0 | If A and B are zero the result is zero |
0 | 1 | 0 | If A is zero and B is 1 the result is zero |
1 | 0 | 0 | If A is 1 and B is zero the result is zero |
1 | 1 | 1 | If A is 1 and B is 1 the result is 1 |
The only result that has a 1 is when both A and B are 1. Masking uses this by comparing a 1 (masking-in) with the bit of interest and comparing the rest of the bits with a zero (masking-out).
Let's look at an example: Say you have a four bit value (nibble) but you only care if the most significant bit (MSB) is 1. You would mask this nibble with 1000 binary (b) so that anything other than the MSB will be zero. Here is a table so you can see this:
The nibble is 1010b and our mask is 1000b
1 | 0 | 1 | 0 | Nibble of Interest |
1 | 0 | 0 | 0 | Our mask |
1 | 0 | 0 | 0 | Result |
What if our nibble was 0110b ?
Nibble 0110b
Mask 10000
1 | 1 | 0 | 0 | Nibble of Interest |
1 | 0 | 0 | 0 | Our mask |
0 | 0 | 0 | 0 | Result |
The result in the above examples depended on the value of the MSB of our nibble of interest. The rest of the bits were «zeroed» or «masked-out» by the mask.
Numbers | |
---|---|
0 | 0×30 |
1 | 0×31 |
2 | 0×32 |
3 | 0×33 |
4 | 0×34 |
5 | 0×35 |
6 | 0×36 |
7 | 0×37 |
8 | 0×38 |
9 | 0×39 |
Capital Letters | |
A | 0×41 |
B | 0×42 |
C | 0×43 |
D | 0×44 |
E | 0×45 |
F | 0×46 |
G | 0×47 |
H | 0×48 |
I | 0×49 |
J | 0x4A |
K | 0x4B |
L | 0x4C |
M | 0x4D |
N | 0x4E |
O | 0x4F |
P | 0×50 |
Q | 0×51 |
R | 0×52 |
S | 0×53 |
T | 0×54 |
U | 0×55 |
V | 0×56 |
W | 0×57 |
X | 0×58 |
Y | 0×59 |
Z | 0x5A |
Space | 0×20 |