在最近的幾場比賽中,部分賽題牽扯到緩沖區的知識,之前對這塊的知識還不夠特别的了解,是以抽時間來總結一下。
一、什麼是緩沖區機制
首先我們要知道什麼是緩沖區
總的來說,緩沖區是記憶體空間的一部分,在記憶體中預留了一定的存儲空間,用來暫時儲存輸入和輸出等
I/O
操作的一些資料,這些預留的空間就叫做緩沖區;而
buffer
緩沖區和
Cache
緩存區都屬于緩沖區的一種
buffer
緩沖區存儲速度不同步的裝置或者優先級不同的裝置之間的傳輸資料,比如鍵盤、滑鼠等;此外,
buffer
一般是用在寫入磁盤的;
Cache
緩存區是位于CPU和主記憶體之間的容量較小但速度很快的存儲器,
Cache
儲存着CPU剛用過的資料或循環使用的資料;
Cache
緩存區的運用一般是在
I/O
的請求上
緩存區按性質分為兩種,一種是輸入緩沖區,另一種是輸出緩沖區。對于C、C++程式來言,類似cin、getchar等輸入函數讀取資料時,并不會直接從鍵盤上讀取,而是遵循着一個過程:
cingetchar --> 輸入緩沖區 --> 鍵盤
,我們從鍵盤上輸入的字元先存到緩沖區裡面,
cingetchar
等函數是從緩沖區裡面讀取輸入;那麼相對于輸出來說,程式将要輸出的結果并不會直接輸出到螢幕當中區,而是先存放到輸出緩存區,然後利用
coutputchar
等函數将緩沖區中的内容輸出到螢幕上。
cin
和
cout
本質上都是對緩沖區中的内容進行操作。
二、為什麼使用緩沖區機制
減少CPU對磁盤的讀寫次數;CPU讀取磁盤中的資料并不是直接讀取磁盤,而是先将磁盤的内容讀入到記憶體,也就是緩沖區,然後CPU對緩沖區進行讀取,進而操作資料;計算機對緩沖區的操作時間遠遠小于對磁盤的操作時間,大大的加快了運作速度。下面一個圖檔描述的就是這樣的一個過程
提高CPU的執行效率;比如說使用列印機列印文檔,列印的速度是相對比較慢的,我們操作CPU将要列印的内容輸出到緩沖區中,然後CPU轉手就可以做其他的操作,進而提高CPU的效率
合并讀寫;比如說對于一個檔案的資料,先讀取後寫入,循環執行10次,然後關閉檔案,如果存在緩沖機制,那麼就可能隻有第一次讀和最後一次寫是真實操作,其他的操作都是在操作緩存
三、緩沖區的分類
緩沖區分為三大類:全緩沖、行緩沖、無緩沖
- 全緩沖;隻有在緩沖區被填滿之後才會進行
操作;最典型的全緩沖就是對磁盤檔案的讀寫。I/O
- 行緩沖;隻有在輸入或者是輸出中遇到換行符的時候才會進行
操作;這忠允許我們一次寫一個字元,但是隻有在寫完一行之後才做I/O
操作。一般來說,标準輸入流(I/O
)和标準輸出流(stdin
)是行緩沖。stdout
- 無緩沖;标準
不緩存字元;其中表現最明顯的就是标準錯誤輸出流(I/O
),這使得出錯資訊盡快的傳回給使用者。stderr
四、對緩沖區操作的函數(C語言)
标準輸出函數:
printf、puts、putchar
等。
标準輸入函數:
scanf、gets、getchar
等。
IO_FILE:
fopen、fwrite、fread、fseek
等
fflush
函數的作用是清除緩沖區中的内容,如下所示(實驗環境影響差距較大):
輸入
123↙
,程式輸出如下:
一筐蘿蔔➜ test ./test_1
123↙ a = 123, b = 0xa
在程式中添加上
fflush
後
輸入
123↙
,程式輸出如下
一筐蘿蔔➜ test ./test_1 123↙ c↙ a = 123, b = 0x63
可以看出來fflush的效果,那麼現在跟進源碼來看fflush是怎麼處理的
此glibc源碼的版本是2.23
/glibc-2.23/libio/iofflush.c:31
int _IO_fflush (_IO_FILE *fp) {
if(fp == NULL)
return_IO_flush_all ();
else{
intresult; CHECK_FILE (fp, EOF); _IO_acquire_lock (fp); result = _IO_SYNC (fp) ? EOF : 0; _IO_release_lock (fp);
returnresult; } } libc_hidden_def (_IO_fflush)
在這段代碼裡面最關鍵的就是調用了
vtable
中的
_IO_new_file_sync
函數,在這個函數中将标準輸入流(
stdin
)重新整理
/glibc-2.23/libio/fileops.c:867
另一個重要的函數就是
setbuf
和
setvbuf
,這兩個函數都是用來在程式中設定緩沖機制的。具體的用法可以到菜鳥教程上詳細的學習。
setbuf
setvbuf
例子(2019SSCTF攻防賽-pwn)
該程式隻開啟了NX(堆棧不可執行)保護
一筐蘿蔔➜ sscft checksec --file tinypad [*] '/root/sscft/tinypad' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
拖到IDA中分析代碼,main函數一目了然,典型的菜單題
在edit中存在任意長度輸入,可造成堆溢出
int edit_node() {
intv1; // [rsp+8h] [rbp-8h]
intv2; // [rsp+Ch] [rbp-4h] printf("enter the index of the node you want to edit:"); __isoc99_scanf((__int64)"%d", (__int64)&v2); printf("please enter the length of the input:", &v2); __isoc99_scanf((__int64)"%d", (__int64)&v1); getchar(); printf("please enter the contents of the node:", &v1); fread(name[v2], v1, 1uLL, stdin);
returnputs("edit compete!"); }
在delete中存在UAF
int delete_node() {
intv1; // [rsp+Ch] [rbp-4h] printf("enter the index of the node you want to create:"); __isoc99_scanf((__int64)"%d", (__int64)&v1); free(name[v1]);
returnputs("delete complete!"); }
我們利用堆溢出來僞造chunk,如圖所示
然後delete第二個chunk,進而觸發
Unlink-Exploit
成功達到bss段上
name
的區域可控,第二步洩露libc位址,第三步覆寫
malloc_got
位址為後門函數的位址
最後調用create,擷取到shell
可以看出來對該程式漏洞的利用并不難,但是由于該程式沒有設定無緩沖,在pwn遠端的時候沒有回顯,在比賽的時候沒能打通遠端伺服器
我在本地搭建了該題目的環境,nc連接配接的時候同樣是無回顯
經過一番測試之後,發現如果把腳本中所有的
recv
函數都去掉之後,按照順序發送
payload
,最後也能getshell。
完整exp:
總結
雖然在這次比賽中沒能用pwn題得分,但是總結了這次失敗的經驗和教訓,總的來說收獲還是挺大的。
本文如有不妥之處,敬請斧正。