利用場景常見為:能實現任意地址寫、程序沒有打印功能。
目的:代替打印函數泄露信息。
源碼
源碼位置
/usr/include/x86_64-linux-gnu/bits/libio.h
IO FILE結構
從程序運行說起。一個進程啟動之後,內核中會創建PCB進程控制塊,裏面就有一個文件描述符表(一進程一個)。
該表記錄的是該進程 打開的文件描述符fd 和 對應的FILE結構體的地址ptr 。比如我們所熟知的標準輸入/輸出/錯誤,對應的fd分別是0/1/2。這三個文件一般在進程啟動後打開。
ptr指向 file table entry,記錄的是文件讀寫打開模式,包括status flags、file offset、v-node ptr(虛擬文件系統對應的文件節點)。右下角的i-node是磁盤文件系統對應的文件節點。可以理解為PCB建立了一個映射,從ptr出發能找到文件在磁盤文件系統上的位置,進行讀寫等操作。
而 _IO_FILE 就是linux的標準IO庫中用於描述這些文件的結構。該結構在程序執行fopen等函數的時候創建(分配在堆中),open函數返回的fd就指向了這個新創建的 _IO_FILE 結構體。
_IO_FILE結構體的源碼定義就在libio.h文件中:
可以看到結構體中定義了一些狀態位、讀寫buf起止指針、文件描述符等。
_IO_2_1_stdout_結構躰
_IO_2_1_stdout_ 的 _flags 一般結構為:
_IO_MAGIC
| _IO_IS_FILEBUF
| _IO_CURRENTLY_PUTTING
| _IO_LINKED
| _IO_NO_READS
| _IO_UNBUFFERED
| _IO_USER_BUF
對應的值到底是什麽呢,在源碼開頭有定義:(已省去其他宏定義)
也就是:0xfbad2887
在程序無法泄露地址、又能實現任意地址寫的時候,如果能修改這個 _IO_2_1_stdout_ 的一些值(比如 _flags、_IO_write_base、_IO_write_ptr等),讓它指向目的地址,就能在調用puts或printf的時候,泄露出目的地址的值,比如libc地址。
首先是_flags的值,具體設置什麽得參考puts函數的調用過程到底做了什麽檢查,一般為0xfbad18**。
接著_IO_write_base和_IO_write_ptr設置成需要泄露的起止地址就可以了。
調用puts函數
調用puts函數的時候,它是怎麽跟_IO_2_1 stdout_ 聯系起來的呢。這又得去看源碼了。
官網上下載源碼:http://www.gnu.org/software/libc/sources.html
源碼
這裏以glibc-2.27爲例。找到 libio 文件下的 ioputs.c:
可以看到puts函數調用_IO_puts,而_IO_puts又調用了_IO_sputn。
_IO_sputn實際上是一個宏,調用的是 _IO_2_1_stdout_ 的 vtable 中的 __xsputn,也就是 _IO_new_file_xsputn 函數。
這個函數在libio文件下的fileops.c中:
該函數在寫入緩存區的時候調用了 _IO_OVERFLOW 函數,判斷有沒有往緩存區中寫入數據,也就是下面的 _IO_new_file_overflow 函數:
除此之外,
繞過了 _IO_CURRENTLY_PUTTING 的檢查後,調用了 _IO_do_write 函數:
緊接著調用了 new_do_write 函數。可以看到_IO_IS_APPENDING 標誌位需要設置為1(默認為0),否則無法繼續輸出。
到這裏,要是前面的檢查全都繞過了,那就能正常write出我們目標地址的內容了。
思路整理
如果stdout還沒輸出過,_IO_CURRENTLY_PUTTING 的標誌位默認為0,需要設置為1 。
如果已經輸出過就不需要修改了。
下面修改的值都是stdout尚未輸出過的情況。
只改write_base
為了繞過 stdout->_IO_read_end != stdout->_IO_write_base
的檢查,
需要同時修改 _flags 的 _IO_IS_APPENDING 和 _IO_CURRENTLY_PUTTING ,加上 _IO_MAGIC 就是:0xfbad1800(0xfbad2087| 0x1000 | 0x800)。
這時_IO_write_base
可修改成目的地址,調用printf或者puts時就能泄露目的地址。
改write_base和read_end
stdout->_IO_read_end
和 stdout->_IO_write_base
的偏移相同時,兩者仍相等,這樣就不需要修改 _IO_IS_APPENDING 了。
_flags 只用修改 _IO_CURRENTLY_PUTTING ,也就是:0xfbad2800(0xfbad2087 | 0x800)。
利用方法
當 unsorted bin 中的 chunk 和 tcache/fastbin 中的 chunk 重合(同一個)時,fd指向 main_arena 地址。
利用uaf等漏洞,覆蓋低位,爆破stdout的地址,分配到stdout結構體處,劫持stdout結構體、泄露地址。
補充
参考链接