當前位置:成語大全網 - 新華字典 - PWN格式化字符串2——例子

PWN格式化字符串2——例子

這壹部分主要寫三道PWN題,都還算是比較簡單的,同樣參考CTF-wiki裏的writeup

這道題是64位的格式化字符串漏洞,主要利用格式化字符串漏洞泄露內存中的數據就足夠了。

64位的偏移計算和32位類似,都是算對應的參數。只不過64位函數的前6個參數是存儲在相應的寄存器中的。那麽在格式化字符串漏洞中呢?雖然我們並沒有向相應寄存器中放入數據,但是程序依舊會按照格式化字符串的相應格式對其進行解析。

以UIUCTF中 pwn200 GoodLuck為例進行介紹。需要在本地設置壹個flag.txt文件

IDA看壹下程序,發現錯誤出在 printf(format) 這裏

裏邊的format字符串是接收用戶的輸入,這樣就提供了觸發該漏洞的機會。

在printf處下斷點,放開運行

(1)可見flag對應的棧上的偏移為5,除去對應的第壹行為返回地址外,其偏移為4。

(2)此外,由於這是壹個64程序,所以前6個參數存放在對應的寄存器中,fmt字符串存儲在RDI寄存器中,所以fmt字符串對應的地址的偏移為10。

(3)而fmt字符串中 %order$s 對應的order為fmt字符串後面的參數的順序,所以我們只需要值入 %9$s 即可得到flag的內容

在目前的C程序中,libc中的函數都是通過GOT表來跳轉的。此外,在沒有開啟RELRO保護的前提下。每個libc的函數對應的GOT表項是可以被修改的。

因此,我們可以修改某個libc函數的GOT表內容為另壹個libc函數的地址來實現對程序的控制。

比如說我們可以修改printf的got表項內容為system函數的地址。從而,程序在執行printf的時候實際執行的是system函數。

假設我們將函數A的地址覆蓋為函數B的地址,那麽這壹攻擊技巧可以分為以下步驟

(1)確定函數A的GOT表項

這壹步我們利用的函數A壹般在程序中已有

(2)確定函數B的內存地址

這壹步通常來說,需要我們子集想辦法來泄露對應函數B的地址。

(3)將函數B的內存地址寫入到函數A的GOT表地址處。

這壹步壹般來說需要我們利用函數的漏洞來進行觸發,壹般利用方法有如下兩種

·寫入函數:write函數

·ROP

開啟了NX保護,即數據不可執行

main函數

簡單運行壹下pwn3,再看主函數,需要簡單進行壹下reverse

ask_username()函數接收第壹次的輸入,ask_password對輸入進行驗證,和"sysbdmin"進行比較

ask_username()中對輸入字符串進行了簡單的變化,先計算壹下需要輸入的字符串

計算出輸入值為 'rxraclhm'

在主循環中有put_file(),show_dir(),get_file()三個函數

put_file()

看看put_file函數中,接收我們輸入file的名字以及內容。

使用malloc分配244個字節,建立如下數據結構,多次的put將形成壹條鏈表。

get_file()

get_file()函數中存在格式化字符串函數。

要求先輸入filename,然後遍歷鏈表,匹配filename,找到則輸出內容。找不到的話,是輸出當前棧裏的內容。

show_dir()

遍歷鏈表,將所有的filename串起來輸出。

並且裏邊調用了puts()函數,因此可以修改puts@got表

利用之前的知識來計算偏移量,輸入 aaaa.bbbb.cccc.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p

,則

有兩種判斷偏移的辦法

(1)看%p輸出的結果裏,偏移為7

(2)從棧中看0xffffced4到0xffffceec的偏移為7

(1)首先讀取puts@got的內容,得到puts函數的地址,然後通過libc中偏移量固定的方式計算出system的地址。

(2)將system地址寫到puts@got裏,替換puts函數

(3)讓程序執行pus('/bin/sh'),那麽實際執行的是system('/bin/sh')

再說兩句

這道題踩了幾個坑,首先是用題目自帶的libc,怎麽做也做不出來。後來用LibcSearcher,也匹配不到,到後來還是去libc database search搜索到的對應libc版本。

嗯,得到了puts的地址之後,慢慢做,會出來的。

fmtstr_payload()

fmtstr_payload()是pwntools裏面的壹個工具,用來簡化對格式化字符串漏洞的構造工作。

可以實現修改任意內存

fmtstr_payload(offset,{printf_got:system_addr}) (偏移,{源地址:目的地址})

mtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’)

第壹個參數表示格式化字符串的偏移;

第二個參數表示需要利用%n寫入的數據,采用字典形式,我們要將printf的GOT數據改為system函數地址,就寫成{printfGOT:

systemAddress};本題是將0804a048處改為0x2223322

第三個參數表示已經輸出的字符個數,這裏沒有,為0,采用默認值即可;

第四個參數表示寫入方式,是按字節(byte)、按雙字節(short)還是按四字節(int),對應著hhn、hn和n,默認值是byte,即按hhn寫。

fmtstr_payload函數返回的就是payload

hijack retaddr

利用格式化字符串漏洞來劫持程序的返回地址到我們想要執行的地址。

開啟了數據不可執行(NX),並且有RELRO,所有不能修改GOT表

首先是壹個讓我們輸入username和 password的函數

需要註意的是裏邊讀取密碼的部分,

read(0,(char *)&a9+4,0x14uLL);

漏洞出現在下邊的函數中,其輸出內容為&a4+4。

第二個printf輸出的內容正&a9+4好是之前read寫入password的內容。

並且在IDA中分析可以得知&a9+4-&bufa=14h=20

system('/bin/sh')

發現strings裏面有/bin/sh,在0x4008A6地址處有壹個直接調用system('/bin/sh')的函數。

如果修改某個函數的返回地址為0x4008A6,那就相當於獲得了shell。

雖然存儲返回地址的內存本身是動態變化的,但是其相對於rbp的地址並不會改變,所以我們可以使用相對地址來計算。利用思路如下:

(1)確定偏移

(2)獲取函數的RBP與返回地址

(3)根據相對偏移獲取存儲返回地址的地址

(4)將執行system函數調用的地址寫入到存儲返回地址的地址。

在printf處下斷點,輸入aaaaaaaa為用戶名,

密碼輸入為 %p%p%p%p%p%p%p%p%p%p

可以看到用戶名在棧上第三個位置,除去本身格式化字符串的位置,其偏移為5+3=8。

並且棧上第二個位置存儲的就是該函數的返回地址(其實也就是調用show account函數時執行push rip所存儲的值),在格式化字符串中的偏移為7。

與此同時棧上,第壹個元素存儲的也就是上壹個函數的rbp。所以我們可以得到偏移0x7fffffffdde0- 0x7fffffffdda8=0x38。繼而如果我們知道了rbp的數值,就知道了函數返回地址的地址。

0x400d74與0x4008A6只有低2字節不同,所以我們可以只修改0x7fffffffdda8開始的2個字節,因此可以利用$hn只對後三位修改即可,0x8A6對應的10進制數字為2214,但是這裏需要修改為2214+4=2218