天天看點

[一道藍鲸安全打卡Web分析] 檔案上傳引發的二次注入

藍鲸打卡的一個 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 上傳後的資料庫如下

[一道藍鲸安全打卡Web分析] 檔案上傳引發的二次注入

注意,rename過程中進行查詢時,查詢的結果 result[\'fid\'] = 1,result[\'extension\'] = \'jpg\'

然後進行update,這時執行了構造的SQL語句,資料庫如下

[一道藍鲸安全打卡Web分析] 檔案上傳引發的二次注入

注意這兩行代碼

$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,上傳,資料庫如下

[一道藍鲸安全打卡Web分析] 檔案上傳引發的二次注入

再進行rename,傳入的oldname為 1.jpg,newname為 1.php

進行查詢的結果為 result[\'fid\'] = 1,result[\'extension\'] = \'\'

在最後的過程中,oldname = 1.jpg,newname = 1.php,這樣就把上傳的 1.jpg 變成了 1.php