Lance
Spitzner
Last Modified: 28 July, 1999
Stateful inspection is becoming the buzzword of choice in the world of firewall marketing. A combination of packet filtering and proxy inspection, stateful inspection is consider by some to be the best of both worlds. But what exactly is stateful inspection? This article attempts to answer that question. My goal is to not only understand how stateful inspection works, but how 'stateful' it really is. For this article, I focus on the stateful inspection connections table of Check Point FireWall-1. I selected this firewall for the simple reason this is the firewall I have the most experience with. Included at the bottom is a PERL script that will help you to read and understand your own stateful connections table for your FW-1 firewalls.
Stateful Inspection
This paper started off with a basic question. If you have a firewall with a rule base that allows anything through it (any - any - accept), will the firewall allow a new TCP connection that is initiated with an ACK? A part of me said yes. If the firewall allows everything, then any packet should go through. However, a part of me also said no. Based on how stateful inspection works, the packet should be dropped.
Many of you may be wondering as to what kind of weirdo initiates a TCP connection with a ACK packet. My answer to you is a weirdo who is trying to figure out how the stateful table works. With a rule base that allows anything, you might think that this packet should go right through. But after a better understanding of stateful inspection, you might change your mind.
My initial understanding of stateful inspection (at least on Check Point FireWall-1) worked as follows. Whenever a firewall receives a SYN packet initiating a TCP connection, that SYN packet is reviewed against the Firewall rulebase. Just like a router, this SYN packets is compared to the rules in sequential order (starting with rule 0). If the packet goes through every rule without being accepted, the packet is denied. The connection is then dropped or rejected (RST is sent back to the remote host). However, if the packet is accepted, the session is then entered into the firewall's stateful connection table, which is located in kernel memory. Every packet that follows (that does not have a SYN) is then compared to the stateful inspection table. If the session is in the table, and the packet is part of that session, then the packet is accepted. If the packet is not part of the session, then it is dropped. This improves system performance, as every single packet is not compared against the rule base, only SYN packets initiating a connection are compared to the rule base. All other TCP packets are compared to the state table in kernel memory (very fast).
Now, back to our original question. If you initiate a session with an ACK packet, will the firewall accept the packet, even with a rulebase that accepts everything? As we said earlier, your would think yes. But now that we have a better understanding of the connections table, maybe the answer is no. When the firewall receives the ACK packet, it is going to compare it to the state table in kernel memory, not the rule base. However, the firewall will not have this session in its state table, there was never a SYN packet. So, does the firewall accept the packet, or drop it since there is no entry for it in the state table?
The Result - How
FW-1 Builds a Connection.
The results were surprising. Not only was the ACK packet accepted,
but it was entered into the state table. My understanding of the
firewall state table was incorrect. What I discovered is this, when
the firewall receives a packet that is NOT part of the state connection
table, that packet is checked against the rule base, regardless if it is
a SYN, ACK or 'whatever' packet. If the rule base accepts the session,
then it is entered into the state table. All subsequent packets of
that session are compared to the state connection table and then
accepted. Since there is an entry in the state table for the session,
the packets are accepted without being compared to the rulebase.
Below is some of the output from the tool, fwtable.pl,
which converts the data found in 'fw tab -t connections'. The entries
you see below are part of my firewall state connections table created by
initiating connections with ACK packets.
mozart #fwtable
---- FW-1 CONNECTIONS TABLE ---
Src_IP Src_Prt Dst_IP Dst_Prt IP_prot Kbuf Type Flags Timeout
192.168.7.131
10003 207.229.143.8 25
6 0
16385 02ffff00 2845/3600
192.168.7.131
10002 207.229.143.8 24
6 0
16385 02ffff00 2845/3600
192.168.7.131
10001 207.229.143.8 23
6 0
16385 02ffff00 2845/3600
Here you see three packets accepted and entered into the firewall state table. However, these three packets were initiated with ACK packets. The same thing is true for Null, SYN/ACK, and various other "invalid" packets, such as FIN/ACK. If a packet is not part of the state table, it is then compared to the rulebase. If the rulebase accepts the packet, the session is then added to the state table. If the packet is not accepted by the rulebase, the packet is dropped/rejected, killing the session. This is how the firewall "maintains" connections when you do a 'fwstop;fwstart'. When you bounce the firewall, the connections table is cleared, nothing is maintained. However, any concurrent connectins will most likely be sending ACKs. The firewalls sees these packets, verifies them against the rulebase, and rebuilds the connections table. All of this is transparent to the end user. This is why you lose Authenticated and Encrypted sessions, the firewall does not have the 'initial state' for these connections. Also, notice the timeout in the right hand column, 3600 seconds. After entering a session into its state table, the firewall leaves that entry. That means you have 60 minutes to create and send another packet to reset the timeout clock. The timeout properties can be set in the control properties menu.
NOTE: valid FIN or RST packets cannot build a session,
as they are used to tear a connnection down. Also, the only packet
that was NEVER added to the state table were 'Xmas' packets created with
Fyodor's nmap (-sX option), however these packets were logged.
Another thing I learned, stateful inspection for FW-1 looks only at
Source/Destination IP and Port numbers for determining a session.
It does NOT care about sequence numbers, as I was making up all sorts of
whacked out sequence numbers, which the firewall accepted. Nor does
it care about packet type when building a connection. When you send
a SYN packet initializing a session, the Firewall compares it to the rulebase.
If accepted, it adds it to the state table, as we discussed before.
At this point, the timeout is first set to 60 seconds. The firewall
then expects a return packet to build the connection. When it sees
this return packet, the timeout is then set to 3600 seconds (60 minutes).
However, the firewall is not particular about what type of packet
comes back. I initiated a connection with SYN, then sent back an
ACK only, which the firewall happily accepted as part of that connection
(as long as the IPs and Ports matched up). So, the firewall does
not have the intelligence to expect SYN/ACK response, nor matching of sequence
numbers. Also, when building a connection, if you start a connection
with an ACK instead of a SYN packet, the timeout is automatically set to
3600 seconds, not 60 seconds. This has dangerous Denial of Service
implications.
One feature I liked was how the firewall treated SYN packets.
If you attempt to initialize a new session that emulates an existing one,
the firewall still compares it to the rulebase. For example, lets
say you attempt the following.
A --- FW --> B # System A connects to system B
Now, system B can send whatever packets it wants to system A, as long as the IPs and ports match up (ie, the packets are part of the session). However, if system B attempts to initialize a new connection (with the standard SYN), even if he uses the exact same ports of the existing session, the firewall still considers the SYN part of a new session and compares it to the rulebase. In my opinion, this is a good thing. In the example above, lets say the firewall allows ALL traffic from system A outbound, but no traffic from system B inbound. The only way system B can talk to system A is if it is part of a connection.
When system A connects to system B, the connection is added to the firewalls inspection table (see example above of inspection table). Now system B can respond by sending packets to system A. However, the firewall has NOT blown a whole wide open. System B cannot send any SYN packets to System A initiating another connection, even if the IPs and port numbers are the same. When the firewall sees that SYN packet, it applies the packet to the rulebase. In the above scenario, that packet would be dropped, even thought there is an established connection.
Something else I learned is if fastpath is enabled, then the session
is not added to a connections table, ie no connections table is built.
The reason for this is Fastpath only looks at the SYN packet, so there
is no need for a session to be added to the connections table. If
the packet has any other flag enabled, then the packet is not filtered
and is allowed through by default. Normally, fastpath is used to
improve performance (or in rare routing situations). The idea is,
if a packet does not have the SYN flag, then it must already be part of
an establish connection, as only a SYN packet can start a connection.
Since only SYN packets are inspected, peformance is greatly improved.
However, enabling fastpath is normally a bad move, as this opens you up
to a wide variety of attacks. Fastpath is in FW-1 ver 3.0 only and
is a globall property applied to all TCP packets. In ver 4.0, it
is called Fastmode, and can be selectively applied to different TCP services.
Closing a Connection
Based on some initial testing, it seems FW-1 closes connections by
timeing the connection out. When the inspection module sees a session
exchange a FIN or RST packet, it changes the timeout from 3600 seconds
to 50. If no other packets are exchanged in that 50 second period,
the connection is then removed from the state table. If any packets
are sent during the timeout period, the clock is reset to 50 seconds.
By continually sending packets after a session tear down, you can keep
resetting the clock to 50 seconds. This prevents Denial of Service
attacks if someone sends spoofed RST or FIN packets. This timeout
behavior can also be considered similar to the TIME_WAIT state a TCP connection
enters after acknowledging (ACK) the second FIN packet in closeing a session.
Conclusion
My initial impression, based on preliminary testing, is Check
Point FW-1's stateful inspection is semi intelligent, but only semi.
If the FW-1 receives a packet that is NOT part of the state table, that
packet is checked against the rulebase. If accepted, it is added to the
state table, where all subsequent packets are checked against (known exceptions
are Xmas, FIN, and RST packets). This is a good thing, as the firewall
has a robust state table that will maintain connections. What concerns
me is when you initiate a connection with an ACK packet, the timeout is
automatically set to 3600 seconds, regardless if a system responds or not.
This has dangerous Denial of Service potential. What I do like
is all SYN packets are checked against the rulebase, regardless if its
part of an existing session (this prevents 'tunneling' or 'piggybacking').
However, the inspection table does NOT keep state about sequence numbers,
nor SYN - SYN/ACK - ACK sequence. All it knows is that SYN is the
first packet of a session, and accepts anything afterwards (if the SYN
was accepted). As for closing connections, its methods seem rather
straight forward, similar to TCP's TIME_WAIT period. Hopefully, after
further testing and input from the firewall community, this whitepaper
can be a production document that answers many common questions concerning
what stateful inspection is, and how really stateful the tables are.
Further Testing
What I have presented was tested on Check Point FireWall-1, ver 4.0
SP3 on Solaris x86 2.6. The tools I used to read the state table
and create my own packets can be found below. I would like to do
further testing to understand how the firewall interprets the 'Type' and
'Flags' columns in the state connections table. Also, how the Firewall
'drops' a connection. I am looking for anyone to validate (or invalidate)
what I have presented here. Also, any additional information would
be greatly appreciated.
Downloads:
fwtable.pl
will help you better understand the stateful inspection tables for your
firewalls (only works on Check Point FW-1). The script can be ran
locally on any Firewall Module, remotely from any Management Station, or
standalone on any system that has PERL.
lego.pl allows you to build your own TCP packets, including flags, ports, sequence numbers, etc. There is no command line interface, you have to edit the code, but it is brain dead simple. Written by miff.
libnet for you hardcore C coding types.
Author's bio
Lance Spitzner enjoys learning by blowing up his Unix systems at
home. Before this, he was an Officer
in the Rapid Deployment Force, where he blew up things of a different
nature. You can reach him at lance@spitzner.net
.
Whitepapers / Publications |