藍鲸打卡的一個 web 檔案上傳引發二次注入的題解和思考
藍鲸檔案管理系統
源代碼位址:http://www.whaledu.com/course/290/task/2848/show
首先在設定檔案裡把所有的輸入都采用 addslashes() 函數進行轉義
upload.php關鍵代碼
将上傳的檔案通過pathinfo()函數分成三個部分,[dirname] [filename] [extension]
然後進行字尾名檢查,拼接後進行addslashes轉義,查詢是否存在這個檔案
if($file["error"] == UPLOAD_ERR_OK) {
$name = basename($file["name"]);
$path_parts = pathinfo($name);
if(!in_array($path_parts["extension"], array("gif", "jpg", "png", "zip", "txt"))) {
exit("error extension");
}
$path_parts["extension"] = "." . $path_parts["extension"];
$name = $path_parts["filename"] . $path_parts["extension"];
$path_parts[\'filename\'] = addslashes($path_parts[\'filename\']);
$sql = "select * from `file` where `filename`=\'{$path_parts[\'filename\']}\' and `extension`=\'{$path_parts[\'extension\']}\'";
$fetch = $db->query($sql);
if($fetch->num_rows>0) {
exit("file is exists");
}
将檔案名和字尾名插入資料庫,将檔案移動到相應檔案夾并傳回路徑
if(move_uploaded_file($file["tmp_name"], ROOT . UPLOAD_DIR . $name)) {
$sql = "insert into `file` ( `filename`, `view`, `extension`) values( \'{$path_parts[\'filename\']}\', 0, \'{$path_parts[\'extension\']}\')";
$re = $db->query($sql);
if(!$re) {
echo \'error\';
print_r($db->error);
exit;
}
$url = "/" . UPLOAD_DIR . $name;
echo "Your file is upload, url:
<a href=\"{$url}\" target=\'_blank\'>{$url}</a><br/>
<a href=\"/\">go back</a>";
} else {
exit("upload error");
}
rename.php關鍵代碼
查詢舊檔案是否存在
if(isset($req[\'oldname\']) && isset($req[\'newname\'])) {
$result = $db->query("select * from `file` where `filename`=\'{$req[\'oldname\']}\'");
if ($result->num_rows>0) {
$result = $result->fetch_assoc();
}else{
exit("old file doesn\'t exists!");
}
更新filename,将oldname和newname重組,查詢oldname是否存在,然後将檔案的oldname更新為newname
if($result) {
$req[\'newname\'] = basename($req[\'newname\']);
$re = $db->query("update `file` set `filename`=\'{$req[\'newname\']}\', `oldname`=\'{$result[\'filename\']}\' where `fid`={$result[\'fid\']}");
if(!$re) {
print_r($db->errorInfo());
exit;
}
$oldname = ROOT.UPLOAD_DIR . $result["filename"].$result["extension"];
$newname = ROOT.UPLOAD_DIR . $req["newname"].$result["extension"];
if(file_exists($oldname)) {
rename($oldname, $newname);
$url = "/" . $newname;
echo "Your file is rename, url:
<a href=\"{$url}\" target=\'_blank\'>{$url}</a><br/>
<a href=\"/\">go back</a>";
}
else{echo $oldname." not exists.";}
}
解題思路
在upload的過程中,全程進行轉義并檢測字尾,無法對上傳進行操作,但是在rename的時候,沒有對newname進行控制,這就可能會造成update的二次注入。
假設我們上傳的檔案是 1.jpg,然後進行改名,這個時候就會觸發資料庫的update語句
update `file` set `filename`=\'newname\', `oldname`=\'1\' where `fid`=fid
很明顯,這裡的newname和oldname都是我們可以控制的。
考慮上傳問題,假設 1.jpg 是一句話木馬,要把 1.jpg 變成 1.php,由于filename和extension分開操作,然後再合并,所有這裡希望extension為空,這樣在rename時可以将 1.jpg 變成 1.php。
構造檔案 \',extension=\'\',filename=\'1.jpg.jpg,上傳,進行rename為 1.php,發現結果為 1.php.jpg
解釋:
檔案 \',extension=\'\',filename=\'1.jpg.jpg 上傳後的資料庫如下
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIx0DciV2dmADM30zd-ckTQ5CRzUCRzUydS9kQsxEe4YVTpZlMkZmWHRmaWdkYohmMkZXVHJGaoJDZsZFSilWOppFMOdVZ0lzUiZnTtxkeO1mYxwGWhNnRtxUdWdUY2UzVa9mTYxUdOdFT650MiVXTYplbGdlYwRmMZNTO5xkNNh0YwIFSh9CXt92YuM3YltWas5iclN3Ztl2Lc9CX6MHc0RHaiojIsJye.png)
注意,rename過程中進行查詢時,查詢的結果 result[\'fid\'] = 1,result[\'extension\'] = \'jpg\'
然後進行update,這時執行了構造的SQL語句,資料庫如下
注意這兩行代碼
$oldname = ROOT.UPLOAD_DIR . $result["filename"].$result["extension"];
$newname = ROOT.UPLOAD_DIR . $req["newname"].$result["extension"];
在這個過程中,oldname=\',extension=\'\',filename=\'1.jpg.jpg,newname=1.php.jpg,由于oldname存在,是以最後變成1.php.jpg
解決
構造檔案 \',extension=\'\',filename=\'1.jpg.jpg,上傳,進行rename為 1.jpg,結果為 1.jpg.jpg
構造另外一個一句話木馬檔案1.jpg,上傳,資料庫如下
再進行rename,傳入的oldname為 1.jpg,newname為 1.php
進行查詢的結果為 result[\'fid\'] = 1,result[\'extension\'] = \'\'
在最後的過程中,oldname = 1.jpg,newname = 1.php,這樣就把上傳的 1.jpg 變成了 1.php