一、分析源代碼
// 初始化上傳狀態標記,默認為false,即文件未上傳
$is_upload = false;
// 初始化消息變量,用于存儲錯誤信息
$msg = null;// 檢查是否通過POST方式提交了表單(點擊上傳按鈕)
if (isset($_POST['submit'])) {// 檢查上傳目錄是否存在if (file_exists(UPLOAD_PATH)) {// 定義禁止上傳的文件擴展名列表(主要是可執行腳本類型)$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");// 從表單獲取用戶指定的保存文件名$file_name = $_POST['save_name'];// 提取文件擴展名(用于類型驗證)$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);// 檢查文件擴展名是否不在禁止列表中if(!in_array($file_ext,$deny_ext)) {// 獲取上傳文件的臨時存儲路徑$temp_file = $_FILES['upload_file']['tmp_name'];// 構造目標存儲路徑(上傳目錄 + 文件名)$img_path = UPLOAD_PATH . '/' .$file_name;// 將臨時文件移動到指定目標路徑if (move_uploaded_file($temp_file, $img_path)) { // 移動成功,標記上傳狀態為成功$is_upload = true;}else{// 移動失敗,設置錯誤消息$msg = '上傳出錯!';}}else{// 文件擴展名被禁止時的錯誤消息$msg = '禁止保存為該類型文件!';}} else {// 上傳目錄不存在時的錯誤消息$msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';}
}
簡單概括就是,用戶上傳一個文件并指定保存文件的名稱,再從保存文件的名稱中提取擴展名進行類型驗證,通過驗證則移動目錄。
二、解題思路
這關代碼中兩個重要的函數是pathinfo()函數和move_uploaded_file()函數。
pathinfo()是PHP內置函數,作用是提取文件的目錄名,擴展名,文件名等部分,這一關是提取擴展名,而擴展名是通過提取文件名最后一個點(.)之后的部分。
move_uploaded_file()函數的作用是移動文件路徑,在處理過程中,會自動解析并移除相對路徑符號(如 /.、/..),將路徑轉換為絕對路徑形式。
再看本關判斷流程,pathinfo()函數提取擴展名驗證通過后,交給move_uploaded_file()函數轉移路徑。
假設攻擊者將文件名修改為xx.php/.這種形式,pathinfo()函數提取最后一個點(.)之后的部分為空,則繞過了黑名單的驗證,轉交下一步處理,而move_uploaded_file()函數又會將“/.”當作相對路徑移除,最后文件就保存為了xx.php。
另外,這里的代碼只有黑名單驗證,用大小寫,空格,點號也可以繞過上傳,參考6-9關。
三、解題步驟
1.上傳木馬,bp抓包,將保存名稱改為“xx.php/.”。
2.文件成功繞過上傳,被保存為xx.php。