1. 程式人生 > 其它 >攻防世界pwn題:forgot

攻防世界pwn題:forgot

0x00:檢視檔案資訊

該檔案是32位的,canaryPIE保護機制沒開。

 

0x01:用IDA進行靜態分析

總覽:

該函式就是:v5初值為1,對v2輸入一串字元。然後執行一個會根據輸入的字串而修改v5的迴圈語句,最後呼叫相應的函式。

 

同時,發現檔案裡面已經含有cat flag的函式:

 

函式snprintf介紹:
printf("cat %s", "./flag")是將cat ./flag輸出到螢幕上。
snprintf(s, 0x32, "cat %s", "./flag")是最多將後面的字串("cat ./flag")輸入0x32個到變數s上。

       所以,我們要想辦法去執行這個cat_flag函式。現在我們已知的漏洞點是scanfv2輸出存在溢位。再根據程式的第3行到第8行中變數的宣告可瞭解到v2可以溢位覆蓋陣列v3、陣列s、變數v5甚至是所在棧幀的返回地址。

       一個常規的解法是覆蓋v3[0]cat_flag函式的地址值,然後進一步想辦法使執行迴圈後,v5的值為1。這樣在最後就會呼叫v3[--1]指向的函式,即get_flag函式。

v5的初值為1,所以在switch中為case 1。其條件判斷為:

因為v5本來就是1,所以我們只要保持不變就好了,即return false。根據短路原理,只要字串均為A(ascii=65 < 96)就行了。

 

0x02:編寫exp

from pwn import *
context(os='linux', arch='i386', log_level='debug')
io
= process("
./forgot") #io = gdb.debug("./forgot",'b *0x08048A5D') #io = remote("111.200.241.244",58065) get_flag = 0x080486CC payload = b'A'*0x20 + p32(get_flag) io.sendline("tolele") io.recv() io.sendline(payload) io.recv() io.interactive()

可以成功catflag

 

0x03:回顧再分析

因為v2定義的陣列大小是32個元素,所以我們還需要考慮的問題是:

  • 當對v2進行溢位式的賦值後,strlen(v2)會等於多少呢?
  • 如果strlen(v2)的值大於或等於32,程式中對陣列v2的訪問豈不是越界了麼?

 

解決問題:

我們可以對strlen函式的原始碼進行分析:

strlen.c source code [glibc/string/strlen.c] - Woboq Code Browser

對該原始碼分析的部落格:

c語言庫函式strlen原始碼實現_風雨也從容的部落格-CSDN部落格_c語言strlen原始碼

 

簡而言之,strlen(const char* str)的返回值就是,從首位元組開始從1往上計數,到'\x00'停止,且不算入'\x00'

 

(注:以下內容僅是個人想法,請保留質疑!)

       既然這樣,那麼payload = b'A'*0x20 + p32(get_flag)豈不是會遠遠大於32(陣列v3中均沒有出現'\x00'位元組),這樣對陣列的訪問不會報錯嗎?

       其實,我們平時遇到的陣列訪問越界是由於在整合開發環境(vs,vc…)中會進行檢測。但實際上原始碼中是沒有檢測機制的,linux中就是直接對動態庫進行連結,相當於是直接使用了原始碼。所以,在linux上並不會出現報錯。

        同樣,陣列索引的本質實現是基於組合語言,相當於就是*(v2+i)。在linux中並不會進行越界檢測。

 

實驗一下:

我們對c2進行字串輸入後,會自動在字串末尾添加個'\x00'。即上面實驗中c3[14]被賦值了'\x00'。同時,由於get_flag = 0x080486CC,ascii碼值均小於96,所以v5不會被改變。

所以,問題也就差不多解決了。

 

0x04:其他思路的分析

其實在最開始時,我們如果看到了棧溢位和get_flag函式。最先想到的就是直接棧溢位覆蓋main函式棧幀的返回地址,結束時直接呼叫就好啦~

此時,payload = b'A'*0x78 + b'B'*0x4 + p32(get_flag)

(動態除錯後發現是0x78,並不是ida中的0x74)

執行後發現pwn不通,動態除錯一下:

得知,程式開在了0x08048a61這一步。這不卡住才怪,用eax*4來進行偏移,而eax0x44444443('CDDD')

 

ida中繼續對eax值的來源進行調查:

       對該處按F5檢視反彙編程式碼,發現((void (*)(void))v3[--v5])(); 這裡的。上一步是將esp + 78h的值賦給了eaxesp + 78h對應的變數則是v5。由於我們直接覆蓋到返回地址,所以圖中也把v5給覆蓋了,值還挺大的。早就超了最大空間,所以執行不下去了。

 

0x05:個人嘮叨

        以前在做題過程中遇到問題總喜歡逃避,畢竟這是“解決”問題最輕鬆的一種方式。在做這道題時,我硬逼著自己去思考能夠解決問題的方法,以及動手去嘗試。一步一步的去解決問題,雖然很慢,但知識學得很實。希望自己能夠繼續以這種方式面對各種困難。

        最後再反省一下:在第一個思路pwn不通的時候,直接丟個程式碼和exp問群裡的師傅為啥pwn不通?現在想想這樣去提問還挺不好的,這將會浪費師傅們很多時間去確定問題所在。所以,以後問問題的話,儘量縮小出問題的範圍,或者將問題轉換為概念性的提問。

 


 

tolele

2022-06-05