標籤:tac das 而且 lan src png lang 允許 環境
緩衝區溢位原理
顧名思義,緩衝區溢位的含義是為緩衝區提供了多於其儲存容量的資料,就像往杯子裡倒入了過量的水一樣。通常情況下,緩衝區溢位的資料只會破壞程式資料,造成意外終止。但是如果有人精心構造溢出資料的內容,那麼就有可能獲得系統的控制權!如果說使用者(也可能是駭客)提供了水——緩衝區溢位攻擊的資料,那麼系統提供了溢出的容器——緩衝區。
緩衝區在系統中的表現形式是多樣的,進階語言定義的變數、數組、結構體等在運行時可以說都是儲存在緩衝區內的,因此所謂緩衝區可以更抽象地理解為一段可讀寫的記憶體地區,緩衝區攻擊的最終目的就是希望系統能執行這塊可讀寫記憶體中已經被蓄意設定好的惡意代碼。按照馮·諾依曼儲存程式原理,程式碼是作為位元據儲存在記憶體的,同樣程式的資料也在記憶體中,因此直接從記憶體的二進位形式上是無法區分哪些是資料哪些是代碼的,這也為緩衝區溢位攻擊提供了可能。
進程地址空間分布
是進程地址空間分布的簡單表示。代碼儲存了使用者程式的所有可執行代碼,在程式正常執行的情況下,程式計數器(PC指標)只會在程式碼片段和作業系統地址空間(核心態)內定址。資料區段記憶體儲了使用者程式的全域變數,文字池等。棧空間儲存了使用者程式的函數棧幀(包括參數、局部資料等),實現函數調用機制,它的資料增長方向是低地址方向。堆空間儲存了程式運行時動態申請的記憶體資料等,資料增長方向是高地址方向。除了程式碼片段和受作業系統保護的資料區域,其他的記憶體地區都可能作為緩衝區,因此緩衝區溢位的位置可能在資料區段,也可能在堆、棧段。如果程式的代碼有軟體漏洞,惡意程式會“教唆”程式計數器從上述緩衝區內取指,執行惡意程式提供的資料代碼!
棧的主要功能是實現函數的調用。每次函數調用時,系統會把函數的返回地址(函數調用指令後緊跟指令的地址),一些關鍵的寄存器值儲存在棧內,函數的實際參數和局部變數(包括資料、結構體、對象等)也會儲存在棧內。這些資料統稱為函數調用的棧幀,而且是每次函數調用都會有個獨立的棧幀,這也為遞迴函式的實現提供了可能。當調用一個函數時,需要用ebp儲存函數棧幀基址,因此先儲存ebp原來的值到棧內,然後將棧指標esp內容儲存到ebp。函數返回前需要做相反的操作——將esp指標恢複,並彈出ebp。這樣,函數內正常情況下無論怎樣使用棧,都不會使棧失去平衡。之所以會有緩衝區溢位的可能,主要是因為棧空間內儲存了函數的返回地址。該地址儲存了函數調用結束後後續執行的指令的位置,對於電腦安全來說,該資訊是很敏感的。如果有人惡意修改了這個返回地址,並使該返回地址指向了一個新的代碼位置,程式便能從其它位置繼續執行。
實驗準備
實驗樓提供的是64位Ubuntu linux,而本次實驗為了方便觀察彙編語句,我們需要在32位環境下作操作,因此實驗之前需要做一些準備。安裝一些用於編譯32位C程式的東西:
安裝完成後,輸入“/bin/bash”使用bash:
實驗步驟初始設定
Ubuntu和其他一些Linux系統中,使用地址空間隨機化來隨機堆(heap)和棧(stack)的初始地址,這使得猜測準確的記憶體位址變得十分困難,而猜測記憶體位址是緩衝區溢位攻擊的關鍵。因此本次實驗中,我們使用以下命令關閉這一功能。
此外,為了進一步防範緩衝區溢位攻擊及其它利用shell程式的攻擊,許多shell程式在被調用時自動放棄它們的特權。因此,即使你能欺騙一個Set-UID程式調用一個shell,也不能在這個shell中保持root許可權,這個防護措施在/bin/bash中實現。
linux系統中,/bin/sh實際是指向/bin/bash或/bin/dash的一個符號連結。為了重現這一防護措施被實現之前的情形,我們使用另一個shell程式(zsh)代替/bin/bash。下面的指令描述了如何設定zsh程式:
shellcode
在esp後繼續追加shellcode代碼會將上級函數的棧幀淹沒,這樣做並沒有什麼好處,甚至可能會帶來運行時問題。既然被溢出的函數棧幀內提供了緩衝區,我們還是把核心的shellcode放在緩衝區內,而在esp之後放上跳轉指令轉移到原本的緩衝區位置。由於這樣做使代碼的位置在esp指標之前,如果shellcode中使用了push指令便會讓esp指令與shellcode代碼越來越近,甚至淹沒自身的代碼。這顯然不是我們想要的結果,因此我們可以強制抬高esp指標,使它在shellcode之前(低地址位置),這樣就能在shellcode內正常使用push指令了。
漏洞程式
通過代碼可以知道,程式會讀取一個名為“badfile”的檔案,並將檔案內容裝入“buffer”。
編譯該程式,並設定SET-UID。命令如下:
GCC編譯器有一種棧保護機制來阻止緩衝區溢位,所以我們在編譯代碼時需要用 –fno-stack-protector 關閉這種機制。而 -z execstack 用於允許執行棧。
攻擊程式
我們的目的是攻擊剛才的漏洞程式,並通過攻擊獲得root許可權。把以下代碼儲存為“expolit.c”檔案,儲存到 /tmp 目錄下。代碼如下:
注意上面的代碼,“\x??\x??\x??\x??”處需要添上shellcode儲存在記憶體中的地址,因為發生溢出後這個位置剛好可以覆蓋返回地址。而 strcpy(buffer+100,shellcode); 這一句又告訴我們,shellcode儲存在 buffer+100 的位置。現在我們要得到shellcode在記憶體中的地址,輸入命令:
根據語句 strcpy(buffer+100,shellcode); 我們計算shellcode的地址為 0xffffd1b0(十六進位)+100(十進位)
然後,編譯exploit.c程式:
攻擊結果
通過前後對比,可以獲得root許可權。實驗成功。
感想
在實驗之前,查閱了很多關於緩衝溢出攻擊的總聊,所以就想自己在虛擬機器上獨自完成,自己編寫攻擊代碼,自己反組譯碼以及自己計算需要溢出的位移量。但是在編譯的時候發現,攻擊代碼無法通過編譯,儘管屏蔽了一些棧的保護機制(網上查到的方法都用),但始終無法編譯通過。只好在實驗樓上面做緩衝區一處實驗,實驗也成功率。雖然,整個實驗過程參照了實驗樓的步驟,同時也將自己在網上查閱到的一些重要知識補充在了這篇部落格中。通過這次實踐,對“棧”有了更深的認識,也更加熟悉了緩衝區溢位攻擊這一經典的攻擊方法,獲益匪淺。
通過這次實驗,我覺得對於我們學資訊安全系統設計基礎的學生來說,完成緩衝區溢位攻擊這個實驗是非常有必要的,就像gdb反組譯碼實踐一樣,很多東西就能通過這個實驗來掌握。通過學習課本的第三、四章的學習,我們只能略懂一些皮毛,而通過gdb反組譯碼實踐和緩衝區溢位實驗,我們肯定會將那些概念性的東西,深深記載腦子裡,而且會將所學的知識,提高一個檔次。
教材配套項目——緩衝區實驗