當前位置:
首頁 > 新聞 > pwnable.tw刷題之dubblesort

pwnable.tw刷題之dubblesort


前言


上一篇中我介紹了phttp://www.freebuf.com/articles/others-articles/134271.htmlwnable.tw中第三題calc的解題思路,在這篇里,我將和大家分享第四題dubblesort的分析過程。


該題在演算法上難度不大,能看得懂彙編就基本上可以分析清楚,重點是如何在ASLR、NX等多重保護開啟的情況下,利用題目中出現的漏洞來進行漏洞利用,並獲取系統shell。該題為我們提供了一個在多重保護下棧溢出的思路,而且還有幾個小的技巧值得我們學習。作為一名新手,在這題上我也是絞盡腦汁,最後還是在別人的提示下完成題目,在此將學到的知識分享給大家。





1、題目解析





題目給出了程序dubblesort和libc庫文件,說明可能可以通過return to libc的方式進行漏洞利用。從題目來看,貌似是一道和排序(sort)相關的題目。下載並執行程序dubblesort,如下圖可以看到,首先需要輸入用戶名,之後輸入想要排序的數字的個數,再依次輸入要排序的數字,最後程序會計算並輸出排序結果。



使用pwntools的checksec功能對程序的執行保護進行檢查,發現包括NX在內的大部分保護都開啟了,這對我們來說並不是一個好消息





2、演算法分析



使用IDA對dubblesort程序進行分析,發現程序流程並不複雜。




2.1) main函數分析


如下圖所示,在main函數中,首先生成canary並將其入棧,然後調用了timer()函數來計時,超時則會結束程序。在這之後,程序調用read函數來獲取用戶輸入的用戶名,緩衝區大小為64位元組,也就是64/4=16個棧單元。緊接著,程序調用scanf函數接收用戶輸入的要排序的數字個數。在以上準備工作完成後,程序就進入while循環,依次接收要排序的數字,並將其保存在nums數組中,該數組是函數創建的局部變數,從下圖中可以看出,其起始位置位於棧上esp+0x1c的位置。





2.2) sort函數分析

在接收完用戶的所有輸入後,程序調用sort函數對用戶輸入的所有數字按照從小到大的順序進行排序。



上圖為sort函數的代碼,演算法很簡單,是一個典型的冒泡排序,重複count次,每次排序將當前最大的數放在數組的最後,在循環結束後,所有數就從小到大排列了。由於排序並不是本題的重點,因此在這裡就不詳細介紹了,感興趣的朋友可以查閱冒泡排序相關知識。在這裡我們只用記得,排序後的數字序列仍然保存在原先棧上開闢的這段空間內,只不過數值的順序變了。




3、漏洞分析






3.1) 棧溢出和canary繞過


從上面的分析可以看出來,在輸入待排序數的時候,程序並沒有限制要排序數的個數。但是,由於待排序數組位於棧空間內,而當前棧空間的大小是有限的,這就可以導致棧溢出。循

環為數組賦值的彙編代碼如下:



從上圖我們可以看出,待排序數組的起始位置為esp+0x1c。但是不要忘了,我們的main函數開啟了canary保護,canary的位置在esp+0x7c的位置(

如下圖

),該位置在esp+0x1c和ebp之間,這讓棧溢出無所適從。



這時我們要考慮,有沒有什麼方法在輸入數據時不改變棧上原來數據的內容?我嘗試著輸入非法字元,結果如下:



這裡出現了一個奇怪的現象,當我在第五個數的位置輸入「f」這個非法字元時,之後的所有輸入自動結束,並且從該位置之後的數據被泄露出來。這個原因我思索了好久,最後發現,這是因為scanf函數接收的數據格式為無符號整型(%u),而程序在檢測到stdin中的字元是「f」時,將其視為非法輸入,於是本次的scanf函數執行失敗,原棧上對應位置的數據也沒有被改變。


在下一次循環執行到scanf時,程序又到stdin中取數據,這時,上次輸入的「f」由於非法並沒有被取走,它還在stdin中存在著,因此scanf的輸入又失敗了……至此往後的每次循環,scanf都去取stdin中的這個「f」,然後每次都失敗,於是從第五個位置往後的所有棧上數據都不會被修改,且在程序最後被泄露出來。


這裡可能有朋友要問了,在循環中明明有fflush,為什麼無法清空stdin?我在網上查了相關內容,發現對於一些編譯器,fflush會失效,不知道這裡是不是這個原因。如果有朋友清楚這裡的疑問,請一定要幫我解惑!


題目到此,好像這條路走不通了。我曾經查閱資料,嘗試使用其它方式繞過canary,但都證明了不可行。那有沒有什麼字元可以既讓scanf認為它是合法字元,同時又不會修改棧上的數據呢?在多次嘗試和不斷查閱資料後,我發現「+」和「-」可以達到此目的!因為這兩個符號可以定義正數和負數,所以會被識別為合法字元。比如輸入「+4」會被識別為4,而「-4」則會將其轉為正數輸出(%u的原因)。測試如下圖:



如上圖所示,4294967293即為-3的無符號值,它們的十六進位是一樣的(0xFFFFFFFD)。那麼我們只輸入一個「+」或者「-」就可以達到我們的目的。如下圖:



從圖中可以看到,在第4個數的位置我輸入了「+」,它並未改變棧上數據,且不會影響之後的輸入。


至此,我們可以解決canary繞過的問題了。canary距離待輸入數據的起始位置為(esp+0x7c)-(esp+0x1c)+4=100位元組,100/4=25個棧空間。也就是說,當我們要輸入第25個數據時輸入「+」或者「-」就可以保持canary不變,從而繞過函數最後的canary檢查,實現棧上任意位置的寫入。




3.2) libc地址的泄露


那麼我們要往棧上寫入什麼數據呢?前文提到,題目給了libc庫文件libc.so.6,這就暗示我們可以通過ret2libc的方式來進行棧溢出。該方法的利用方式是,修改棧上函數返回值地址,將其變為libc庫中某函數的地址(如system函數),從而達到獲取系統shell等目的。


我們可以修改棧上的main函數返回值為libc中的system函數地址,並在參數對應的位置寫入「/bin/sh」字元串的地址,從而使程序跳轉到system函數,並執行shell。所期望的溢出後的棧空間如下圖:



從圖中可以看出,我們要溢出的數據總共35個棧空間,其中第25個棧空間的canary通過輸入「+」保持其值不變;第33個棧空間寫入system函數的地址;第34個棧空間是system函數的返回地址,由於我們無需考慮system返回後的工作,次數據可任意填寫;第35個棧空間需寫入「/bin/sh」字元串的地址。現在的主要問題是,如何獲取system函數和「/bin/sh」字元串的地址?


首先我們通過gdb調試發現,在ebp+4(main函數返回地址)的位置存放了一個libc中的函數地址__libc_start_main(main函數執行完後返回至該函數),可通過多次執行程序泄露該位置數據來判斷libc地址是否隨機,即目標系統是否開啟ASLR。由上圖可知,ebp+4的位置是從nums[0]開始的第33個棧空間,因此我們通過多次輸入來泄露該位置的值,過程如下:




從上圖可以看出,兩次執行程序後,第33個位置的內容改變了,分別為4149671479=0xF756F637和4150175287=0xF75EA637,說明系統的libc基址改變了,開啟了ASLR。


我們知道,在ASLR開啟的情況下,堆棧地址和libc的地址都是隨機的,那麼我們如何獲取libc中函數的地址呢?通過在輸入數字時輸入「+」來泄露棧上數據的方法開上去可行,但每次泄露後程序就結束了,下次再執行程序時libc的地址又改變了,無法通過這種泄露來獲取當前進程空間的libc地址並進行利用。因此我們要通過其它的手段來在程序執行的過程中泄露libc地址。


經過研究,我發現在輸入用戶名後程序的返回有點奇怪:



上圖可看到,我們輸入的用戶名是mike,但程序返回的輸出並不正常,不僅在mike後換行,而且後面還跟著幾個不可見字元,這是為什麼?


再來到程序接收用戶名輸入和輸出的過程:



我們發現,程序在用printf函數輸出歡迎字元串「Hello….」的時候格式為%s,大家知道,printf在做格式化輸出字元串時,是以0×00(null)為結尾來判斷字元串結束,可是在我們輸入用戶名name的時候,程序是用read來接收的,它並不會自動為我們輸入的字元串補0。當我們輸入「mike」這4個字元並敲回車後,真正傳給程序的是「mike
」這樣一個5位元組的字元串。


程序在接收這個字元串後將這五個字元保存在棧上的esp+0x3c的位置,但這五個字元之後是否跟著0×00就不得而知了。根據上面的輸出我們大致可以猜到,「Hello mike」之後的換行應該是我們輸入的回車(「
」)導致的,而下一行一開始的幾個不可見字元,應該是棧上緊跟著換行符後面的數據。也就是說,我們通過輸入,無意中泄露了棧上的數據!


這是一個好消息,因為我們可能可以在覆寫棧上數據之前泄露出libc的地址。那麼name之後的64位元組地址空間中是否含有libc中的地址呢?


通過gdb調試,我們發現在name後的第7個棧單元保存著一個疑似libc中的地址0xf7fb1000:



那麼此時的libc基址是多少呢?該地址又是否是libc上的地址呢?我首先通過 info sharedlibrary命令來獲取libc的地址:



從圖中可以

看出,libc.so.6的地址空間為0xf7e16750到0xf7f4204d,好像並不包括我們上面可泄露的地址0xf7fb1000。我又用vmmap命令(info proc mappings命令亦可)查看libc在內存中的載入情況:



可以看出,libc的地址範圍變為0xf7dff000到0xf7fb2000,和info sharedlibrary命令獲取的libc地址不同(這裡libc-2.23.so和libc.so.6是同一個文件,libc.so.6是libc-2.23.so鏈接)。這個問題同樣困擾了我好久,通過查閱資料

,我找到了兩個命令的不同之處:info sharedlibrary顯示的地址範圍為libc-2.23.so文件的.text段在內存中的地址範圍,而vmmap顯示的為libc-2.23.so文件載入到內存中的全部地址空間。在這裡可以這樣驗證:






首先通過hexdump命令驗證了0xf7dff000確實為libc-2.23.so載入在內存中的起始地址(可清楚地看到ELF頭部標誌)。之後通過readelf -S命令查看libc-2.23.so文件的.text段偏移(0×17750),將其加上起始地址0xf7dff000即為0xf7e16750。驗證成功。這個小實驗和本題關係不大,但是能告訴大家如何在gdb調試時更加清楚地查看libc基址。


回到問題開始,0xf7fb1000這個地址確實在libc載入在內存中的地址範圍內(0xf7dff000到0xf7fb2000),它的偏移是0xf7fb1000-0xf7dff000=0x1b2000,那麼我們就可以泄露這個地址並減去它相對於libc基地址的偏移來動態獲取libc的基址。可是事情並沒有這麼簡單,在我寫好exp後,怎麼執行都無法獲取shell,最後發現是這個偏移出了問題。因為我自己的libc庫和目標系統的libc庫不一樣,偏移也就不同!那麼真正的偏移是多少呢?


我們再用readelf命令來看看0x1b2000這個偏移在我的libc中的位置:




從上圖可以看出,該偏移是.got.plt節相對於libc基址的偏移,那麼我們再來看看題目中給的目標系統的libc文件的節情況:



可以看出,.got.plt節的偏移為0x1b0000,並不等於我們之前得到的0x1b2000。而system函數的偏移和「/bin/sh」字元串在libc中的偏移我們可以通過readelf -s命令和二進位編輯器HxD得到:




這樣一來,我們就可以得到libc基址、system函數地址以及「/bin/sh」字元串的地址:


addr_libc=addr_leak-0x1b0000


addr_system=addr_libc+0x3a940


addr_shell=addr_libc+0x158e8b




4、漏洞分析




有了以上的分析,漏洞利用的實現就簡單多了。


首先我們要泄露name後第6個棧單元上的數據(

.got.plt節地址

)。由分析可知,該單元距離name的初始地址為24位元組,因此我們至少要發送24位元組的冗餘數據。經測試後發現,該棧單元的數據的第一個位元組(即

.got.plt節地址的最後一個位元組,因為小端序

)總為0×00,因此若要泄露該數據,需要多發送一個位元組覆蓋掉0×00,否則printf會將0×00之後的數據截斷。可以發送』A"*24+』
』來泄露出該數據的後三個位元組,再加上』x00′即可。


之後就可以根據泄露的地址推算出system函數和「/bin/sh」字元串在內存中的地址。需要注意的是,程序在執行過程中會將所有數據排序,因此我們需要在輸入數據時注意數據的大小,這並不難,具體做法是將canary之前的數據都置0,canary和返回地址之間(包括返回地址)的數據都寫入system函數的地址(canary隨機數大部分時間都小於system地址,除非人品不好),而最後兩個棧單元都寫入「/bin/sh」字元串的地址即可。配置好的棧結構如下:





後話




經過一段時間的學習,我深刻地意識到自己根基不牢且知識量匱乏,目前接觸的這些皮毛只是二進位世界裡極小的一部分,仍有太多好玩的東西等待著我們去學習和挖掘。


由於pwnable.tw要求不允許在公開渠道公布高分題目的解題思路,雖然這只是一道200分的題,但是POC和flag我就不留啦,祝大家解題愉快!


*本文作者:oO0ps,轉載請註明FreeBuf.COM


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 FreeBuf 的精彩文章:

某雲用戶網站入侵應急響應
學點演算法搞安全之apriori
維基解密披露CIA惡意軟體框架中的新工具:AfterMidnight與Assassin
使用Kettle模型清洗全國弱口令Top 1000

TAG:FreeBuf |