渗透测试之文件上传

前几天上网冲浪时发现了曾经比较火的一个漏洞,是phpcms的头像上传漏洞,感觉挺有意思的,我们来尝试复现一下这个场景。

phpcms对头像上传是这么处理:上传上去的zip文件,它先解压好,然后删除非图片文件。

关键代码:

function check_dir($dir)
{
    $handle = opendir($dir);
    while (($f = readdir($handle)) !== false) {
        if (!in_array($f, array('.', '..'))) {
            $ext = strtolower(substr(strrchr($f, '.'), 1));
            if (!in_array($ext, array('jpg', 'gif', 'png'))) {
                unlink($dir . $f);
            }
        }
    }
}

这段代码会读取我们文件夹中是否会有恶意代码,如果有则删除。

假如我们删除一个压缩包web.zip

这个压缩包里的文件夹是这个格式:

这个web.php就是我们的恶意代码

当我们选择上传后,文件夹中只留下了图片

这就是phpcms最早的防范机制

但是这段代码存在一个漏洞,它并没有递归的检查文件夹,如果我构造这样一个压缩包:

将恶意代码放在一个文件夹中,由于没有递归检查,我们的恶意代码保存了下来

phpcms迅速修复了这个漏洞

function check_dir($dir){
    $handle = opendir($dir);
    while(($f = readdir($handle)) !== false){
        if(!in_array($f, array('.', '..'))){
            if(is_dir($dir.$f)){
                check_dir($dir.$f.'/');
             }else{
                $ext = strtolower(substr(strrchr($f, '.'), 1));
                if(!in_array($ext, array('jpg', 'gif', 'png'))){
                    unlink($dir.$f);
                }
            }
        }
    }
}

他们添加了递归删除功能

原来的方法行不通了,还有什么方法呢

我们来看这段代码:

if (!is_dir($dir)) {
    mkdir($dir);
}

$temp_dir = $dir . 'member/1/';
if (!is_dir($temp_dir)) {
    mkdir($temp_dir);
}

if (in_array($ext, array('zip', 'jpg', 'gif', 'png'))) {
    if ($ext == 'zip') {

        $archive = new PclZip($file['tmp_name']);

        if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
            exit("解压失败");
        }
        check_dir($temp_dir);
        exit('上传成功!');
    } else {
        move_uploaded_file($file['tmp_name'], $temp_dir . '/' . $file['name']);
        check_dir($temp_dir);
        exit('上传成功!');
    }
} else {
    exit('仅允许上传zip、jpg、gif、png文件!');

这段代码的逻辑是先解压上传的压缩包,然后再删除压缩包。

那么我们可以在解压完到还没删除的这一极短的窗口期,访问解压完的文件,并在它的上级目录生成恶意代码

首先我们写这样一个代码:

<?php file_put_contents('../../../web.php','<?php phpinfo();?>');

这个代码的作用是将生成的恶意代码向上跳三级目录,跳到upload这个目录(upload目录是系统自带的目录,一般程序不会检查到这个目录)

我们用Yakit抓包

之后重复发包,我这里设置1000次

然后进这个网址,手动刷新页面

成功

之后phpcms修复了这个漏洞

他们添加了一行代码

$temp_dir = $dir . md5(time().rand(1000, 9999)) . '/';

这行代码使目录变成了随机数,那么上一个方法就行不通了

那怎么办呢

我们看这段代码

当解压失败就退出解压过程

这是一个很平常的思路,解压失败了当然要退出,但我们可以构造一个解压了一半就退出的压缩包。这个压缩包可以解压出部分文件,但会在解压未完成时报错。因而程序停在了这里,这就导致了后面的删除操作都没有了作用。

那么怎么构造一个解压到一半就报错的压缩包呢?

首先我们创建一个文件夹,在文件夹中放入以下文件:

第一个是恶意代码文件,第二个是文本文件(注意顺序不能错,恶意代码文件要在最前面)

然后将文件压缩,用010editor打开

可以看到一共有五部分

点开第四部分

因为windows下不允许文件名中包含冒号 ,因此可以将2.txt后面加一个冒号

此时解压就会出错,但1.php被保留了下来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值