君衍.
- 一、环境搭建
- 二、第一关 前端JS检测后缀
- 1、源码分析
- 2、禁用浏览器JS上传
- 3、burp抓包修改
- 三、第二关 MIME头验证
- 1、源码分析
- 2、burp抓包绕过
- 四、第三关 PHP3绕过
- 1、源码分析
- 2、PHP3绕过
- 五、第四关 .htaccess重写绕过
- 1、源码分析
- 2、.htaccess复写
- 六、第五关 黑名单大小写绕过
- 1、源码分析
- 2、大小写绕过上传
一、环境搭建
注意:本靶场使用php版本为5.2.17才可正常使用。
首先这个靶场用来学习和测试文件上传漏洞的靶场(Web安全实验场)。它提供了一系列的漏洞场景和挑战,让安全研究人员和开发者能够实践和测试文件上传漏洞相关的技术和解决方案。
文件上传漏洞是指应用程序在接收用户上传的文件时,未能充分验证或限制文件类型、大小、执行权限等导致的安全漏洞。攻击者可能通过滥用这一漏洞,上传包含恶意代码或有害内容的文件,从而危害服务器和应用程序的安全。
这个我们使用小皮来提供中间件,源码可以去github上进行下载:
https://github.com/c0ny1/upload-labs
使用Nginx或者Apache中间件即可。同时放入小皮的web目录里面进行访问即可。
即为www目录下面,然后进行访问查看是否完成环境的搭建。
看到如上界面我们即可完成环境的搭建。
二、第一关 前端JS检测后缀
首先我们这里我们首先构造一个一句话木马来供我们来测试,这里我们使用phpinfo函数来进行判断:
创建一个文件为1.php:
<?php phpinfo(); ?>
我们直接上传尝试:
这个我们可以看到回显结果为:该文件不允许上传,请上传.jpgl.png|.gif类型的文件,当前文件类型为: .php
在文件上传时,用户选择文件时,或者提交时,有些网站会对前端文件名进行验证,一般检测后缀名,是否为上传的格式。如果上传的格式不对,则弹出提示文字。此时数据包并没有提交到服务器,只是在客户端通过 js 文件进行校验,验证不通过则不会提交到服务器进行处理。
1、源码分析
下面我们来观察其中的源码进行分析:
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;
}
}
我们这里只查看关键代码可以看到这里使用前端JS进行了检测。详细查看下源码:
# 定义了一个JS函数
function checkFile() {
# 通过document.getElementsByName方法来获取名为upload_file元素
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;
}
}
var ext_name = file.substring(file.lastIndexOf("."));
:提取用户上传文件的后缀名。 使用lastIndexOf()方法找到文件名中最后一个.的位置,然后使用substring()方法获取该位置到字符串末尾的子字符串,即文件的后缀名。if (allow_ext.indexOf(ext_name) == -1) {
:检查用户上传的文件后缀名是否在允许的文件类型列表中。 如果用户上传的文件后缀名不在允许的文件类型列表中(即indexOf()返回值为-1),则弹出提示框告知用户不允许上传该类型的文件,并返回false,表示文件不符合条件,上传操作终止。
2、禁用浏览器JS上传
这里我们可以看到是使用的是前端JS进行过滤的,所以我们可以禁用浏览器的JS解析,我这里使用的火狐浏览器。
这里我们在URL中输入:
about:config
然后搜索:
JavaScript.enabled
我们可以看到这里是true,也就是启用的状态,然后点击:
即为false即为禁用,这下我们继续尝试上传:
即可完成本关的文件上传。
3、burp抓包修改
首先我们使用它规定上传的文件类型进行上传,这里我使用jpg文件来绕过JS的过滤:
然后去上传,绕过JS过滤同时使用burp进行抓包:
这里我们需要注意抓包使用我们真实的IP地址而不是使用127.0.0.1进行抓包
这里我们即可看到我们上传的文件类型为jpg,然后我们在这里将文件类型改为php:
点击放通,即可看到我们上传成功:
我们在upload目录下即可进行执行,可见前端的过滤即可轻松绕过。
三、第二关 MIME头验证
我们同样使用1.php尝试去上传,即可看到:
这里我们看到输出:文件类型不正确,请重新上传。 同时我们点击提示可以看到
本pass在服务端对数据包的MIME进行检查!其实是对我们上传的内容进行了MIME验证,判断上传文件的MIME类型是不是我们的预期类型。
1、源码分析
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
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.'文件夹不存在,请手工创建!';
}
}
首先判断表单是否进行了提交
if (isset($_POST['submit']))
,然后判断服务器上是否存在指定的上传目录UPLOAD_PATHif (file_exists(UPLOAD_PATH))
,再次进行判断上传文件的MIME类型是否被允许的图像类型即为jpeg、png、gif类型if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))
,然后获取上传文件的临时文件路径,构建目标文件的路径,同时包含了上传目录以及上传文件的原始文件名,使用move_uploaded_file
函数完成上传文件的移动,如果文件成功移动,则为true,否则$msg
为上传出错。以及如果上传文件的MIME类型不在允许的图像类型中,那么将$msg
设置为文件类型不正确,请重新上传。同时如果上传目录不存在,将$msg设置为文件夹不存在,手工创建。
其实就是判断HTTP请求中的content-Type头部参数是否在允许上传的类型当中。
这个参数描述了请求中发送的实体的MIME类型,对于文件上传来说,它描述了上传文件的MIME类型。在文件上传的HTTP请求中,Content-Type 头部参数告诉服务器正在上传的文件的类型是什么。
2、burp抓包绕过
这里我们可以来看下php文件上传之后类型为:
我们可以看到类型为:application/octet-stream
这个文件类型是二进制流的默认类型,即为未知文件类型,所以这里也就无法上传成功,我们尝试将文件类型进行修改,改为image/jpeg
即jpeg图片格式,查看是否可以成功绕过:
然后放通查看能否上传成功:
即可看到上传成功。当然我们一样可以上传s.jpg文件然后进行抓包修改为php类型,一样可以上传成功。
四、第三关 PHP3绕过
本人后面在小皮上重新创建了一个网站,然后将网站根目录放入靶场,
将该靶场的PHP版本更改为了5.2版本的来使用。
这里我无法下载PHP5.2的配置包,所以索性就在网上找了个php按在了虚拟机上,害
下面我们来看第三关:
可以看到提示:不允许上传.asp,.aspx,.php,.jsp后缀文件!
1、源码分析
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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 . '文件夹不存在,请手工创建!';
}
}
添加注释来解读该代码:
$is_upload = false;
$msg = null;
// 检查是否有名为 submit 的表单提交
if (isset($_POST['submit'])) {
// 检查上传目录是否存在
if (file_exists(UPLOAD_PATH)) {
// 定义不允许上传的文件后缀列表
$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 . '文件夹不存在,请手工创建!'; // 如果上传目录不存在,设置错误消息
}
}
首先,设置了
$is_upload
和$msg
两个变量,用于标记上传状态和存储错误消息。检查是否有名为submit
的表单提交。检查上传目录是否存在,如果不存在则设置相应的错误消息。定义了不允许上传的文件后缀列表。获取上传文件的原始文件名,并对其进行处理,包括删除末尾的点、获取文件扩展名、转换为小写、去除特殊字符串、去除首尾空格。如果上传的文件后缀不在禁止列表中,则将文件移动到指定的上传目录,并生成一个新的文件名。如果移动文件失败,则设置相应的错误消息。如果上传的文件后缀在禁止列表中,则设置相应的错误消息。如果上传目录不存在,则设置相应的错误消息。
该源码就是添加了黑名单来进行验证,但是这里黑名单并不全,可以使用PHP3,4,5来进行绕过。
上传模块,有时候会写成黑名单限制,在上传文件的时获取后缀名,再把后缀名与程序中黑名单进行检测,如果后缀名在黑名单的列表内,文件将禁止文件上传。
2、PHP3绕过
如果在 apache
可以开启 application/x-httpd-php
在 AddType application/x-httpd-php .php .phtml .php3
后缀名为 phtml 、php3
均被解析成 php 有的 apache 版本默认就会开启。
上传目标中间件可支持的环境的语言脚本即可,如.phtml、php3
。上面经过源码的分析,我们就将1.php改为1.php3来进行上传:
点击上传我们即可发现上传成功。
这里我们通过查看器看到上传后文件的名称,然后我们去文件目录下执行:
可以看到成功被执行,注意,php版本需为5.2.17
五、第四关 .htaccess重写绕过
首先我们来查看提示:
我们看到这里过滤掉了很多后缀,但是没有进行过滤.htaccess,这里首先我们就需要认识.htaccess文件的作用:
.htaccess文件的作用:
htaccess文件时Apache服务中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮助我们实现:网页301重定向、自定义404错误页面,改变文件扩展名、允许/阻止特定的用户或者目录的访问,禁止目录列表,配置默认文档等功能
最常用的作用就是实现一个伪静态页面,SetHandler application/x-http-php
的意思是设置当前目录所有文件都使用php解析,一方面我们文件的后缀更改为html依然可以被当作php进行解析。另一方面,如果在上传目录不进行限制,一旦出现恶意上传.htaccess文件的行为,立刻就会引发安全隐患。无论上传任何文件,只要符合php语言代码规范,就会被当做PHP执行。
1、源码分析
$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 . '文件夹不存在,请手工创建!';
}
}
添加注释解读代码:
$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 . '文件夹不存在,请手工创建!'; // 如果上传目录不存在,设置错误消息
}
}
首先,设置了 $is_upload 和 $msg 两个变量,用于标记上传状态和存储错误消息。检查是否有名为 submit 的表单提交。检查上传目录是否存在,如果不存在则设置相应的错误消息。定义了不允许上传的文件后缀列表。获取上传文件的原始文件名,并对其进行处理,包括删除末尾的点、获取文件扩展名、转换为小写、去除特殊字符串、去除首尾空格。如果上传的文件后缀不在禁止列表中,则将文件移动到指定的上传目录。如果移动文件失败,则设置相应的错误消息。如果上传的文件后缀在禁止列表中,则设置相应的错误消息。如果上传目录不存在,则设置相应的错误消息。
2、.htaccess复写
上传模块,黑名单过滤了所有的能执行的后缀名,如果允许上传.htaccess
。htaccess
文件的作用是 可以帮我们实现包括:文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件扩展名、封禁特定 IP 地址的用户、只允许特定 IP 地址的用户、禁止目录列表,以及使用其他文件作为 index 文件等一些功能。
在 htaccess 里写入
SetHandler application/x-httpd-php
则可以文件重写成 php 文件。要htaccess
的规则生效 则需要在apache
开启rewrite
重写模块,因为apache
是多数都开启这个模块,所以规则一般都生效。
我们在上传.htaccess的内容为:
<FilesMatch "jpg">
SetHandler application/x-httpd-php
</FilesMatch>
首先上传.htaccess文件,再上传恶意的jpg
文件来访问图片从而执行脚本。
然后复制图片的链接来访问:
即可完成文件上传。
六、第五关 黑名单大小写绕过
1、源码分析
$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 . '文件夹不存在,请手工创建!';
}
}
添加注释解读代码:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) { // 检查是否有名为 `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 . '文件夹不存在,请手工创建!'; // 如果上传目录不存在,设置错误消息
}
}
首先,设置了 $is_upload 和 $msg 两个变量,用于标记上传状态和存储错误消息。检查是否有名为 submit 的表单提交。检查上传目录是否存在,如果不存在则设置相应的错误消息。定义了不允许上传的文件后缀列表,包括一些常见的可执行文件和脚本文件,以及 .htaccess 文件。获取上传文件的原始文件名,并对其进行处理,包括删除末尾的点、获取文件扩展名、去除特殊字符串、去除首尾空格。如果上传的文件后缀不在禁止列表中,则将文件移动到指定的上传目录,文件名采用时间戳和随机数以确保唯一性。如果移动文件失败,则设置相应的错误消息。如果上传的文件后缀在禁止列表中,则设置相应的错误消息。如果上传目录不存在,则设置相应的错误消息。
2、大小写绕过上传
我们仔细查看黑名单以及函数会发现忽略了以PHP、phP结尾的后缀名,同时php不区分大小写,所以这里我们直接可以使用大小写进行绕过,将1.php改为1.PHP来进行上传。
然后复制图片链接进行访问,即可执行: