文件上傳漏洞防御——圖片寫馬的剔除
最近回顧了一下CasperKid大牛在2011年11月發(fā)布的Upload Attack Framework,非常有感觸,寫得非常好,想深入了解這個漏洞的都推薦看看。
上傳功能常見于圖片的上傳,例如博客頭像設(shè)置,廣告位圖片上傳等。
上傳檢測方法在paper中也寫的比較明朗,這里總結(jié)一下:
1. 客戶端使用JS對上傳圖片做檢測,例如文件大小,文件擴(kuò)展名,文件類型
2. 服務(wù)端檢測,例如文件大小(免得拒絕服務(wù)),文件路徑(避免0x00截?cái)啵夸洷闅v),文件擴(kuò)展名(避免服務(wù)器以非圖片的文件格式解析文件),文件類型(避免修改Content-Type為image/jpeg等),文件內(nèi)容(避免圖片寫馬)
上傳檢測繞過的方法,也總結(jié)一下:
1.客戶端檢測,相當(dāng)于沒有檢測,可以使用HTTP代理例如burp繞過
2.服務(wù)端檢測,一般采用白名單+黑名單的方式,但也極有可能出紕漏。例如大小寫,不在名單內(nèi)的特例,操作系統(tǒng)bt特性(windows系統(tǒng)會自動去掉文件名最后面的點(diǎn)和空格),0x00截?cái)?,服?wù)器文件解析漏洞,最后還有圖片寫馬繞過類型檢測
本篇博客重點(diǎn)講講圖片寫馬的檢測。
我們知道PHP中文件類型的檢測可以使用
1.$_FILES['uploaded']['type'];
2.getimagesize
兩種方式來判斷是否是正常圖片,其實(shí)只要在不破壞圖片文件格式的情況下,就可以繞過檢測
例如使用以下命令,將正常圖片與一句話php木馬綁定在一起生成一個新的文件的方式
copy /b tangwei.jpg+yijuhua.php tangweiyijuhua.jpg
我們查看新生圖片的內(nèi)容,在圖片底端可以看到一句話木馬寫入,如下圖所示
strings tangweiyijuhua.jpg

接下來我們演示這張圖片是否能正常上傳。
試驗(yàn)用到了兩個腳本
1.upload.html 上傳客戶端
Choose an image to upload
2. upload.php 上傳文件處理
這個腳本會檢測文件后綴與文件類型,符合白名單jpeg格式的才允許上傳,并打印出上傳文件的基本信息及顯示圖片。
注意:紅色字體部分可以先注釋掉,下一步演示中會使用到
if (isset($_POST['upload'])){
// 獲得上傳文件的基本信息,文件名,類型,大小,臨時文件路徑
$filename = $_FILES['uploaded']['name'];
$filetype = $_FILES['uploaded']['type'];
$filesize = $_FILES['uploaded']['size'];
print "
File name : $filename
";
print "
File type : $filetype
";
print "
File size : $filesize
";
$tmpname = $_FILES['uploaded']['tmp_name'];
print "
Temp File path : $tmpname
";
$uploaddir='/var/www/upload/';
$target_path=$uploaddir.basename($filename);
// 獲得上傳文件的擴(kuò)展名
$fileext= substr(strrchr($filename,"."),1);
print "
File extension : $fileext
";
$serverip = $_SERVER['SERVER_ADDR'];
//判斷文件后綴與類型,合法才進(jìn)行上傳操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
print $target_path." successfully uploaded !
";
//顯示上傳的圖片
print 'Origin image
';
//使用上傳的圖片生成新的圖片
$im = imagecreatefromjpeg($target_path);
//給新圖片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
print "
new file name $newfilename
";
$newimagepath = $uploaddir.$newfilename;
imagejpeg($im,$newimagepath);
//顯示二次渲染后的圖片(使用用戶上傳圖片生成的新圖片)
print 'New image
';
}else{
print "
Your image was not uploaded.
";
}
}else{
print "
Your image was not uploaded.
";
}
}else{
print "
Your image was not uploaded.
";
}
?>
進(jìn)行上傳操作,我們會發(fā)現(xiàn)寫馬后的圖片也能正常生成預(yù)覽,如下圖所示

帶有一句話木馬的圖片,如果配合文件解析攻擊(將圖片當(dāng)成PHP或HTML等非圖片格式來解析),就能起到webshell的作用。
所以,我們需要剔除掉圖片中惡意代碼部分內(nèi)容, paper中說可以采取二次渲染,剛開始有點(diǎn)被這個名詞嚇到,后來才明白是啥意思,其實(shí)就是根據(jù)用戶上傳的圖片,生成一個新的圖片,然后刪除用戶上傳的原始圖片,將新圖片存儲到數(shù)據(jù)庫中。這個過程,PHP開發(fā)會非常眼熟吧,這個不就是論壇頭像設(shè)置功能中根據(jù)用戶上傳圖片生成縮略圖需求的代碼實(shí)現(xiàn)大綱嘛。
接下來,我們試驗(yàn)一下重新生成的圖片是否還包含惡意代碼
讓我們回到upload.php腳本的紅色字體部分,代碼功能是使用用戶上傳的圖片生成新的圖片,重新命名并在前端顯示
//使用上傳的圖片生成新的圖片
$im = imagecreatefromjpeg($target_path);
//給新圖片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
print "
new file name $newfilename
";
$newimagepath = $uploaddir.$newfilename;
imagejpeg($im,$newimagepath);
//顯示二次渲染后的圖片(使用用戶上傳圖片生成的新圖片)
print 'New image
';
關(guān)鍵函數(shù)imagecreatefromjpeg ,從jpeg生成新的圖片(類似的還有imagecreatefromgif,imagecreatefrompng等),下圖就是生成的新圖片。

查看新圖片的內(nèi)容,會發(fā)現(xiàn)新生成的圖片文件沒有了綁定的一句話木馬,算是成功去掉了圖片寫馬了吧。
paper中說到了對于這種重新生成圖片的防御方法,也有兩種攻擊方式
1. 利用數(shù)據(jù)二義性,構(gòu)造符合圖像數(shù)據(jù)格式的木馬代碼,在paper中只是個思路,不知道現(xiàn)在能不能正確構(gòu)造出來這樣的圖像文件
2. 利用溢出,攻擊生成新圖片的函數(shù),至于什么樣的惡意圖片會使得這個函數(shù)溢出,就不清楚了,希望大牛門指導(dǎo)
總結(jié)一下,對于文件上傳的防御,做好以下幾點(diǎn),就投入與收益來看,應(yīng)該足夠了吧
1. 客戶端初步檢測文件大小,文件擴(kuò)展名,文件類型
2. 服務(wù)端檢測文件大小,文件擴(kuò)展名(為了避免麻煩,可以替換上傳文件的文件名,就是將文件路徑與文件名都寫死),文件類型
3.服務(wù)端根據(jù)用戶圖片生成新的圖片存儲到數(shù)據(jù)庫