Linux
firewalling code has come a long way since the time ipfwadm was
introduced in kernel version 1.2.1 in 1995. Ipfwadm enabled standard
TCP/IP packet filtering features such as filtering by source/target
addresses and port numbers. Then, in early 1999, when the first stable
2.2.0 kernel was released, firewalling code was replaced with new
ipchains-controlled code. New features included support for chains of
rules, fragmentation handling, better network address translation (NAT)
support and several usability improvements. Readers should be reminded
that Linux firewalling includes kernel-level code (usually in form of
loadable module or kernel source patch) and user-level code (a control
utility such as /usr/bin/ipchains, that is used to insert packet rules
into kernel-space). Thus whenever new Linux firewalling code was
introduced it involved both kernel and userspace code rewrite.
With the release of 2.4 in 2001, iptables code came out.
It introduced many important improvements such as stateful firewalling,
filtering packets based on any combination of TCP flags and on MAC
address, more configurable and flexible logging, powerful and easy to
use support for network address translation (NAT) and transparent
proxies, DoS blocking support by rate-limiting and others. (For details
see, for example A Comparison of iptables Automation Tools.)
However, the most important architectural addition was
the introduction of the modular architecture. For example, ipchains and
ipfwadm compatibility mode is implemented as a set of kernel modules
that can be inserted into the running kernel to provide the
corresponding functionality. In addition, user-coded modules are now
possible. For example, filtering by port range, TTL value and time of
packet arrival, stateful inspection for custom protocols, and random
packet inspection are not part of the iptables suite, but were
implemented later. Many new and interesting modules were coded. To
program a module one has to create a kernel-level part that will be
compiled into a loadable module and user-level part that will be used to
control the filtering behavior. For more details see Rusty Russell's Linux iptables HOWTO.
This article will cover string pattern matching
functionality (evidently, implemented as a module), which allows limited
investigation of the packet payload. This is a significant breakthrough
for iptables technology since it goes beyond inspecting TCP/IP flags,
which is standard for packet filter firewalls. It is well-known that
firewalls can be loosely categorized into proxies and packet filters.
The latter "know" the application-level protocols such as telnet, HTTP
or SMTP and can inspect the protocol payloads and verify the commands.
This comes at a significant performance penalty since packets have to be
processed higher in the network protocol stack in application layer.
For each inspected protocol a new proxy should be written. The packet
filters, on the other hand, can usually only inspect source and target
addresses and ports, TCP/IP flags and have to totally ignore
higher-layer protocol payloads. Due to that reason, they are usually
much faster than proxy firewalls (3-10 times). Thus proxies are used for
more granular security while packet filters are used on higher
bandwidth lines for higher throughput.
In light of the above, adding content inspection
capabilities in iptables presents an attempt to cross the bridge between
two firewall groups without getting stuck in disadvantages of either
method. It also clearly demonstrated an advantage of a new modular
architecture over old ipchains code. It should be noted that packet
filter such as iptables does not become higher-layer-protocol-aware
since it still operates at network level (layer 3 in OSI structure), but
is only allowed to peek at payloads, rather than analyze the
application-level communication structure.
Before the string matching module was started in May
2001, there were attempts to add content inspection to iptables-based
firewalls. One such project is Hogwash, which couples the Snort IDS rule-matching engine with iptables in order to respond to packets with attack signatures in them.
Now we will provide a step-by-step direction for
enabling string matching packet inspection for RedHat Linux. Standard RH
7.2 comes with iptables 1.2.3 and 1.2.4 is available as an RPM update.
However, string-matching functionality is not included in either since
it is marked as "experimental" by developers. Thus some kernel
recompilation is required.
If you are using RH 7.1-7.2 you already have kernel
version 2.4 installed. You need at least 2.4.4 for the latest iptables
1.2.4 to function. It is always recommended to download the latest
kernel version available from you system distributor. At present, there
is one exception from this rule: iptables-1.2.4 string matching patch
does not seem to work with 2.4.9 kernel version. You should have kernel
source RPM package installed (that usually puts the full source tree in
/usr/src/linux-2.4.x) or just have the kernel source downloaded from
elsewhere (such as www.kernel.org or one of its mirrors).
For this article, the latest 2.4.16 will be used as an
example. Test were also run with 2.4.7 kernel shipped with RedHat 7.2,
but the 2.4.7 kernel is not recommended due to several minor bugs in
security mechanisms such as SYN-cookie protection and iptables
save/restore functionality.
Surely it is redundant to remind the reader to backup
the entire kernel source tree before starting the experiment or keep a
source RPM handy. Keep in mind, that the latter will only help if you
have not compiled the kernel before.
Now, iptables code should be downloaded from http://netfilter.samba.org/iptables-1.2.4.tar.bz2
. After archive is unpacked, the iptables should be configured and the
appropriate source code merged with the kernel source tree. The
semi-automated program is available for that purpose. First, one might
want to run the program to include the iptables patches that are already
considered stable, but have not been included in the kernel release.
From the directory where iptables are unpacked (in this example iptables
are in /home/anton/iptables-1.2.4 and kernel source is in
/usr/src/linux-2.4.16) run:
make pending-patches KERNEL_DIR=/usr/src/linux-2.4.16
That
will start the process of interactive patch application. It appears,
that, while you can safely apply all of those patches, none are required
for iptables string support. Say
y (yes) to whichever patches you think will be needed for your installation. If you want to play it safe choose none.
Now we are ready to apply experimental patches such as string matching support. Run:
make patch-o-matic KERNEL_DIR=/usr/src/linux-2.4.16
In the interactive session that follows answer
y (yes) to application of string.patch. The program will go though all available patches, including the stable ones.Testing... string.patch NOT APPLIED ( 2 missing files) The string patch: Author: Emmanuel Roger Status: Working, not with kernel 2.4.9 This patch adds CONFIG_IP_NF_MATCH_STRING which allows you to match a string in a whole packet. THIS PATCH DOES NOT WORK WITH KERNEL 2.4.9 ! Do you want to apply this patch [N/y/t/f/q/?] y
While
the rest of the patches may sound like fun as well, they are not
relevant to this article. If you choose to install any other patches,
heed the warnings given by developers about what patches break
functionality (such as dropped tables patch). Make sure you do not
install the MAC filtering patch since it was recently found to contain a
bug.
Now we are ready to compile the user-space code and the libraries:
make KERNEL_DIR=/usr/src/linux-2.4.16
and then install them (iptables program goes in /usr/local/user/sbin and libraries go into /usr/local/lib/iptables). As root:
make install KERNEL_DIR=/usr/src/linux-2.4.16
Now we are ready to configure and compile the kernel:
cd /usr/src/linux-2.4.16
Any of the kernel
configuration methods can be used. Detailed discussion of this is
provided in a huge number of Internet sources such as kernel HOWTO. In
brief:
make xconfig
In the GUI that appears got to Netfilter configuration and choose
m (for modular support) in String match support (EXPERIMENTAL). See picture
Then follow with a standard
make dep ; make bzImage ; make modules ; make modules_install
Now
install the kernel itself using you favorite method and reboot. Upon
reboot, test whether the iptables with string support is enabled. As
root:
/usr/local/sbin/iptables -m string -help
It should produce the iptables standard help message appended by:
STRING match v1.2.4 options: --string [!] string Match a string in a packet
The
resulting functionality will allow you to match packets by the content.
Some real life tests can be performed using netcat or telnet to make
sure we can really grab packets by the content.
Run:
iptables -A INPUT -m string --string "test" -j LOG --log-level info --log-prefix "TEST"
Then start a netcat server by:
nc -l -p 3456
Connect to it via:
telnet localhost 3456
Now type:
test whatevertestdoes
It should produce the
message similar to the following in your log files (providing you log
messages with severity level ‘info’ somewhere)
Nov 27 23:16:53 pua kernel: TEST IN=lo OUT=MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1 LEN=2154 TOS=0x00 PREC=0x00 TTL=64 ID=42880 DF PROTO=TCP SPT=3128 DPT=33018 WINDOW=32767 RES=0x00 ACK PSH URGP=0 Nov 27 23:16:53 pua kernel: TEST IN=lo OUT=MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1LEN=1830 TOS=0x00 PREC=0x00 TTL=64 ID=17451 DF PROTO=TCP SPT=8000 DPT=33017 WINDOW=32767 RES=0x00 ACK PSH URGP=0
Every time the string
test
is present in TCP/IP packets, the above log message is produced. What
is it good for? Many things. As was suggested in the excellent article, Filtering packets based on string matching,
by sysctl at Linuxguru.net, it can be used to block all those pesky IIS
worms from filling your UNIX web server log files with requests to cmd.exe
etc. Those worms cannot hurt you, but if your /var partition (where
logs are usually stored) is not too big this measure can be pretty
helpful. Just silently drop all those port 80 requests or log them using
the message limiting facility:
To silently drop them:
iptables -I INPUT -j DROP -p tcp -s 0.0.0.0/0 -m string --string "cmd.exe"
To only log no more then 1 message per hour:
iptables -I INPUT -j LOG -p tcp -s 0.0.0.0/0 -m string --string "cmd.exe" -m limit --limit 1/hour
Replace the string with whatever the new Windows "worm-of-the-moment" is requesting and just add another rule.
You can also stop requests to things you do not want to have retrieved from your Web server. Apparently,
test.cgi
should be removed, rather than put into iptables rules. However, in
some cases the ability to stop requests for specific documents at the
TCP/IP level before they reach the Web server access control facility
might provide the needed "defense in-depth" for your Web infrastructure.
Another great suggestion from Bill Stearns (author of Mason firewall building script) is to convert your Snort
network IDS rules into iptables rules with string support. Snort IDS
attack signature database contains about 1200 signatures and appears to
be the biggest publicly available attack database suitable for instant
deployment. The ability to use the ready-made signatures for iptables is
of immense value. The page that describes his experimental software is
at http://www.stearns.org/snort2iptables/.
There, you can find the shell script to convert a standard Snort
ruleset into iptables rules. Here are a couple of examples for
well-known Linux attacks against mountd and bind network daemons:
Snort rules:
1. alert udp $EXTERNAL_NET any -> $HOME_NET 518 (msg:"EXPLOIT ntalkd x86 linux overflow"; content:"|0103 0000 000 0 0001 0002 02e8|"; reference:bugtraq,210; classtype:attempted-admin; sid:313; rev:2;) 2. alert tcp $EXTERNAL_NET any -> $HOME_NET 53 (msg:"EXPLOIT named tsig infoleak"; content: "|AB CD 09 80 00 00 00 01 00 00 00 00 00 00 01 00 01 20 20 20 20 02 61|"; reference:cve,CAN-2000-0010; reference:bugtraq,2302; reference:arachnids,482; classtype:attempted-admin; sid:303; rev:3;) 3. alert udp $EXTERNAL_NET any -> $HOME_NET 635 (msg:"EXPLOIT x86 linux mountd overflow"; content:"|5eb0 0289 06fe c889 4604 b006 8946|"; reference:cve,CVE-1999-0002; classtype:attempted-admin; sid:315; rev:1;)
Converted iptables rules:
1. iptables -A SnortRules -p udp -s $EXTERNAL_NET -d $HOME_NET --dport 518 -m string --string " è" -j LOG --log-prefix "SID313 " # "EXPLOIT ntalkd x86 linux overflow" bugtraq,210 classtype: attempted-admin sid:313
2. iptables -A SnortRules -p tcp -s $EXTERNAL_NET -d $HOME_NET --dport 53 -m string --string "«Í .a" -j LOG --log-prefix " SID303 " # "EXPLOIT named tsig infoleak" cve,CAN-2000-0010 bugtraq,2302 arachnids,482 classtype:attempted-admin sid:303
3. iptables -A SnortRules -p udp -s $EXTERNAL_NET -d $HOME_NET --dport 635 -m string --string "^° ‰ þȉF ° ‰F" -j LOG --log-prefix " cve-CVE-1999-0002 " # "EXPLOIT x86 linux mountd overflow" classtype:attempted-admin sid:315
It
is easy to see that the above conversion uses the buffer overflow
string used for the above exploits to catch the attack. Some rules are
not converted mostly due to the fact that Snort is still "smarter" than
iptables in fragmentation handling.
Overall, iptables with string support can be used to
protect networks (if deployed on the organization gateway) and
individual hosts (deployed as a part of host hardening) from many
attacks on the network services that have to be open to the world (WWW,
mail, DNS, ftp) and have not been protected by ordinary packet filters.
In addition, iptables string matching can also help with policy
enforcement - just implement the rules that stop inappropriate content
by keyword. There might be better solutions of implementing the content
scanning, but another layer of protection never hurts.
Anton Chuvakin,
Ph.D. is in the process of looking for another infosec job after having
his fun dot-bomb experience. He spends his plentiful pastime running
Linux, building a small infosec portal and preparing for CISSP. This article originally appeared on SecurityFocus.com -- reproduction in whole or in part is not allowed without expressed written consent.