• 复现文件上传四次绕过


    代码

    DOCTYPE html>
    <html lang="zh">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>文件上传章节练习题title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
        <style type="text/css">
            .login-box{
                margin-top: 100px;
                height: 500px;
                border: 1px solid #000;
            }
            body{
                background: white;
            }
            .btn1{
                width: 200px;
            }
            .d1{
                display: block;
                height: 400px;
            }
        style>
    head>
    <body>
        <form method="post" action="upload.php" enctype="multipart/form-data">
            <input type="file" name="file" value=""/>
            <input type="submit" name="submit" value="upload"/>
        form>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    
    header("Content-Type:text/html; charset=utf-8");
    require_once('pclzip.lib.php');
    
    $name = $file['name'];
    $dir = 'upload/';
    $ext = strtolower(substr(strrchr($name, '.'), 1));
    
    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);
                }
            }
        }
    }
    
    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') {                   
            if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
                check_dir($dir);
                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文件!');
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    解析代码
    先上传文件,在建一个上传文件的目录upload,在解压文件,解压完文件后在对解压完的文件进行判断,判断文件里面是不是有除了zip、jpg、gif、png之外的文件,有的话就直接删除。只能上传压缩包,因为若上传png文件的话也会被压缩,然后在进行解压缩。

    代码问题
    1、存在时间竞争型漏洞:若上传的是php文件,是先被解压,再被删除。
    2、没有进行递归删除。

    第一次绕过

    新建一个压缩包,压缩包里面有一张png图片和一个php文件。
    在这里插入图片描述

    web.php文件内容

    aaaaaaa
    
    • 1

    开始上传
    在这里插入图片描述
    查看上传结果
    在这里插入图片描述
    发现php文件已经被删除,只有png文件保留了下来。

    解决方案:
    代码未进行递归删除,导致文件夹内的webshell得以保留。
    只需要将上传的压缩文件中,递归创建一个文件夹,即可上传成功。

    建一个新的文件aaa,在文件aaa里面放一张png图片和一个php文件,再对文件aaa进行压缩。
    再次上传,上传压缩包aaa。

    上传结果:
    在这里插入图片描述
    这就是phpcms最早的头像上传漏洞。这个漏洞影响的不只是phpcms,也包括抄袭其代码的finecms。

    在phpcms出问题以后,finecms偷偷将漏洞修复了,当然修复方法就是直接拷贝了phpcms的补丁。

    发现php文件并没有被删除,因为程序没有进行递归删除,导致该问题的存在。

    修复方法:
    增加递归删除,但是又造成了新的问题。

    第二次绕过

    针对第一次的绕过,进行了递归删除的补丁修复
    得到以下PHP代码

    
    header("Content-Type:text/html; charset=utf-8");
    require_once('pclzip.lib.php');
    
    $file = $_FILES['file'];
    if (!$file) {
        exit("请勿上传空文件");
    }
    $name = $file['name'];
    $dir = 'upload/';
    $ext = strtolower(substr(strrchr($name, '.'), 1));
    
    //递归删除  zip  1   web.php
    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) {
                check_dir($dir);
                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文件!');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    从第二版的代码可以看出,对目录中的所有次级目录进行了递归删除,但是它还是先解压压缩包到本地再进行删除,所以,可以利用这个时间竞争型漏洞来绕过,只要能在被删除之前访问到一次php文件,就能够成功在它不会进行检查和删除的上级目录中生成一个新的php文件。

    在文件上传解压到被删除这个时间差里访问,就能在网站根目录下生成新的php文件,那么新生成的php文件是不会被删除的。

    所以修改我们之前所搭建的环境,将php代码中的第一个function递归删除的函数替换成以下代码。

    重写一个php文件
    web.php

     fputs(fopen('../../../payload.php','w'),'');?>
    
    • 1

    ../可以让文件向上跳一层,../../可以让文件向上跳两层,依次类推。

    使用burp进行抓包,然后将这个包投放到intruder模块中,反复进行发送,访问的数据包同样用intruder进行。

    再次尝试上传一下
    在这里插入图片描述因为这里使用了递归删除,所以上传文件夹不能绕过了。

    上传后发现文件夹中只剩图片了,php代码被删除了。但是这里上传文件夹后代码名字未修改,并且这里是先上传再删除,所以存在一个时间竞争漏洞。

    查看我们之前上传的路径,为

    http://127.0.0.1/xss_location/upload.php/member/1/1/web.php
    
    • 1

    因此需要跳跃三次才能到达跟目录,则代码为

    ');
    
    • 1

    用时间竞争型漏洞直接在根目录下写入我们的一句话木马,这样他递归删除只是删除当前文件夹的,不会检测根目录。尝试一下

    打开burpsuit,上传web.zip并重发到Intruder模块中,准备模拟多次上传。

    用时间竞争型漏洞直接在根目录下写入我们的一句话木马,这样他递归删除只是删除当前文件夹的,不会检测根目录。尝试一下

    使用burp进行抓包,然后将这个包投放到intruder模块中,反复进行发送,访问的数据包同样用intruder进行。

    在这里插入图片描述

    设置访问次数为5000次
    在这里插入图片描述

    在抓取的要访问的目录代码,上传到Intruder模块
    在这里插入图片描述

    payload设置成6000次
    在这里插入图片描述

    两个同时开始start attack。

    在这里插入图片描述
    可以看到,访问成功了,时间竞争漏洞利用成功

    解决方案:
    由于先解压,再删除,导致暴力getshell
    时间竞争拿下webshell

    第三次绕过

    针对第二次的绕过,进行了递归删除的补丁修复
    得到以下PHP代码

    
    header("Content-Type:text/html; charset=utf-8");
    require_once('pclzip.lib.php');
    
    $name = $file['name'];
    $dir = 'upload/';
    $ext = strtolower(substr(strrchr($name, '.'), 1));
    
    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.md5(time(). rand(1000,9999)).'/';
    if (!is_dir($temp_dir)) {
        mkdir($temp_dir);
    }
    
    if ($ext == 'zip') {
            $zip = new ZipArchive;
            if(!$zip->open($file['tmp_name'])) {
                echo "fail";
                return false;
            }
            if(!$zip->extractTo($temp_dir)) {
    
                exit("fail to extract");
            }
    
            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文件!');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    通过观察得到,第三版和第二版的区别在于,使用时间随机生成了临时文件夹,我们不知道文件夹名称的情况下,我们就无法访问到我们的php文件,就算使用时间竞争也没办法了。

    因此我们需要利用一个新的技巧来绕过它。
    可以看到源码中的如果解压失败了,那就会直接退出程序,而不对文件夹进行检测,我们只需要将压缩包中的第一个php文件解压出来,第二个正常文件不被解压出来就可以绕过了。

    如何构造一个出错的压缩包

    如果压缩包是.7z格式,而7zip的容忍度很低,只需要修改第二个文件的crc,就可以让它解压出错了。
    在这里插入图片描述

    使用010editor打开web.7z文件。

    为了让ZipArchive出错,比如,Windows下不允许文件名中包含冒号(:)等特殊符号,
    可以在010editor中将1.txt的deFileName属性的值改成“1.tx:”。
    在这里插入图片描述
    此时解压就会出错,但1.php被保留了下来。
    尝试上传,提示fail to extract

    在这里插入图片描述
    查看服务端,发现php代码成功上传

    在这里插入图片描述

  • 相关阅读:
    React Router 路由守卫
    MAUI Blazor (Windows) App 动态设置窗口标题
    领域驱动设计:分布式架构关键设计10问
    江森自控×实在智能丨RPA一天完成20+人工作量,为制造业赋能
    Linux 编译链接那些事儿(02)C++链接库std::__cxx11::basic_string和std::__1::basic_string链接问题总结
    【C\C++】内存分配 和 动态内存管理方式
    13-JVM调优实战-3
    iPhone取消Siri语音关机是好是坏
    第四章:指令系统
    2023软件测试面试题(亲身经历)
  • 原文地址:https://blog.csdn.net/weixin_53002381/article/details/126336869