參考資料1.https://www.cnblogs.com/0831j/p/9219243.html (實驗、原理) 2.https://www.cnblogs.com/xidian-wws/p/10819073.html(getenvaddr那個的說明) 實驗過程中的一些問題https://mp.csdn.net/postedit/89915014 遺留問題: 1.那些位址在gdb和直接運作的位址是不一樣的,不清楚為什麼,也沒有找到規律 結論這些結論都在我的另一篇部落格中有詳細說明,為了閱讀友善,放到一起三個程式實際運作的位址差異首先要關閉位址随機化
從上面的表格可以看出: (1)在shell中運作時,這幾個程式位址的差異是它們程式名的二倍,這也是本次實驗利用getenvaddr.c加上偏移擷取retlib.c環境變量的方法,具體原因見這個部落格的末尾:https://www.cnblogs.com/xidian-wws/p/10819073.html; (2)在gdb中不管是上面兩種方法中的哪一種,同一程式擷取到的位址都是一樣的,但是不同程式擷取到的位址偏移是它們程式名的一倍,這個原因尚沒有找出來,希望知道的朋友告知; (3)retlib是漏洞程式,getenvaddr和expl是攻擊程式,這兩個攻擊程式其實是可以合并的,我們的目的就是通過攻擊程式猜漏洞程式環境變量的位址; (4)假設不用getenvaddr,隻用expl這個攻擊程式:可以通過在shell中運作expl程式(程式中會利用getenv函數擷取程式變量位址),然後加上expl和retlib程式名偏移量的二倍來猜測retlib程式環境變量的位址;也可以通過在expl的gdb中擷取到環境變量的位址後,加上他們程式名偏移量的一倍來擷取retlib程式環境變量的位址(已驗證); (5)假設是在gdb中擷取到的位址,那麼retlib隻能在gdb中運作才能攻擊成功,在shell中運作是不會攻擊成功的,因為同一環境變量在shell中和在gdb中的位址是不一樣的 (關于這個的解釋,老師說因為gdb調試是靜态的,shell運作是動态的,位址肯定不一樣; 一個大神的解釋是:gdb的調試環境會影響buf在記憶體中的位置,雖然關閉了ASLR,但這隻能保證buf的位址在gdb的調試環境中不變,但當直接執行的時候,buf的位置會固定在别的位址上) 感覺大神的這個解釋應該某一函數中的局部變量在gdb和直接運作時地不一樣的原因,我現在也不是很懂 (6)對于(5),在gdb中攻擊成功的話,擷取的是普通使用者的shell,不是root,現在我也不知道能不能擷取到root以及如何擷取到root 不同程式環境變量及位址差異
可以看到,這兩個程式的環境變量位址是一樣的,但是環境變量所指向的空間是不一樣的,差了兩個位元組,自己也還沒有想明白? 自己整了一份他們的記憶體比較,見自己的筆記。 同一程式不同參數位址的差異這個例子說明,對于同一個程式,找其環境變量的位址時,加不加那個參數都是一樣的,隻是環境變量的位址變量一點,但是環境變量所指向的 空間是一樣的 |
實驗原理(攻擊)之前的攻擊方法大量使用Shellcode,核心思想是修改EIP和注入Shellcode,在函數傳回時跳到Shellcode去執行。 (防禦)DEP(Data Execution Prevention,資料執行保護):将資料所在記憶體頁辨別為不可執行,當程式溢出成功轉入shellcode時,程式會嘗試在資料頁面上執行指令,此時CPU就會抛出異常,而不是去執行惡意指令。DEP 的主要作用是阻止資料頁(如預設的堆頁、各種堆棧頁以及記憶體池頁)執行代碼。 (攻擊)系統庫函數通常是不受DEP保護的,是以通過将傳回位址指向系統函數可以繞過DEP保護,是以可以通過調用系統函數system()獲得shell。system函數是通過/bin/sh指令去執行一個使用者執行指令或者腳本。是以,我們可以利用system來實作Shellcode的功能。 對于ret2libc,即return-to-libc(傳回到系統庫函數執行),攻擊者能夠通過緩沖區溢出改寫傳回位址為一個庫函數的位址,并且将此庫函數執行時的參數也重新寫入棧中。這樣當函數調用時擷取的是攻擊者設定好的參數值,并且結束後傳回時就會傳回到庫函數而不是 main()。而此庫函數實際上就幫助攻擊者執行了其惡意行為。 構造惡意輸入資料:借助緩沖區溢出手段,我們可以構造輸入資料,使得其将傳回位址 ret 的值覆寫,修改為系統中已存在的 system 函數的值,則函數傳回時控制流會跳轉至 system 函數,system 函數會将寄存器 %ebp 儲存,之後其通過 %ebp + 8 獲得所需的參數,而在函數傳回時,其會認為位于儲存的 %ebp 之後的棧頂資料為函數調用的傳回位址。通過構造資料,可以将原函數調用的 ret 修改為目标函數位址,之後放置我們所需的傳回位址(比如exit函數的位址),并根據正常的函數調用棧結構構造參數清單。 | |||
實驗複現:複現流程按照該實作進行:https://www.cnblogs.com/0831j/p/9219243.html 環境:ubuntu12.04.5 64位 1.輸入指令安裝一些用于編譯 32 位 C 程式的東西。 sudo apt-get update sudo apt-get install lib32z1 libc6-dev-i386 sudo apt-get install lib32readline-gplv2-dev 2.關閉位址随機化 sudo sysctl -w kernel.randomize_va_space=0 3.即使你能欺騙一個 Set-UID 程式調用一個 shell,也不能在這個 shell 中保持 root 權限,這個防護措施在/bin/bash 中實作。 linux 系統中,/bin/sh 實際是指向/bin/bash 或/bin/dash 的一個符号連結。為了重制這一防護措施被實作之前的情形,我們使用另一個 shell 程式(zsh)代替/bin/bash。下面的指令描述了如何設定 zsh 程式。 sudo apt-get install zsh sudo su cd /bin rm sh ln -s zsh sh exit 4.漏洞代碼retlib.c sudo su gcc -m32 -g -z noexecstack -fno-stack-protector -o retlib retlib.c chmod u+s retlib exit 5.讀取環境變量的程式 gcc -m32 -o getenvaddr getenvaddr.c export BIN_SH="/bin/sh" //這種是屬于臨時導入環境變量的方法,是以每次打開終端都要添加 echo $BIN_SH ./getenvaddr BIN_SH ./retlib 6.攻擊程式expl.c gcc -m32 -g -o expl expl.c//編譯 gdb -q ./exploit//調試 b 10//設定斷點 run//運作到斷點處 p system//擷取system位址 p exit//擷取exit位址 7.修改 expl.c 檔案,填上剛才找到的記憶體位址 vim expl.c gcc -m32 -g -o expl expl.c 8.攻擊 ./expl ./retlib | |||
實驗代碼
我們可以看到buffer距離ebp有20個位元組,是以buffer距離ret有24個位元組,是以我們将buffer[24](即system函數棧幀的ebp)填充為system位址,将buffer[32](即system函數棧幀的ebp+8)構造為/bin/sh的位址,這個位址作為system函數的輸入;而buffer[28](即system函數棧幀的ebp+4)作為system函數的傳回位址。 (1)buffer[28](即system函數棧幀的ebp+4)不做任何填充 (2)buffer[28](即system函數棧幀的ebp+4)填充為exit函數的位址 | |||
實驗結果(1)buffer[28](即system函數棧幀的ebp+4)不做任何填充 (2)buffer[28](即system函數棧幀的ebp+4)填充為exit函數的位址 | |||
新知識: export BIN_SH="/bin/sh" 定義了一個BIN_SH變量,指派為”/bin/sh”,并将其導出為環境變量 echo $BIN_SH 輸出變量BIN_SH的值 頭檔案:#include <stdlib.h> 定義函數:char * getenv(const char *name); 函數說明:getenv()用來取得參數name 環境變量的内容. 參數name 為環境變量的名稱, 如果該變量存在則會傳回指向該内容的指針. 環境變量的格式為name=value. 傳回值:執行成功則傳回指向該内容的指針, 找不到符合的環境變量名稱則傳回NULL. http://c.biancheng.net/cpp/html/377.html C語言main函數參數說明: https://blog.csdn.net/xiaocainiaoshangxiao/article/details/13996459 Shell變量由全大寫字母加下劃線組成,有兩種類型的Shell變量: 環境變量 環境變量可以從父程序傳給子程序,是以Shell程序的環境變量可以從目前Shell程序傳給fork出來的子程序。用printenv指令可以顯示目前Shell程序的環境變量。 本地變量 隻存在于目前Shell程序,用set指令可以顯示目前Shell程序中定義的所有變量(包括本地變量和環境變量)和函數。 環境變量是任何程序都有的概念,而本地變量是Shell特有的概念。在Shell中,環境變量和本地變量的定義和用法相似。 在Shell中定義或指派一個變量: $ VARNAME=value 注意等号兩邊都不能有空格,否則會被Shell解釋成指令和指令行參數。 一個變量定義後僅存在于目前Shell程序,它是本地變量,用export指令可以把本地變量導出為環境變量,定義和導出環境變量通常可以一步完成: $ export VARNAME=value 也可以分兩步完成: $ VARNAME=value $ export VARNAME 如果一個變量叫做VARNAME,用${VARNAME}可以表示它的值,在不引起歧義的情況下也可以用$VARNAME表示它的值。注意,在定義變量時不用$,取變量值時要用$。 參考:http://docs.linuxtone.org/ebooks/C&CPP/c/ch31s03.html linux下如何在終端上運作和安裝可執行檔案 linux下可執行檔案,如果是在/bin、/sbin、/usr/bin或者/usr/sbin 裡頭,就直接敲檔案名就可以運作.(注意環境變量PATH是否包含了路徑) 如果是在其他檔案夾裡,比如說運作/root裡頭的 time檔案 你就 cd /root 再 ./time。注意./與time之間沒有空格。 ./表示目前目錄下,../表示上一級目錄,../../表示上上一級目錄。 linux終端中,是用"sh"來運作檔案,或者"./" 比如你在/usr/temp檔案夾中有一個可執行檔案"flash" 你可以在終端中輸入 sh /usr/temp/flash 或者進入檔案夾 cd /usr/temp 然後運作: ./flash 參考:https://blog.csdn.net/Se7enYea/article/details/8245672 lea對變量沒有影響是取位址,對寄存器來說加[]時取值,第二操作數不加[]非法 mov對變量來說沒有影響是取值,對寄存器來說是加[]時取位址,第二操作數不加[]是取值 LEA指令有兩個操作數。左邊是目的操作數,表示操作結果儲存在此,該指令目的操作數隻能是8個通用寄存器之一。逗号右邊的是源操作數,該指令的源操作數隻能是一個存儲單元,表達存儲單元有多種尋址方式。(看這個彙編代碼感覺左右剛好反着呢,等自己看完彙編的書,再來研究研究) LEA指令的功能是将源操作數、即存儲單元的有效位址(偏移位址)傳送到目的操作數。 示例LEA BX,[BX+SI+0F54H]指令中,[BX+SI+0F54H]采用相對基址變址的尋址方式表達存儲單元,它表示的存儲單元的有效位址是:BX内容加SI内容加0F54H。這個結果被傳送到BX中。 |