天天看点

[一道蓝鲸安全打卡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