0x00 UAF原理

如上代碼所示,指針p1申請記憶體,列印其位址,值
然後釋放p1
指針p2申請同樣大小的記憶體,列印p2的位址,p1指針指向的值
Gcc編譯,運作結果如下:
p1與p2位址相同,p1指針釋放後,p2申請相同的大小的記憶體,作業系統會将之前給p1的位址配置設定給p2,修改p2的值,p1也被修改了。
由此我們可以知道:
1.在free一塊記憶體後,接着申請大小相同的一塊記憶體,作業系統會将剛剛free掉的記憶體再次配置設定。
根本原因是dllmalloc:
參考資料:http://blog.csdn.net/ycnian/article/details/12971863
當應用程式調用free()釋放記憶體時,如果記憶體塊小于256kb,dlmalloc并不馬上将記憶體塊釋放回記憶體,而是将記憶體塊标記為空閑狀态。這麼做的原因有兩個:一是記憶體塊不一定能馬上釋放會核心(比如記憶體塊不是位于堆頂端),二是供應用程式下次申請記憶體使用(這是主要原因)。當dlmalloc中空閑記憶體量達到一定值時dlmalloc才将空閑記憶體釋放會核心。如果應用程式申請的記憶體大于256kb,dlmalloc調用mmap()向核心申請一塊記憶體,傳回返還給應用程式使用。如果應用程式釋放的記憶體大于256kb,dlmalloc馬上調用munmap()釋放記憶體。dlmalloc不會緩存大于256kb的記憶體塊,因為這樣的記憶體塊太大了,最好不要長期占用這麼大的記憶體資源。
2.通過p2能夠操作p1,如果之後p1繼續被使用(use after free),則可以達到通過p2修改程式功能等目的。
0x01 一個利用場景
在網上找了一個有UAF漏洞的ctf程式,參考部落格如下:
http://www.syjzwjj.com/use-after-free-tutorial/
作者已經分析的很詳細了,通過對整個程式的調試,來了解UAF的利用。
1. 結合IDA與程式運作,簡要了解程式的功能。
選擇1可以留言,資訊會存到1個連結清單中
選擇2将會周遊連結清單找到對應的節點,列印節點資訊
列印完,可以對其進行删除修改等操作
連結清單節點結構如下
Tips:在逆向代碼中應用資料結構參考《IDA Pro權威指南》第8章資料類型與資料結構
2. UAF漏洞代碼
連結清單節點被删除後,可以繼續進入modify函數,modify函數之後可以繼續進入modify函數。
Delete函數如下:
Delete函數中對節點的進行了free操作,如果在循環代碼中,進行delete操作,釋放節點之後,再選擇2進入modify函數。
Modify函數如下:
Modify函數從使用者讀取資料,然後拷貝到對應的指針中,但此時使用的是一個已經釋放的指針。當輸入content時,會取content的長度作為大小配置設定記憶體,當配置設定記憶體大小等于msg結構大小(48位元組,通過前面的結構獲得)時,會将剛才釋放的記憶體配置設定給content指針。
如下所示
Content指向了msg結構本身
接着将content拷貝到content指針中,即我們的輸入會拷貝到這個被釋放的節點記憶體中。
在循環代碼中,modify完之後可以繼續進入modify。 此時會再對msg結構的author,title,content指針指向的位址進行拷貝。 由于上一步已經能夠對msg結構進行随意更改了,是以将幾個memcpy的目的位址修改成想要的位址即可進行任意記憶體(屬于該程式的合法記憶體)的修改了。
至此我們已經能夠完成任意記憶體位址的修改了。
下面需要考慮的就是完成一些指令執行的利用。
3. 漏洞利用執行指令
要執行指令,需要調用system函數,但是代碼中并沒有system函數,需要如何完成指令執行呢?
可以利用linux的延遲加載功能,改變strlen函數的指向,将原本要執行的strlen,改成執行system。
Tips:延遲加載
當調用标準函數時,需要從其他so檔案中将标準函數加載進來,并不直接調用函數的位址,而是通過一張中間表跳轉到函數的真正位址。
以strlen函數的調用為例
在程式調試中,列印0x804c04c的資訊
整個過程如下:
Call strlen跳轉到strlen函數,裡面隻有一句jmp ds:off_804c04c
當程式運作起來時0x804c04c裡的值為0xb7658210,才是strlen的真正位址
即0x804c04c中存儲libc庫中的strlen的真正位址。
如果将0x804c04c的值改掉,改成system的位址0xb7614360。 雖然看起來調用的是strlen,但真正執行的是system函數。
修改前:
Call strlen
Strlen:
Jmp 0x804c04c
0x804c04c: 0xb7658210(strlen)
修改後:
Call strlen
Strlen:
Jmp 0x804c04c
0x804c04c: 0xb7614360 (system)
Tips: 尋址system的真正位址
由于整個程式并沒有調用system函數,是以在程式的重定位表中找不到system。 是以需要自己定位一下system在這個程式中的真正位址。
Libc被裝到0xb75d6000-0xb777a000 位址空間,大小為0x1a4000
編寫程式調用system函數
調試運作檢視其libc位址空間
被裝入到0xb7e10000-0xb7fb4000,大小也為0x1a4000。
是以system在漏洞程式中的位址應為 =(system在調用程式中的位址-調用程式libc起始位址+漏洞程式libc起始位址)
0xb7e4e360
System在漏洞程式中位址= 0xb7e4e360-0xb7e10000+0xb75d6000= 0xb7614360
4. poc運作效果
執行一個mkdir hack指令建立一個hack目錄
Poc片段
5. 過程回顧
1) delete函數中釋放節點
2) modify函數傳入被釋放的指針
3) modify函數中配置設定記憶體大小可控,通過配置設定與節點相同的大小,取得被釋放記憶體的控制權
4) 修改将要被拷貝的目的位址msg->author指針指向将要被執行的函數strlen的中間表位址
5) 将strlen指向的真實strlen位址,修改為system的真實位址
6) 看似執行call strlen,實則執行了system函數
0x02 總結
在指針釋放後再申請相同大小的記憶體,系統會将釋放的位址進行配置設定,以提高系統運作速度,是以可以修改到被釋放的記憶體資料,如果被釋放的指針繼續被使用,則會造成UAF漏洞。
通過UAF漏洞,可能可以造成一些任意記憶體的修改,結合代碼特點,可能會造成任意記憶體的讀取或者,嚴重的能夠造成任意指令的執行,獲得shell。 取決于被釋放的指針是怎麼使用的。
相關知識點:gdb調試(如何調試fork出來的程式),延遲加載(plt與got),标準函數在記憶體中的定位,UAF修改被釋放指針内容的原因dllmalloc