当前位置: 首页 > 代码审计, 网络安全 > 正文

【三个白帽之一】二次注入+文件名修改导致getshell

0x01 入口:二次注入漏洞

此题入口点是二次注入。
在common.inc.php中可以看到全局进行了转义,这样常规注入少了大部分。遍观代码,输入处没有任何反转义、反解压、数字型等特殊情况,基本可以确定不存在直接的注入漏洞。 看到上传处的代码upload.php:

$name= basename($file["name"]);
$path_parts= pathinfo($name);   
if(!in_array($path_parts["extension"], ["gif", "jpg", "png", "zip", "txt"])) {
    exit("error extension");
}
$path_parts["extension"] = ".". $path_parts["extension"];   
$name= $path_parts["filename"] . $path_parts["extension"];
$path_parts["filename"] = $db->quote($path_parts["filename"]);   
$fetch= $db->query("select * from `file` where
`filename`={$path_parts['filename']}
and`extension`={$path_parts['extension']}");
if($fetch&& $fetch->fetchAll()) {
    exit("file is exists");
}   
if(move_uploaded_file($file["tmp_name"], UPLOAD_DIR . $name)) {
    $re= $db->exec("insert into `file`
          ( `filename`, `view`, `extension`) values
          ( {$path_parts['filename']}, 0, '{$path_parts['extension']}')");
    if(!$re) {
        print_r($db->errorInfo());
        exit;
    }

可见,上传的文件名走过的流程是:
$file['name'] -> pathinfo() –> $path_parts["filename"] -> quote() -> insert
由于经过了pdo的quote方法转义,所以此处也不存在注入。再看到rename.php
$result= $db->query("select * from `file` where `filename`='{$req['oldname']}'");
if($result) {
    $result= $result->fetch();
}   
if(!$result) {
    exit("old file doesn't exists!");
} else{   
    $req['newname'] = basename($req['newname']);
    $re= $db->exec("update `file` set
                `filename`='{$req['newname']}',
                `oldname`='{$result['filename']}'
                where `fid`={$result['fid']}");

根据$req['filename']从数据库里查询到已存在的一行,并调用update语句进行修改。
但这里oldname='{$result['filename']}' 将从数据库里查出的$result['filename']再一次入库,结果造成一个二次注入。

0x02 利用二次操作进行getshell

那么注入有什么用?
这应该是大家拿到题目,想到的第一个问题。这题明显与getshell有关,源码里包含文件上传、文件改名、文件删除等函数。
我们来一个个分析。

首先upload.php是文件上传的操作,但可见上传处对文件进行了白名单验证:

if(!in_array($path_parts["extension"], ["gif", "jpg", "png", "zip", "txt"])) {
    exit("error extension");
}

导致我们无法上传恶意文件。

其次是delete.php,这个文件其实是个烟雾弹,删除操作并不能利用。
再次是rename.php,这里明显是getshell的关键。

$result= $db->query("select * from `file` where `filename`='{$req['oldname']}'");
if($result) {
    $result= $result->fetch();
}   
if(!$result) {
    exit("old file doesn't exists!");
} else{
    $req['newname'] = basename($req['newname']);
    $re= $db->exec("update `file` set
                `filename`='{$req['newname']}',
                `oldname`='{$result['filename']}'
                where `fid`={$result['fid']}");
    if(!$re) {
        print_r($db->errorInfo());
        exit;
    }
    $oldname= UPLOAD_DIR . $result["filename"] . $result["extension"];
    $newname= UPLOAD_DIR . $req["newname"] . $result["extension"];
    if(file_exists($oldname)) {
        rename($oldname, $newname);
    }

最重要的就是后面这5行。
Oldname和newname,有几个特点:
  1. 后缀相同,都是$result[‘extension’]
  2. oldname的文件名来自数据库,newname的文件名来自用户输入

首先后缀相同这个特点,就导致getshell似乎难以完成,如果要getshell那么一定要将“非.php”后缀的文件重命名成“.php”的文件。后缀相同怎么重命名?
除非后缀为空!
所以我们的update型注入就开始派上用场了。通过update型注入,我们可以将数据库中extension字段的值改为空,同时也可以控制filename的值,那么等于说我能控制rename函数的两个参数的值,这样getshell就近在咫尺了。

但还有个坑,这里改名的时候检查了文件是否存在:if(file_exists($oldname))
我虽然通过注入修改了filename的值,但我upload目录下上传的文件名是没有改的。
因为我利用注入将extension改为空了,那么实际上数据库中的filename总比文件系统中真是的文件名少一个后缀。
那么这里的file_exists就验证不过。怎么办?
简单啊,再次上传一个新文件,这个文件名就等于数据库里的filename的值就好了。

所以最后整个getshell的流程,实际上是一个二次注入 + 二次操作getshell.

0x03 具体操作

1.选择文件上传

【三个白帽之一】二次注入+文件名修改导致getshell|二次注入 getshell|熊猫博客

2.rename造成注入:

【三个白帽之一】二次注入+文件名修改导致getshell|二次注入 getshell|熊猫博客

3.上传真正包含webshell的文件x.jpg

【三个白帽之一】二次注入+文件名修改导致getshell|二次注入 getshell|熊猫博客

4.重命名进行getshell:

【三个白帽之一】二次注入+文件名修改导致getshell|二次注入 getshell|熊猫博客

5.成功

【三个白帽之一】二次注入+文件名修改导致getshell|二次注入 getshell|熊猫博客


本文固定链接: http://www.chnpanda.com/927.html | 熊猫博客 | 转载请注明出处,谢谢合作!

本文关键字: ,

【三个白帽之一】二次注入+文件名修改导致getshell:目前有1 条留言

  1. 沙发
    Panda:

    不错,学习了。

    2015-12-05 下午6:37 [回复]

发表评论

亲,不支持纯字母、符号评论哦~