重建天堂之門:從 32 位元地獄一路打回天堂聖地(下)攻擊篇:x96 Shellcode、天堂聖杯 & 天堂注入器
WOW64逆向工程
在前一篇部落格文 重建天堂之門:從 32 位元地獄一路打回天堂聖地(上)深度逆向工程 WOW64 設計 筆者已經以逆向工程角度完整解釋了微軟這套幫助 32-bit 在原生 64-bit Windows 執行之架構的設計
若讀者尚未有研究過天堂之門相關基礎、建議先回到上一篇文通讀後再回來本文閱讀。而接下來我們會把焦點圍繞在接續著如何將上一篇的背景知識組合出一些有趣的利用手法 :)
x96 Shellcode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; x96 shellcode (x32+x64) by aaaddress1@chroot.org | |
; yasm -f bin -o x96shell_msgbox x96shell_msgbox.asm | |
section .text | |
bits 32 | |
_main: | |
call entry | |
entry: | |
mov ax, cs | |
sub ax, 0x23 | |
jz retTo32b | |
nop | |
retTo64b: | |
add dword [esp], b64_shellcode-entry | |
ret | |
retTo32b: | |
add dword [esp], b32_shellcode-entry | |
ret | |
; 64 bit shellcode - FatalAppExitA(0, "64bit Hello!") | |
b64_shellcode: | |
db 0xE9, 0x2B, 0x01, 0x00, 0x00, 0x90, 0x4C, 0x8D, 0x41, 0x02, 0x31, 0xC0, 0x66, 0x83, 0x39, 0x00, 0x74, 0x1E, 0x41, 0x0F, 0xB7, 0x08, 0x49, 0x83, 0xC0, 0x02, 0x89, 0xCA, 0x83, 0xCA, 0x20, 0x0F, 0xB7, 0xD2, 0x01, 0xD0, 0xC1, 0xC8, 0x08, 0x66, 0x85, 0xC9, 0x75, 0xE6, 0xC3, 0x0F, 0x1F, 0x00, 0xC3, 0x4C, 0x8D, 0x41, 0x01, 0x31, 0xC0, 0x80, 0x39, 0x00, 0x74, 0x24, 0x0F, 0x1F, 0x40, 0x00, 0x41, 0x0F, 0xB6, 0x08, 0x49, 0x83, 0xC0, 0x01, 0x89, 0xCA, 0x83, 0xCA, 0x20, 0x0F, 0xBE, 0xD2, 0x01, 0xD0, 0xC1, 0xC8, 0x08, 0x84, 0xC9, 0x75, 0xE7, 0xC3, 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00, 0xC3, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x40, 0x18, 0x4C, 0x8B, 0x48, 0x20, 0x4C, 0x8D, 0x50, 0x20, 0x4D, 0x39, 0xD1, 0x74, 0x2F, 0x48, 0x83, 0xEC, 0x28, 0x41, 0x89, 0xCB, 0xEB, 0x08, 0x4D, 0x8B, 0x09, 0x4D, 0x39, 0xD1, 0x74, 0x17, 0x49, 0x8B, 0x49, 0x50, 0xE8, 0x71, 0xFF, 0xFF, 0xFF, 0x44, 0x39, 0xD8, 0x75, 0xEA, 0x49, 0x8B, 0x41, 0x20, 0x48, 0x83, 0xC4, 0x28, 0xC3, 0x31, 0xC0, 0x48, 0x83, 0xC4, 0x28, 0xC3, 0x31, 0xC0, 0xC3, 0x57, 0x56, 0x53, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x63, 0x41, 0x3C, 0x8B, 0xB4, 0x01, 0x88, 0x00, 0x00, 0x00, 0x85, 0xF6, 0x74, 0x42, 0x48, 0x01, 0xCE, 0x8B, 0x46, 0x18, 0x85, 0xC0, 0x74, 0x38, 0x44, 0x8B, 0x4E, 0x20, 0x89, 0xD7, 0x49, 0x89, 0xCB, 0x45, 0x31, 0xD2, 0x8D, 0x58, 0xFF, 0x49, 0x01, 0xC9, 0xEB, 0x03, 0x4D, 0x89, 0xC2, 0x4D, 0x85, 0xC9, 0x74, 0x0F, 0x41, 0x8B, 0x09, 0x4C, 0x01, 0xD9, 0xE8, 0x3D, 0xFF, 0xFF, 0xFF, 0x39, 0xF8, 0x74, 0x18, 0x4D, 0x8D, 0x42, 0x01, 0x49, 0x83, 0xC1, 0x04, 0x4C, 0x39, 0xD3, 0x75, 0xDC, 0x48, 0x83, 0xC4, 0x20, 0x31, 0xC0, 0x5B, 0x5E, 0x5F, 0xC3, 0x90, 0x8B, 0x46, 0x24, 0x4B, 0x8D, 0x14, 0x53, 0x0F, 0xB7, 0x14, 0x02, 0x8B, 0x46, 0x1C, 0x49, 0x8D, 0x14, 0x93, 0x8B, 0x04, 0x02, 0x48, 0x83, 0xC4, 0x20, 0x5B, 0x5E, 0x5F, 0x4C, 0x01, 0xD8, 0xC3, 0x48, 0xB8, 0x46, 0x61, 0x74, 0x61, 0x6C, 0x41, 0x70, 0x70, 0x57, 0x56, 0x53, 0x48, 0x83, 0xEC, 0x40, 0x48, 0x89, 0x44, 0x24, 0x32, 0x48, 0x8D, 0x4C, 0x24, 0x32, 0xB8, 0x41, 0x00, 0x00, 0x00, 0xC7, 0x44, 0x24, 0x3A, 0x45, 0x78, 0x69, 0x74, 0x66, 0x89, 0x44, 0x24, 0x3E, 0xE8, 0xCF, 0xFE, 0xFF, 0xFF, 0x89, 0xC7, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x40, 0x18, 0x48, 0x8B, 0x58, 0x20, 0x48, 0x8D, 0x70, 0x20, 0x48, 0x39, 0xDE, 0x75, 0x0A, 0xEB, 0x45, 0x48, 0x8B, 0x1B, 0x48, 0x39, 0xDE, 0x74, 0x10, 0x48, 0x8B, 0x4B, 0x20, 0x89, 0xFA, 0xE8, 0x1A, 0xFF, 0xFF, 0xFF, 0x48, 0x85, 0xC0, 0x74, 0xE8, 0xC7, 0x44, 0x24, 0x2D, 0x6C, 0x6C, 0x6F, 0x21, 0x48, 0x8D, 0x54, 0x24, 0x25, 0x31, 0xC9, 0x48, 0xBF, 0x36, 0x34, 0x62, 0x69, 0x74, 0x20, 0x48, 0x65, 0x48, 0x89, 0x7C, 0x24, 0x25, 0xC6, 0x44, 0x24, 0x31, 0x00, 0xFF, 0xD0, 0x48, 0x83, 0xC4, 0x40, 0x5B, 0x5E, 0x5F, 0xC3, 0x31, 0xC0, 0xEB, 0xCF, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 | |
; 32 bit shellcode - FatalAppExitA(0, "32bit Hello!") | |
b32_shellcode: | |
db 0xE9, 0x1E, 0x01, 0x00, 0x00, 0x90, 0x66, 0x83, 0x39, 0x00, 0x74, 0x24, 0x53, 0x31, 0xC0, 0x8D, 0x59, 0x02, 0x0F, 0xB7, 0x0B, 0x83, 0xC3, 0x02, 0x89, 0xCA, 0x83, 0xCA, 0x20, 0x0F, 0xB7, 0xD2, 0x01, 0xD0, 0xC1, 0xC8, 0x08, 0x66, 0x85, 0xC9, 0x75, 0xE8, 0x5B, 0xC3, 0x8D, 0x74, 0x26, 0x00, 0x31, 0xC0, 0xC3, 0x80, 0x39, 0x00, 0x74, 0x28, 0x53, 0x31, 0xC0, 0x8D, 0x59, 0x01, 0x66, 0x90, 0x0F, 0xB6, 0x0B, 0x83, 0xC3, 0x01, 0x89, 0xCA, 0x83, 0xCA, 0x20, 0x0F, 0xBE, 0xD2, 0x01, 0xD0, 0xC1, 0xC8, 0x08, 0x84, 0xC9, 0x75, 0xE9, 0x5B, 0xC3, 0x8D, 0xB4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0xC3, 0x57, 0x56, 0x53, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B, 0x40, 0x0C, 0x8B, 0x58, 0x14, 0x8D, 0x70, 0x14, 0x39, 0xF3, 0x74, 0x27, 0x89, 0xCF, 0xEB, 0x09, 0x8D, 0x76, 0x00, 0x8B, 0x1B, 0x39, 0xF3, 0x74, 0x1A, 0x8B, 0x4B, 0x28, 0xE8, 0x78, 0xFF, 0xFF, 0xFF, 0x39, 0xF8, 0x75, 0xEE, 0x8B, 0x43, 0x10, 0x5B, 0x5E, 0x5F, 0xC3, 0x8D, 0xB4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x31, 0xC0, 0x5E, 0x5F, 0xC3, 0x8B, 0x41, 0x3C, 0x8B, 0x44, 0x01, 0x78, 0x85, 0xC0, 0x74, 0x6F, 0x55, 0x01, 0xC8, 0x57, 0x56, 0x53, 0x83, 0xEC, 0x08, 0x8B, 0x78, 0x18, 0x89, 0x44, 0x24, 0x04, 0x85, 0xFF, 0x74, 0x28, 0x8B, 0x58, 0x20, 0x89, 0x14, 0x24, 0x89, 0xCE, 0x31, 0xED, 0x01, 0xCB, 0x85, 0xDB, 0x74, 0x0E, 0x8B, 0x0B, 0x01, 0xF1, 0xE8, 0x55, 0xFF, 0xFF, 0xFF, 0x3B, 0x04, 0x24, 0x74, 0x1D, 0x83, 0xC5, 0x01, 0x83, 0xC3, 0x04, 0x39, 0xEF, 0x75, 0xE4, 0x83, 0xC4, 0x08, 0x31, 0xC0, 0x5B, 0x5E, 0x5F, 0x5D, 0xC3, 0x89, 0xF6, 0x8D, 0xBC, 0x27, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x7C, 0x24, 0x04, 0x8D, 0x04, 0x6E, 0x03, 0x47, 0x24, 0x0F, 0xB7, 0x00, 0x8D, 0x04, 0x86, 0x03, 0x47, 0x1C, 0x03, 0x30, 0x83, 0xC4, 0x08, 0x5B, 0x89, 0xF0, 0x5E, 0x5F, 0x5D, 0xC3, 0x90, 0x31, 0xC0, 0xC3, 0x57, 0xB8, 0x41, 0x00, 0x00, 0x00, 0x56, 0x53, 0x83, 0xEC, 0x30, 0x8D, 0x4C, 0x24, 0x22, 0xC7, 0x44, 0x24, 0x22, 0x46, 0x61, 0x74, 0x61, 0xC7, 0x44, 0x24, 0x26, 0x6C, 0x41, 0x70, 0x70, 0xC7, 0x44, 0x24, 0x2A, 0x45, 0x78, 0x69, 0x74, 0x66, 0x89, 0x44, 0x24, 0x2E, 0xE8, 0xDF, 0xFE, 0xFF, 0xFF, 0x89, 0xC7, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B, 0x40, 0x0C, 0x8B, 0x58, 0x14, 0x8D, 0x70, 0x14, 0x39, 0xDE, 0x75, 0x0D, 0xEB, 0x55, 0x90, 0x8D, 0x74, 0x26, 0x00, 0x8B, 0x1B, 0x39, 0xDE, 0x74, 0x0E, 0x8B, 0x4B, 0x10, 0x89, 0xFA, 0xE8, 0x26, 0xFF, 0xFF, 0xFF, 0x85, 0xC0, 0x74, 0xEC, 0x8D, 0x54, 0x24, 0x15, 0xC7, 0x44, 0x24, 0x15, 0x33, 0x32, 0x62, 0x69, 0xC7, 0x44, 0x24, 0x19, 0x74, 0x20, 0x48, 0x65, 0xC7, 0x44, 0x24, 0x1D, 0x6C, 0x6C, 0x6F, 0x21, 0xC6, 0x44, 0x24, 0x21, 0x00, 0x89, 0x54, 0x24, 0x04, 0xC7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xD0, 0x83, 0xEC, 0x08, 0x83, 0xC4, 0x30, 0x5B, 0x5E, 0x5F, 0xC3, 0x8D, 0x74, 0x26, 0x00, 0x31, 0xC0, 0xEB, 0xC0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 | |
這是我覺得最有趣的一個玩法XD
通過逆向工程 WOW64 架構後,發現了通過 cs segment 區段暫存器值 23h 或 33h、得以確認當下 Intel 晶片正在將 program counter 上的程式碼以 32-bit 或 64-bit 指令集解析。
因此藉由此區段暫存器值,我們也能創建出一個 x96 shellcode、亦即同時可當作 32-bit 也可當作 64-bit 的 shellcode!設計概念相當簡單,便是在 shellcode 開頭處設計一個彈頭:判斷當前 cs segment 值若為 23h 則跳去 32-bit shellcode 執行、反之是 33h 則跳去 64-bit shellcode 接續著執行。
講得簡單,設計起來實務麻煩點其實是確保 x96 彈頭 payload 在 32/64-bit 下解析起來的邏輯是一致的,比方 shellcode 相對偏移定址問題、32/64-bit 的指令 alignment 不對稱,或者連 branch jump 指令 offset 在兩指令集上算法超過 2 bytes 也會有問題 等。
所以在上面 PoC 中可以看到,彈頭中所有的指令 opcode 都特別挑選過是在兩種架構上皆相同的、才可確保在兩指令集上邏輯按照預期執行,舉例:
- call $+5 同樣是 \xE8\x00\x00\x00\x00 其在 64-bit 下會以 8 bytes 長度推入 return address 上堆疊,而在 32-bit 下僅推入 4 bytes 長度上堆疊
- \x81\x04\x24 其在 32-bit 下被視作 add ds: [esp], xxxx。而在 64-bit 下會被自動擴展為 add ds: [rsp], xxxx
而挑選使用 call $+5; add ds:[esp]; ret 的做法是利用了無論 32/64-bit 推入 4/8 bytes 地址上堆疊的記憶體、其低 4bytes 必定都會恰巧為 offset 保存點,從而解決跨指令集的定址問題
"the hell" & Heaven's Gate
而早在 2011 年由 Rewolf 發布的 Mixing x86 with x64 code by ReWolf 技巧中就提及了天堂之門的技巧,如圖上所示的攻擊流程。在上一篇部落格裡,我們提及了 WOW64 Process 中都勢必存有 32/64-bit ntdll.dll 兩個已掛載的 DLL 模組,而在配有防毒主動防禦的環境 32-bit ntdll.dll 模組之導出函數會被安裝上熱補丁、而 64-bit ntdll.dll 卻未埋有任何監控。
因此對駭客而言只要能在 32-bit 的 WOW64 Process 通過 shellcode 方法對記憶體進行鑑識並攀爬到 64-bit ntdll.dll 上想使用的 NTAPI 地址,接著對 cs segment 切換到 64-bit 模式並呼叫該地址、達成繞過主動防禦策略的進行惡意攻擊。
看到這邊讀者應該會有個困惑,咦那為什麼不連 64-bit ntdll.dll 都上熱補丁牢牢地監控呢?關於這問題早在 2012 年就有大佬做出過嘗試,可以參考文獻 Knockin’ on Heaven’s Gate by george_nicolaou 嘗試設計一個 DLL 注入器在 32-bit Process 中掛載 64-bit DLL 模組。在這先做個簡略的解釋:主因是在 Windows 體系運作中 64-bit 的系統函數 LdrLoadDll 是不允許在 WOW64 Process 記憶體中掛載 64-bit DLL 模組的。
在32位下讀取64位記憶體資料
剛剛提及了天堂之門攻擊技巧的核心是:在 32-bit 模式下讀取 WOW64 Process 中 64-bit NTAPI 地址。不過由於32-bit 的指令在進行指針操作本來就不可能 讀/寫 4Gb 以上的記憶體資料,在這之前會遇到一個最大的問題是定址問題。
解決方法很明顯的——那麼我們就通過覆寫 cs segment 暫存器值,先將當前晶片指令集切為 64-bit 不就能夠以原生 64-bit 指令對 uint64_t 地址上的記憶體資料進行操作嗎?答案是肯定的!
天堂聖杯 wowGrail
終於能提到本次發表於年會 HITB(Hack In The Box Security Conference)《WoW Hell: Rebuilding Heavens Gate》 的 PoC 天堂聖杯 github.com/aaaddress1/wowGrail :)
而在前一篇文中我們提過僅須以 x64 Calling Convention 的方式呼叫翻譯機函數、並欲呼叫 32-bit NTAPI 函數辨識碼 與 32-bit 參數陣列 傳入進去,便能夠通過 WOW64 層進行翻譯、直接呼叫到 64-bit ntdll.dll 對應的系統函數,從而繞過防毒廠商設於 32-bit 上的監控了。
在天堂聖杯 github.com/aaaddress1/wowGrail 專案以最經典的惡意攻擊手法 Process Hollowing,並將所有使用到的 Win32 API 轉以使用上述惡意利用 WOW64 翻譯機的技巧、得以成功繞過知名且眾多用戶熱愛的歐美廠牌防毒,達成將皮卡球打排球偽造為小算盤程式作為演示。
天堂注入器
參上圖為上一篇部落格文中提過,32-bit 的 Thread 在進入 WOW64 層時會將暫存器狀態建立一份快照、並於離開時從中提取 return address 並跳躍,從而恢復至上一次 32-bit 程式運作的狀態。
而這一份快照的保存指針會被保存在 64-bit 暫存器 r13 中——此地址是在 WOW64 Process 初始化時,通過 64-bit TEB 結構 +1488h offset 處。這意味著:只要我們能夠洩漏他人 WOW64 Process 中的 TEB64 地址、便能夠預測對方 32-bit Thread 快照保存的位址!
而在 KERNEL: Creation of Thread Environment Block (TEB) by waleedassar 與 WoW64 and So Can You 兩份文獻中都提及了在 Kernel 層對 WOW64 Process 記憶體分配有以下特別的處理:
- 由於 32-bit 與 64-bit 呼叫系統函數需要獨立環境處理的特性,因此每個 WOW64 Process 中都勢必會同時存在四塊環境結構塊 TEB64、TEB32、PEB64 與 PEB32(列出的順序是按照記憶體由低到高排列)
- 在產生 WOW64 Process 時,會呼叫到 nt!MiCreatePebOrTeb 函數其內部會替 WOW64 先切 0x3000 bytes 空間用於緊密的擺放並排列上述四塊結構,並且可以在 TEB32 + F70h offset 上找到 TEB64 的地址
- 由於其四結構塊緊密排列的特性,因此四個結構塊彼此地址之偏移量是固定的。只要能知道其中一塊的地址、那麼其他三塊的地址都可以洩漏出來了,比方 TEB64 = TEB32 - 0x2000。
上圖取至天堂注入器 github.com/aaaddress1/wowInjector 的部分程式碼片段,而這邊就可以提到 Process Hollowing 中的使用到的特性:無論 32/64-bit Process 的第一個 Thread 其工作必定會是呼叫 ntdll!LdrInitializeThunk 進行程式裝載器任務、並且暫存器 Ebx/Rbx 必定會指向到 PEB32/PEB64 的地址。
因此藉著這個特性,便能夠洩漏其餘三塊的地址、近一步提取 TEB64 地址與 +1488h 上的 Thread 快照的保存位址。如上圖所示在洩露快照地址成功後,僅需以一行 WriteProcessMemory 修改其返回地址、即可達成控制執行流程。
而這項技巧將其命名為天堂注入器、完整原始碼可以參考我的開源專案 github.com/aaaddress1/wowInjector。此技巧在年會 HITB(Hack In The Box Security Conference)《WoW Hell: Rebuilding Heavens Gate》 的演講中,同樣實測了知名的歐美防毒得以成功繞過其主動防禦、進行經典的 Process Hollowing 攻擊。
留言
張貼留言