2005年的时候Nicolas Waisman就在他的《Owning the windows Heap》中提到了一个0day技巧,就是在堆溢出中覆盖HeapCommitRoutine的指针来获取程序的控制权限。
在Windows 2000中,我们还可以沿用覆盖PEB中lock/unlock的指针的方法,但是再XP SP2/2003中,PEB随机化,TOP SEH等关键指针都已经被encode了,所以只好找各个.dll的.data中的指针,但是这些指针地址往往和系统版本,语言版本,SP版本相关。
而HeapCommitRoutine指针存在于默认进程堆偏移0x57c处,而且默认进程堆的地址是比较固定的(Vista ALSR 除外)。
0:009> dt ntdll!_HEAP 0x150000
+0x000 Entry : _HEAP_ENTRY
+0x008 Signature : 0xeeffeeff
+0x00c Flags : 2
+0x010 ForceFlags : 0
+0x014 VirtualMemoryThreshold : 0xfe00
+0x018 SegmentReserve : 0x100000
+0x01c SegmentCommit : 0x2000
+0x020 DeCommitFreeBlockThreshold : 0x200
+0x024 DeCommitTotalFreeThreshold : 0x2000
+0x028 TotalFreeSize : 0x1b5
+0x02c MaximumAllocationSize : 0x7ffdefff
+0x030 ProcessHeapsListIndex : 1
+0x032 HeaderValidateLength : 0x608
+0x034 HeaderValidateCopy : (null)
+0x038 NextAvailableTagIndex : 0
+0x03a MaximumTagIndex : 0
+0x03c TagEntries : (null)
+0x040 UCRSegments : (null)
+0x044 UnusedUnCommittedRanges : 0x001505a8 _HEAP_UNCOMMMTTED_RANGE
+0x048 AlignRound : 0xf
+0x04c AlignMask : 0xfffffff8
+0x050 VirtualAllocdBlocks : _LIST_ENTRY [ 0x150050 - 0x150050 ]
+0x058 Segments : [64] 0x00150640 _HEAP_SEGMENT
+0x158 u : __unnamed
+0x168 u2 : __unnamed
+0x16a AllocatorBackTraceIndex : 0
+0x16c NonDedicatedListLength : 1
+0x170 LargeBlocksIndex : (null)
+0x174 PseudoTagEntries : (null)
+0x178 FreeLists : [128] _LIST_ENTRY [ 0x1da3c0 - 0x1da3c0 ]
+0x578 LockVariable : 0x00150608 _HEAP_LOCK
+0x57c CommitRoutine : (null)
+0x580 FrontEndHeap : 0x00150688
+0x584 FrontHeapLockCount : 0
+0x586 FrontEndHeapType : 0x1 ''
+0x587 LastSegmentIndex : 0 ''
这个指针会在Heap extend的时候被调用,调用该指针的函数为RtlpFindAndCommitPages(),如下所示:
0:008> bl
0 e 7c833bf9 0001 (0001) 0:**** ntdll!RtlpFindAndCommitPages+0x51
0:008> g
Breakpoint 0 hit
eax=00150000 ebx=0201e5e0 ecx=0201e5e0 edx=00002000 esi=00150598 edi=00150640
eip=7c833bf9 esp=0201e5a0 ebp=0201e5b4 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!RtlpFindAndCommitPages+0x51:
7c833bf9 8b887c050000 mov ecx,dword ptr [eax+57Ch] ds:0023:0015057c=00000000
0:009> r ecx=0x55705570 // 设置HeapCommitRoutine的指针为0x55705570 :)
0:009> r
eax=00150000 ebx=0201e5e0 ecx=55705570 edx=00002000 esi=00150598 edi=00150640
eip=7c833bff esp=0201e5a0 ebp=0201e5b4 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!RtlpFindAndCommitPages+0x57:
7c833bff 85c9 test ecx,ecx
0:009> t
eax=00150000 ebx=0201e5e0 ecx=55705570 edx=00002000 esi=00150598 edi=00150640
eip=7c833c01 esp=0201e5a0 ebp=0201e5b4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
ntdll!RtlpFindAndCommitPages+0x59:
7c833c01 0f8597f00100 jne ntdll!RtlpFindAndCommitPages+0x5b (7c852c9e) [br=1] // 如果该指针不为NULL,跳转
0:009> t
eax=00150000 ebx=0201e5e0 ecx=55705570 edx=00002000 esi=00150598 edi=00150640
eip=7c852c9e esp=0201e5a0 ebp=0201e5b4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
ntdll!RtlpFindAndCommitPages+0x5b:
7c852c9e 53 push ebx
0:009> u eip L 10
ntdll!RtlpFindAndCommitPages+0x5b:
7c852c9e 53 push ebx
7c852c9f 8d5514 lea edx,[ebp+14h]
7c852ca2 52 push edx
7c852ca3 50 push eax
7c852ca4 ffd1 call ecx // 执行指针的地方
0:009> g
(a64.fb0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00150000 ebx=0201e5e0 ecx=55705570 edx=0201e5c8 esi=00150598 edi=00150640
eip=55705570 esp=0201e590 ebp=0201e5b4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
55705570 ??
Vista下这个指针也被encode了,但是还是可以利用的,有兴趣的可以参考Ben Hawkes的《Attacking The Vista Heap》,或者下次有空的时候讲Vista下覆盖Heap handle的时候再讨论。