作者:Lxxx
聲明:筆者初衷用于分享與普及網(wǎng)絡(luò)知識,若讀者因此作出任何危害網(wǎng)絡(luò)安全行為后果自負(fù),與合天網(wǎng)安實(shí)驗(yàn)室及原作者無關(guān),本文為合天網(wǎng)安實(shí)驗(yàn)室原創(chuàng),如需轉(zhuǎn)載,請注明出處!
前言
最近在復(fù)盤SQL注入,看到有一個trick挺有意思的,這個trick主要利用后端程序會將文件exif信息插入數(shù)據(jù)庫進(jìn)行SQL注入
因此本篇文章會從題目解題,源碼分析,底層調(diào)試這幾個方面入手,如有紕漏,請多包涵。
什么是exif?
為了方便后面文章介紹這個trick,首先我們需要了解什么是exif?
其實(shí)exif是可交換圖像文件的縮寫,是專門為數(shù)碼相機(jī)的照片設(shè)定的,可以記錄數(shù)碼照片的屬性信息和拍攝數(shù)據(jù)。
例如生活中利用手機(jī)相機(jī)或者數(shù)碼相機(jī)拍照的時候,exif會記錄拍照時的一些屬性,例如:光圈,焦距,拍照時間,拍照設(shè)備等等屬性
題目解題
題目地址:你沒見過的注入
題目地址獲取:后臺回復(fù)“注入”
首先打開題目,前面一些與本文無關(guān)的解題步驟就不贅述了,簡要的概括一下就是:掃目錄掃到robots.txt,進(jìn)入修改密碼頁面,修改完成密碼之后進(jìn)入后臺。
后臺還是比較簡單的,就是一個上傳的頁面
選擇文件進(jìn)行上傳即可,并且進(jìn)行測試后發(fā)現(xiàn),上傳的文件都沒有被過濾,即使是上傳同一個文件,文件名也是不一樣的,因此猜測這里使用了隨機(jī)數(shù)md5的方式進(jìn)行文件命名
這一題文件名被隨機(jī)數(shù)md5重命名,并且,所以文件名不是注入點(diǎn),但是分析一下上方的列目錄的文件,首先肯定是有數(shù)據(jù)庫存儲相關(guān)文件信息(上圖中的filetype),因此查詢一下php有哪些函數(shù)或者方法會有這樣的功能,查詢PHP官方手冊后可以發(fā)現(xiàn),其中finfo對象以及finfo_file函數(shù)是有這個功能的
因此就可以大膽猜測在filetype會存在SQL注入,并且SQL語句應(yīng)該是insert開頭的插入語句
那么如何控制這個filetype呢?
我們可以使用file命令查看一個文件的信息,如下圖所示
這一個命令一出是不是就發(fā)現(xiàn)和上面那個頁面的filetype十分相似了呢?
那么是否有工具可以控制這個filetype呢?有的,那就是exiftool
安裝完成后,我們嘗試在一個圖片里加入SQL語句,這里我們假設(shè)題目的SQL語句如下(這個很需要經(jīng)驗(yàn),不過這里我就直接給出來了,做題的時候還是需要慢慢fuzz)
insert into columns('字段1','字段2','字段3') value('值1','值2','值3')
因此注入語句
123"';select if(1,sleep(5),sleep(5));--+
具體的exiftool的命令為
exiftool -overwrite_original -comment="123"');select if(1,sleep(5),sleep(5));--+" avatar.jpg
利用exiftool添加comment之后,使用file命令查看文件信息
可以看到,命令已經(jīng)成功注入到了comment中,上傳該圖片,就可以發(fā)現(xiàn)明顯有延遲,所以命令注入成功。
之后拿flag就很簡單了,利用into outfile寫入一句話木馬即可,這里就不贅述了。
那么,究竟是什么函數(shù)會導(dǎo)致這樣的SQL注入呢?
抱著深究的心態(tài)把題目源代碼拷貝下來進(jìn)行分析(所有可以getshell的題目都可以把題目拿下來進(jìn)行分析,可以學(xué)到更多的東西)
題目源代碼分析
首先一些登錄文件就不看了,直接看最重要的,會造成SQL注入的那幾個文件(為了防止篇幅過長,我這里僅將關(guān)鍵代碼進(jìn)行展示)
$filename = md5(md5(rand(1,10000))).".zip";
$filetype = (new finfo)->file($_FILES['file']['tmp_name']);
$filepath = "upload/".$filename;
$sql = "INSERT INTO file(filename,filepath,filetype) VALUES ('".$filename."','".$filepath."','".$filetype."');";
- 上方代碼第一行就是前面說的,將隨機(jī)數(shù)md5存儲文件
- 第二行這里使用到了finfo::file,這里便是我們的注入點(diǎn)
- 第三行是目錄的拼接
- 第四行就是存在SQL注入的SQL語句
if(MySQLi_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
echo "<li>";
echo "filename:<a href='".$row["filepath"]."'>".$row["filename"]."</a> filetype:".$row["filetype"]."<br>";
}
echo "</li>";
}
這一段代碼就比較簡單,就是將存儲的文件名列出來
那么造成注入的罪魁禍?zhǔn)拙褪沁@一行代碼:
$filetype = (new finfo)->file($_FILES['file']['tmp_name']);
這里使用finfo::file方法,這個方法在PHP手冊介紹如下,但是并不是很詳細(xì),后半部分將會對這一個函數(shù)進(jìn)行底層代碼跟蹤分析。
finfo::file底層跟進(jìn)
finfo::file方法在ext/fileinfo/fileinfo.c中
其中finfo中有這么幾個方法:
class finfo
{
/** @alias finfo_open */
public function __construct(int $flags = FILEINFO_NONE, ?string $magic_database = null) {}
/**
* @param resource|null $context
* @return string|false
* @alias finfo_file
*/
public function file(string $filename, int $flags = FILEINFO_NONE, $context = null) {}
/**
* @param resource|null $context
* @return string|false
* @alias finfo_buffer
*/
public function buffer(string $string, int $flags = FILEINFO_NONE, $context = null) {}
/**
* @return bool
* @alias finfo_set_flags
*/
public function set_flags(int $flags) {}
}
我們跟進(jìn)finfo::file
我們在下方圖中位置下三個斷點(diǎn)
將前面題目拉下來的源文件放在一個文件夾中進(jìn)行調(diào)試
源文件如下:
將upload.php修改如下
$filename = md5(md5(rand(1,10000))).".zip";
$filetype = (new finfo)->file($_FILES['file']['tmp_name']);
$filepath = "upload/".$filename;
var_dump($filetype);
die(0);
開啟調(diào)試,上傳注入文件后程序便會停止在斷點(diǎn)處
第一個斷點(diǎn)處,421行,這個斷點(diǎn)處調(diào)用包裝器打開資源并返回流對象
第二個斷點(diǎn)處,431-432行,進(jìn)入magic_stream,單步調(diào)試,監(jiān)視ms以及ret_val
進(jìn)入file_or_stream
直接看file_or_stream的return
跟進(jìn)file_getbuffer,下方其實(shí)就可以看到ms->o.buf已經(jīng)獲取到了exif信息
后面的就不繼續(xù)跟進(jìn)了,但是可以肯定的是file()方法可以檢測圖片的EXIF信息,并且作為題目中的filetype傳入數(shù)據(jù)庫造成注入
$filetype = (new finfo)->file($_FILES['file']['tmp_name']);
總結(jié)
雖然是幾年前的trick,但是每弄清楚一個trick,攻擊面就會更廣。
參考文章
- CTFshow 36D Web Writeup – 穎奇L'Amore (gem-love.com)
- PHP回顧之流 - SegmentFault 思否