Detecting Network Intrusions with Packet Filteringby Don Parker
In my first article, I covered some basic
bpf filter usage as well as bitmask
applications. I also explained that understanding TCP/IP itself has a large
impact on how well you will be able to write filters to parse out wanted
traffic. Though you don't need to have an encyclopedic knowledge of the
building blocks of TCP, UDP, or IP, it certainly helps. Understanding that the
source and destination port numbers come in the first four bytes of both the
TCP and UDP headers will help you understand how to optimize your search
filters. The better you know your TCP/IP, the better your filters will be.
The Example Network
In an effort to put the usage of these filters into context I will explain a normal day in the life of a network security analyst. This day will focus on the usage of building and further explaining some complex examples. To clarify our example, I assume that the make-believe network has all packets that are flagged by the intrusion detection system logged to a central database. I mention this stipulation because not every real network operates in this fashion. Some only turn on full logging after they've detected suspicious activity. For our purposes, though, the system logs all activity.
Our example network is a medium-sized corporate network that offers the following services to the outside world: HTTP, HTTPS, FTP, SMTP, and SSH. This is a small subset of possible services that a real network may offer. For example purposes, the people scanning our networks will have a 192.168/16 address and our network addresses will be within the 10.10/16 range.
After having spent a good weekend away from the office, I roll into work bright-eyed and bushy-tailed. Having dispensed with my normal routine of reading the mailing lists and various security sites, I hit my workstation to verify the intrusion-detection logs for any suspicious activity directed against my company's networks.
It seems that, starting late night on Friday after we had all left, a
couple of people who are unlikely to have any good intent performed a wholesale
scan of our network. I notice that the scan went from port 1 right up to port
65535. A scan of this size will make this unfeasible to check by specifying
only the aggressor's IP address. This is not a problem, as I will simply write
bpf filter to give me the specific information I require.
The scan took the form of a simple
syn scan. This is where the person
scanning sends out a
syn packet to a port or series of ports. If a service such
as IIS is listening on port 80 and receives a
syn packet, the server will send a
syn/ack back to the scanner, indicating that a service is listening.
This follows the normal behavior of TCP/IP. This predictable nature of TCP/IP
is why you will mainly see
Building a Filter
Knowing that we have various services running on our network, I take the IP address that I noted conducting the scan and write the following filter:
-nXvSs 0 tcp and host 192.168.1.100
We covered in the earlier article what the various switches mean, but I will detail the above example just to refresh our memories.
- The hyphen always comes before all of the switches.
nmeans not to convert IP addresses to domain names.
vsays to supply all information possible about the packet. This will show values such as the IP ID numbers.
Smeans absolute logging. This will always show the real values for the TCP sequence and acknowledgement numbers in the packets.
smeans the snaplength, the amount of the packet you want to view, measured in bytes.
0is the value of the snaplength measured in bytes once again. If you put in 0, the filter will default to your network value.
tcpdumpto show all packets with a valid TCP header. This will exclude packets such as ARP traffic.
andjust separates our arguments.
tcpdumpthat the following IP address is a host instead of a subnet.
Now that I have built this filter, I execute it. About five minutes later, I
have a traffic file. Looking inside, I note that it contains a ton of
packets. These are the
syn packets that the scanner sent to our machines.
Realizing my mistake, I build a filter that includes a bitmask to screen out
syn packets, showing only
syn/ack packets, if there are any. The filter is:
-nXvSs 0 tcp and host 192.168.1.100 and tcp = 18
The difference between the first filter and the one above is the
bitmask. Within the TCP header is a byte field containing the packet's flags
urg). All of these flags have a decimal
value, as mentioned in the previous article. By adding up the decimal value of
various flag combinations, I can stipulate exactly which packets I want by
adding a bitmask. Adding a bitmask to our query makes it far more
With this filter and bitmask created,
tcpdump produces a much smaller file
from the database. I quickly scan this file and note that indeed all of our
servers have responded as they should have, with a
At this point, I've begun to wonder how many other people have possibly been scanning our networks over the weekend. With this concern in mind, I decide to take a different approach to my filter design.
Filtering Outbound Traffic
I will design a filter to show me all of the
syn/ack packets our network gave
out over the weekend. The filter is as follows:
-nXvSs 0 tcp and src net 10.10 and src port (21 or 22 or 25 or 80 or 8080) and tcp = 18
As you can see, I have used
src net and
These are quite handy to specify an entire IP range. This is useful if the
company your work for has a range of IP addresses. The
src port is also helpful when you only want to stipulate packets emanating from your
network. Finally, I've also specified the string of ports that I want to
check, all separated by the use of
At this point, I realize that someone directed a fair amount of traffic at our network over the weekend. There was an actual cluster of IP addresses all scanning my employer's network, and, from their IP addresses, they are all from the same subnet. This simplifies the task of my next filter. Now I want to see if this perhaps organized crew has managed to gain entry. That's the purpose of the following filter and bitmask:
-nXvSs 0 tcp and src net 192.168 and dst net 10.10 and tcp = 24
This filter will show me all of the traffic from the attacking subnet and any
responses from our network. (If your network has only one valid external IP
address, substitute the
dst net for
bitmask at the end will drop all packets with the
ack flags set. This
should show me any potential exploit code sent against our network resources,
as well as any answer to it from our company network.
I started with a simple checking of the logs, went on to verify one particular IP address, checked out a subnet on specific ports, and finally made the very focused filter seen above. This saves me enormous amount of time by allowing me to concentrate only on important traffic. If you have services, you can't avoid being scanned. You can control being exploited, especially if you know when people try to exploit your network assets. The latest examples above demonstrate how to narrow down your filter reporting.
After having checked out the resulting traffic file, I notice that someone did indeed send valid exploit code to our servers. Fortunately, there were no breaches; all of our servers are up to date on patches and they sent back only expected responses.
It may be time to perform an overall check of the corporate network and the logged packets. As with many corporate environments, our users have access to email for their work accounts. There's been a recent spate of trojans and viruses, and should one of our users execute an attachment, he may have just allowed an outsider access to his computer and the entire network.
Most of these trojans, viruses, and malware communicate back on an ephemeral
port, or at least listen on one. An ephemeral port is one above port 1024. With
this concern in mind, I decide to create a filter and bitmask that will check
for any ports on our network above 1024 that have issued any
back in response to a
syn packet. The filter is:
-nXvSs 0 tcp and src net 10.10 and tcp[0:2] > 1024 and tcp = 18
To find all packets with a source port above 1024, I must tell
to look for that value. The first two bytes of the TCP and UDP headers contain
the port number. The highest value you can have with 2 bytes is 65535, and
this is why there are only 65535 ports. All I need to do is tell
to look for this value in the TCP header -- hence the
tcp[0:2]. By listing where in the header (byte 0 in the header)
and how many bytes to check from that spot (look at 2 bytes following byte 0),
I can assign a binary value. In this case, I have told
tcpdump to drop anything
with a value greater than 1024. The last part of the filter is the bitmask of
18, which equates to a
With this filter and bitmask in place, it is trivial to find out if any of my company's computers have trojans or peer-to-peer software running.
What all of these
bpf filters and bitmasks show you is that it is indeed
possible, if not easy, to manage the output of an intrusion-detection system. It
is simply the matter of getting used to the syntax to do so. The savings in
time alone of learning this will easily offset the time you would have spent
manually checking all of the packets.
Don Parker is an independent consultant.
Return to the Security DevCenter