O'Reilly    
 Published on O'Reilly (http://oreilly.com/)
 See this if you're having trouble printing code examples


Detecting Network Intrusions with Packet Filtering

by Don Parker
07/22/2004

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 up a 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 syn scans.

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

Related Reading

Managing Security with Snort & IDS Tools
By Kerry J. Cox, Christopher Gerg

We covered in the earlier article what the various switches mean, but I will detail the above example just to refresh our memories.

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 syn 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 the 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[13] = 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 (such as syn, fyn, ack, rst, psh, and 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 granular.

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 syn/ack.

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[13] = 18

As you can see, I have used src net and src port. 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 or.

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[13] = 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 host.) The bitmask at the end will drop all packets with the psh and 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.

Network-Wide Checking

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 syn/ack packets 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[13] = 18

To find all packets with a source port above 1024, I must tell tcpdump where 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 tcpdump where 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 syn/ack packet.

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.

Conclusion

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

Copyright © 2009 O'Reilly Media, Inc.