COMMAND
Firewall-1 (ICMP handling)
SYSTEMS AFFECTED
I guess most systems
PROBLEM
Bill Burns and his team found following during some routine
testing of their Firewall-1 firewall. They uncovered an
"inconsistency" in how Firewall-1 handles ICMP traffic. Note that
there is no known exploit for this, but may be.
His team was tasked with the goal of finding a way to securely let
internal users use VitalSigns' "NetMedic" product work through
their main firewall. NetMedic is a clever application that helps
analyze Internet / Web performance and help to identify
bottlenecks along the entire chain; from PC to webserver. One of
the more interesting statistics it provides is the "hop count" to
the web server you're browsing". It does this in real-time, so
it's a very useful diagnostic tool.
Test Setup:
-----------
A test firewall was built and representative machines were placed
on either side of the firewall to accurately portray what their
production environment looks like:
- Checkpoint Firewall-1 v3.0a running on an Ultra Enterprise 2
with 128M RAM with Solaris 2.5.1+patches
- a default ruleset of "deny all", including all the
"checkbox" protocols like RealAudio, VDOlive, ICMP
- no NAT was used (more on this)
The "external" side of the firewall was connected to an ISDN
connection back to the main office so that the PC could hit real
web servers. The "internal" side of the firewall had only the PC
on it.
A PC with Windows95 running NetMedic 1.2 and Netscape Communicator
as the web browser (NetMedic will work with Microsoft's IE as well
as Netscape Communicator)
Both the Solaris "snoop" utility as well as a Macintosh Ethernet
sniffer (EtherPeek) were used to analyze traffic on each side of
the firewall. EtherPeek was also used to generate traffic from
the external network (more on this later).
How Microsoft tracert works: (example assumes the target is > 1
hop away):
Source machine sends an ICMP type 8 packet ("ping") with a TTL
value of 1. Because this ping packet will only live through
one router, the first router responds to the source machine
with ICMP type 11 (TTL exceeded). The source machine then
increments the TTL value by 1 and issues another ICMP type 8
packet ("ping"). The second router to the path of the target
machine responds with an ICMP type 11 (TTL exceeded) packet.
This continues until the TTL values are high enough to reach
the target machine; in this case the ping packet is replied to
by the target machine with an ICMP type 0 ("ping response")
packet.
What Bill and his team found:
-----------------------------
Since they knew that Microsoft's trace routing utility (called
tracert) used ICMP exclusively to calculate hop counts, they knew
to support NetMedic's hop counting abilities the only protocol
needed was limited to "ICMP". Since tracert "looked" a lot like
ping, they thought they would start testing by just letting ping
through the firewall.
Their initial assumption on how to accomplish this goal:
- Firewall-1's stateful inspection
- combined with checking the checkbox "allow ICMP"
- specifying that this ICMP rule be checked "before the last
rule"
- and that a simple rule could be built into the GUI to allow
"ping traffic" (there's an example in the book)
What they noticed was that stateful inspection did not cover ICMP.
It was perfectly clear by reading an example in the book on how
to allow ping through the firewall, but was not clear was if it
was "stateful" or not. By reading the INSPECT code in
$FWDIR/lib/base.def it became apparent that ICMP was not
statefully inspected.
This meant that if you allow echo-request packets to go out, you
expect echo-reply packets to come back in. But if it didn't send
echo-request packets out, the firewall could still be allowing
echo-reply packets back in. And if you weren't very, very
careful other kinds of ICMP packets could be allowed in. (To be
fair, the "allow ICMP" checkbox does NOT allow the dangerous
"ICMP redirect" packets through -- this is explicitly stated in
the INSPECT code.)
This hypotheses was tested in real-life by setting up a suitable
ruleset in FW-1 and watch the PC (internal network) ping the Mac
(external network). When stopped the PC pinging, you are STILL
able to shoot the captured echo-reply packets back to the PC. In
fact you could send multiplied echo-reply packets back through
the firewall DURING ping sessions. And even if you mangled the
echo-reply packets (i.e. make them the wrong sequence or ID
number or change the destination address) you are still able to
send them through the firewall. (Mind you, "ping" packets
didn't pass, just the ping-reply packets.)
Guess os that the fact that you could not guarantee which ICMP
packets would be passed by your firewall concerned you greatly.
Thus packets could be sent by an outsider without first being
requested from an insider.
SOLUTION
Bill team wrote their own stateful inspection INSPECT code to
handle ping and tracert. Btw, note that Checkpoint ensure that
this does not appear to be a flaw in Firewall-1.
What their code does:
---------------------
This version of the INSPECT code just handles the ability to
"ping" statefully. You've got to walk before you can run. There
have been three major improvements in the code so far so that it
can handle NetMedic and UNIX traceroute as well. But this is
simple enough that you'll get the point.
The code watches for outgoing ping packets and records the
following information:
- source and destination addresses
- the ICMP sequence number (useful to figure how which packets
get dropped)
- the ICMP ID number (which process on the source machine a
packet belongs to)
- allows a maximum of 5 seconds for this half-open state to
exist without an echo-reply
With the code in place external machines will be unable to send
unexpected echo-reply packets back through the firewall. Mangled
packets (with incorrect ID or sequence numbers) will be also
dropped. In both cases, unexpected echo reply packets also
generated a log entry.
To be able to track ICMP traffic you need to "roll your own"
INSPECT code.
This version of the code doesn't include the code snippet from
Checkpoint's web page about defeating the "ping of death" packets.
It was incorporated in subsequent releases.
If anyone sees any glaring errors or has any other comments about
this, send mail to shadow@netscape.com. Here's the INSPECT code:
//
// Stateful ping/tracert v1.2 William D. Burns (shadow@netscape.com)
//
//PROPRIETARY SOURCE CODE OF NETSCAPE COMMUNICATIONS CORPORATION
//Copyright =A9 199x Netscape Communications Corporation. All Rights
Reserved.
//
// This is where the ICMP sequence number is, used for
// tracking session-like information with ping,tracert
#define icmp_ip_seq [ 24 : 2, b ]
#define PING_Timeout 5
ping_table = dynamic {} expires PING_Timeout;
//
// PING handling section
//
// Accept ping packets going to Internet
// ping replies coming from Internet to initiator
// Reject ping replies not anticipated
//
// What a proper echo-request packet looks like
//
#define is_ping_request ( \
icmp, icmp_type=ICMP_ECHO \
)
//
// What a proper echo-reply packet looks like
//
#define is_ping_reply ( \
icmp, icmp_type=ICMP_ECHOREPLY \
)
// if it looks like an echo packet,
// record the session and allow the packet
//
#define ping_request_accept ( \
is_ping_request, \
record <src,dst,icmp_ip_id; icmp_ip_seq> in ping_table \
)
// if we get a echo_reply without an echo,
// or the reply is not the right sequence number
// that is a problem so drop it
// (icmp_long is predefined in formats.def)
//
// Note: the delete looks like an error, but this
// is the way is works. Checking with the
// vendor to see if this syntax is what they
// intended.
//
#define ping_reply_intercept ( \
is_ping_reply, \
accept ( \
((ping_table [dst,src,icmp_ip_id] = icmp_ip_seq), \
delete <dst,src,icmp_ip_id> from ping_table) \
or (log icmp_long, drop) \
) \
)
//This is what you you put in the prologue section
// and what gets executed on a match for a ping
//
#define ping_code { \
ping_request_accept; \
ping_reply_intercept; \
}