目錄
一、代碼審計
二、實踐
三、總結
一、代碼審計
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){//檢查MIME$allow_type = array('image/jpeg','image/png','image/gif');if(!in_array($_FILES['upload_file']['type'],$allow_type)){$msg = "禁止上傳該類型文件!";}else{//檢查文件名$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];if (!is_array($file)) {$file = explode('.', strtolower($file));}$ext = end($file);$allow_suffix = array('jpg','png','gif');if (!in_array($ext, $allow_suffix)) {$msg = "禁止上傳該后綴文件!";}else{$file_name = reset($file) . '.' . $file[count($file) - 1];$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH . '/' .$file_name;if (move_uploaded_file($temp_file, $img_path)) {$msg = "文件上傳成功!";$is_upload = true;} else {$msg = "文件上傳失敗!";}}}
}else{$msg = "請選擇要上傳的文件!";
}
對源代碼進行分析
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
這是個三元運算符,判斷 'save_name' 是否為空?
(A ? B : C? ,如果A成立,則運行B,否則運行C)
如果用戶提交的'save_name'為空,則使用上傳文件的原始文件名,若不為空則使用該參數值作為文件名
if (!is_array($file)) {$file = explode('.', strtolower($file));}
判斷 $file 是不是數組,如果不是數組,則使用 . 進行切分變為數組
比如 cooper.php.jpg,切分為 cooper php jpg 分別對應數組三個元素
$ext = end($file);
從數組中獲取最后一個元素作為擴展名
經過這兩條分析,想想為什么要判斷 'save_name' 參數是否為數組呢?
難道POST提交的不都是字符串嗎?
那么就進行測試
發現請求參數可以直接以數組的方式提交
那么我們能否去構造一個POST請求的save_name參數,將文件后綴名改為jpg,但文件本身還是cooper.php
問題保留一下,接著分析
$file_name = reset($file) . '.' . $file[count($file) - 1];
reset函數為調用第一個元素
拼接文件名:數組第一個元素 + . + 數組最后一個元素
比如文件名為cooper.php,則 $file_name = cooper + . + php
那么如果直接上傳的不是字符串,而是數組會怎么樣?
比如save_name[0] = cooper.php , save_name[2] = jpg
按照拼接方法那我們構造的文件最后的方式為:
$file_name = cooper.php . $file[1]? ?==> cooper.php.空
理論可行,實踐開始!
二、實踐
首先上傳一個帶有木馬的php文件,使用burp抓包
.
將其修改為以下格式
提示上傳成功,那么就來測試一下
試驗成功!
三、總結
復盤一下:先進行代碼審計,然后看到分割數組,思考為什么要進行分割,難道POST可以上傳數組嗎?然后進行實驗,實驗驗證確實可以上傳數組形式。接著代碼審計,發現文件拼接時具有漏洞,那么構造payload進行嘗試,最后成功!