uploads-lab靶场闯关
0x01 Pass-01 前端检测
源代码
function checkFile() {
//这里getElementsByName取name的值,,判断表单里是否选择了文件
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
// substring()表示返回一个从里面的参数位置开始到末尾的字符串
// lastIndexOf()表示 返回'.'首次出现的位置,如果未发现此字符串返回 -1
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name + "|") == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
Get到javascript的知识点:
onsubmit="return false;" 将无论何时都阻止表单的提交
onsubmit="return check();" 是否提交表单取决于check()的返回值
onsubmit="check();" check()的返回值无影响
null:代表声明了一个空对象,不是一个字符串,可以赋给任何对象,是没有地址。
"": 代表声明了一个对象实例,这个对象实例的值是一个长度为0的空字符串,是有地址但是里面的内容是空的。
绕过方法
- 在控制台修改前端js代码,在allow_ext中 添加 '.php' 即可绕过
- 在前端界面审查元素处,删除检测上传的js函数,或者浏览器禁止js运行
- 上传shell.jpg,用burpsuit抓包,修改后缀为shell.php 即可绕过
0x02 Pass-02 MIME类型 检测
源代码
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) { //upload_file 已经在别的文件里定义好了,表示'../upload/'
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)) {//把临时文件移到UPLOAD_PATH目录
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}
将content-type修改为 image/jpeg
image/png
image/gif
其中一个即可
0x03 Pass-03
源码
$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 . '文件夹不存在,请手工创建!';
}
}
- 阅读源码发现,通过自定义的deldot函数进行末尾去点处理,如果没有对末尾的点进行处理,则可以利用windows特性上传后自动去除文件后缀名的点
通过源码可以发现是 检测黑名单后缀,所以这里可以通过将后缀改为.php3 .phtml
GET Apache 相关知识点
.htaccess
一般来说,配置文件的作用范围都是全局的,但 Apache 提供了一种很方便的、可作用于当前目录及其子目录的配置文件—— .htaccess
(分布式配置文件)
要想使 .htaccess
文件生效,需要两个条件:
1. Apache的配置文件中,要写上: AllowOverride All
2. Apache要加载mod_Rewrite模块。加载该模块,需要在Apache的配置文件中写上:LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so
.htaccess文件内容:
AddType application/x-httpd-php xxx
<FilesMatch "shell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
- httpd.conf
如果想要apache可以将xxx的后缀当作php来执行 .php3 .phtml .xxx等后缀,需要在apache下的httpd.conf配置:
AddType application/x-httpd-php .php3 .phtml
这时,服务器会将php3, phtml
后缀的文件当成php
解析。
- mime.types
有一个名为mime.types的文件,其中记录着Apache认识的后缀
可以修改mime.types的配置 让apache 将不常用的后缀解析为php
application/x-httpd-php .php .php3 .phtml
GET windows特性知识点
测试结果
可以成功将phtml文件解析
通过重写.htacess文件 来绕过黑名单(出现点小问题)
由于黑名单没有过滤掉 .htaccess
,所以我们能够上传.htaccess
文件导致其后上传的其他文件能够解析成 php,从而拿到shell ,
问题就在这里出现了,发现上传后的 .htaccess被改写为 2019***.htaccess,所以这里并不能直接上传htaccess
发现源码使用随机数对文件名进行重写
0x04 Pass-04 (.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.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
?>
黑名单过滤了一堆后缀名,但唯独没有过滤 .htaccess
于是上传一个 .htaccess文件,内容:
表示将所有文件名包含jpg的,都作为php解析
<FilesMatch "jpg">
SetHandler application/x-httpd-php
</FilesMatch>
上传成功
再上传一个shell.jpg看能否执行
可以正常执行,说明 .htaccess 成功上传
方法二:
因为apache 由右向左👈解析的特性, 因此可以上传一个它不认识的后缀来绕过黑名单检测,比如 shell.php.aaaaaa ,这样可以绕过黑名单后缀让文件成功上传,又因为apache的特性,可以成功以php来解析
0x05 Pass-05 后缀名大小写绕过
查看源码可以知道是 根据黑名单后缀名检测,且没有对大小写进行统一转换,所有这里可以利用 pHP 等来绕过
0x06 Pass-06 空格绕过黑名单
源码
$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 . '文件夹不存在,请手工创建!';
}
}
通过源码发现,和前面几关相比,这关并没有对上传的文件名进行首位去除空白字符(或者其他字符)
所以可以利用windows的特性,末尾加空格后会自动去掉空格
于是上传 'shell.php ',这个后缀名刚好可以绕过黑名单,并且上传上去空格自动去掉,可以被解析
另外的思路
看到一篇博客上的作者说,可以用 'shell.php. ' 绕过,但是我分析之后发现是不行的,因为这里的源码会将上传文件重命名,首先它会将最后一个'.' 到 末尾的字符串 认为是后缀名, '.'的前面的字符会生成随机数,所以最后上传上去的文件应该是 'xxxx. ' ,又因为windows特性,所以 文件变成 'xxxx'
0x07 Pass-07 (没有对后缀名末尾去除.)
阅读源码发现,没有删除文件名末尾的点,所以这里又是利用windows的特性
上传 shell.php.
别的思路
- 发现没有对文件进行冲命名,所以这里可以上传 '.htaccess.' 'shell.php.xxxxxxx'
0x08 Pass-08(利用windows NTFS $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");
$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小特性也不能利用,对'.' 空格 d等 做了过滤
GET知识点 Windows NTFS $DATA特性
当我们访问 a.php:: $DATA时,就是请求 a.php本身的数据
::DATA之前的文件名
上传成功, 访问 123.php
0x09 Pass-09 .[空格]. 绕过黑名单
源码
$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 . '文件夹不存在,请手工创建!';
}
}
通过阅读源码发现,
这里上传后的文件名用的还是未完全过滤之前的,前面几关采用了拼接经过strrchr()
处理过的函数名,而这一关并没有。因为源码中只进行了一次点号的清除,且我们至需要让经过过滤后的后缀名不在黑名单即可,所以我们直接采用xxx.php.[空格]. 就可以进行绕过。
测试结果
方法二
依旧采用 apache特性👈由右向左解析, 上传 'xxx.php.rrrr' ,绕过黑名单
0x10 Pass-10 双写绕过黑名单
源码
$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 . '文件夹不存在,请手工创建!';
}
}
阅读源码可以发现,这里将把上传的文件名中所有包含黑名单字符的,全部替换为空,所以这里可以利用双写来绕过, xx.p[php]hp,
测试结果
0x11 Pass-11 00截断
源码
$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类型文件!";
}
}
这里先参考下这个链接
浅显的意思就是在url中%00表示ascii码中的0,而ascii码中0作为保留字符,表示字符串结束,所以当url中出现%00时就会认为读取已结束
所以在burpsuit中作出以下修改
最后成功上传2.php文件
上传思路分析
1.这里是因为上传路径可控,所以才可以对路径进行截断
2.当前版本是5.2 存在%00截断问题 (php 5.3 不可以)
GET 到的知识点--> %00和%00(urldecode)
在网上常见用Burp将数据包中的%00进行urldecode的操作,那为什么要进行这一个操作?网上也常见直接放入%00就可以截断成功的案例,为什么呢?
- 首先解释为什么要进行urldecode操作呢?其原因在于上传的表单中有一个
enctype
的属性,并且需要enctype="multipart/form-data"
(不对表单中数据进行编码),path
大多数都是存放在表单中的,因此需要在数据包中进行urldecode操作使%00
变成字符串结束符号。
- 那么为什么网上也有直接添加
%00
而不进行urldecode操作呢?因为path也可以存放在URL或者Cookie中,而在提交数据的时候,浏览器会对数据做一次urldecode的操作,而到服务端,会对数据进行一次urldecode的操作,因此如果path在非enctype=multipart/form-data
的表单中或URL or Cookie中的时候,就可以直接写%00
不需要进行URLdecode操作,让服务端对%00
进行URL解码即可。
- %00截断的使用是在路径上
GET到的知识点--> 00截断利用条件
PHP<5.3.4,且GPC关闭
GET到的知识点--> 误区
有很多朋友喜欢在文件名中加%00
进行截断,笔者认为这种方式是不对的,为什么呢?比如攻击者构造文件名:admintony.php%00a.jpg
,在提取后缀名的时候遇到%00
则认为字符串结束了,那么他提取到的后缀名会是.php
,.php
后缀又不允许上传所以上传失败了(这里有必要提一句,有人可能会说在一些情况下,%00截断文件名可以成功,这种案例你试一下是不是任意文件上传,西普的00截断实验就是一个任意文件上传的上传点,既然是任意文件上传又何必用00截断绕过呢?)
0x12 Pass-12 POST类型 截断
这关和上一关基本一致,这里就不详细说了,附上测试结果
0x13 Pass-13 -15文件头类型检查
13关源码
function getReailFileType($filename){
$file = fopen($filename, "rb");//fopen() 函数打开一个文件或 URL。
$bin = fread($file, 2); //只读2字节 fread() 函数读取打开的文件。
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 = "上传出错!";
}
}
}
通过阅读源码发现,getReailFileType() 只会读取文件的前两个字节
14关
这题一样使用getimagesize
函数判断文件类型,但是这个函数也是检查文件头,是不可信的。
15关
exif_imagetype
的作用是判断一个图像的类型 ,其实是读取一个图像的第一个字节并检查其签名。
方法一:
使用winhex等编辑器 打开图片文件,将webshell直接插入末尾
方法二:
直接上传webshell,使用burpsuit 拦截并添加图片的文件头
方法三:
使用cmd命令行 copy 生成图片马
方法四: php 读文件
若是服务器采用文件头验证, 则可以先上传一个 php文件读文件 readfile() file_get_contents() highlight()
0x16 Pass-16 二次渲染
源码
$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的图片文件!";
}
}
这里有个函数imagecreatefromjpeg
,作用是由文件或 URL 创建一个新图象。
原理:
将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片 部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的。
先知上有人针对16关专门开了一帖分析,分析得十分详细:upload-labs之Pass16
也可以参考国外大神,二次渲染与绕过方法参考连接: https://secgeek.net/bookfresh-vulnerability/
0x17 Apache 换行解析漏洞(CVE-2017-15715)
影响版本:
2.4.0~2.4.29版本
源码
<?php
if(isset($_FILES['file'])) {
$name = basename($_POST['name']);
$ext = pathinfo($name,PATHINFO_EXTENSION);
if(in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'])) {
exit('bad file');
}
move_uploaded_file($_FILES['file']['tmp_name'], './' . $name);
}
可见,这里用到了黑名单,如果发现后缀在黑名单中,则进行拦截。
利用CVE-2017-15715,上传一个包含换行符的文件。注意,只能是\x0A
,不能是\x0D\x0A
,所以我们用hex功能在1.php后面添加一个\x0A
:
然后访问/1.php%0A
,即可发现已经成功getshell:
总结
研究这个漏洞的过程中遇到几个问题:
获取文件名时不能用
$_FILES['file']['name']
,因为他会自动把换行去掉,这一点有点鸡肋默认的Apache配置即可利用,因为默认Apache配置就使用了``:
<FilesMatch \.php$> SetHandler application/x-httpd-php </FilesMatch>
所以理论上,只要用正则来匹配后缀进行php解析的Apache就有这个问题。而这个做法刚好是为了解决Apache老的解析漏洞而做的,可谓非此即彼,必然存在一种解析漏洞。
原理
正则中$可以匹配行尾或一个换行符,所以可以上传一个后缀末尾包含换行符的文件,来绕过FilesMatch。当然绕过FilesMatch不一定就能被PHP解析,所以这个漏洞也需要看情况,比较鸡肋
0x18 Pass-18 条件竞争
源码
*过滤代码:*unlink()
函数作用是删除文件。若成功,则返回 true,失败则返回 false。
$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 = '上传出错!';
}
}
上面源码的代码逻辑:
- 首先接收上传的文件
- 然后判断上传的文件的文件后缀是否在白名单中
- 在白名单中,则将文件重命名
- 不在白名单则删除文件
通过不断发送请求包,直到访问shell.php成功为止
0x19 常见中间件解析漏洞
一:IIS 服务器
01:IIS<=6.0 解析漏洞
起因是解析标准不一致,即Web应用程序与IIS服务器对同一个文件的文件名称(类型)理解不一致造成。
*** 利用方法有两种:**
1. 畸形目录解析/xxxx.asp/xxx.jpg
2. 分号文件解析test.asp;.jpg
- 第1种是因为 xxx.jpg文件在某个以.asp结尾的目录下,而被IIS当成可执行文件来解析
- 第2中虽然以.jpg结尾,但是IIS解析时忽略了';'分号后面的.jpg,因此就当成test.asp来处理
02: IIS 7.0 & 7.5 畸形解析漏洞
默认fast-cgi
开启状况下,在一个文件路径后面加上/xx.php会将原来的文件解析为php文件
*** 利用方法有两种:**
- 上传一张图片马 test.jpg,然后访问test.jpg/.php或test.jpg/abc.php 可以被执行。
二:nginx
01:畸形解析漏洞
默认fast-cgi开启状况下,在一个文件路径后面加上/xx.php会将原来的文件解析为php文件
和 IIS7.0&7.5畸形解析漏洞一样,上传图片马test.jpg,访问/test.jpg/.php或test.jpg/abc.php 可以被执行
02:空字节代码执行漏洞
在fast-cgi关闭的情况下,nginx版本:0.5., 0.6., 0.7- 0.7.65, 0.8 -0.8.37,nginx在图片后附加 php代码然后通过访问
xx.jpg%00.php
意思是指在上传了一个图片马之后,通过访问 xx.jpg%00.php 来执行php代码
03:文件名逻辑漏洞(CVE-2013-4547)
受影响的nginx版本: 0.8.41至1.4.3和1.5.7之前的1.5.x正常上传一个附加代码的图 片"test.jpg",访问时后面+"空格"+"\0"+".php",即让图片作为php文件解析
"/test.jpg \0.php"
三:Apache 解析漏洞
apache 是从右到左开始判断解析文件名,如果为不可识别的后缀名,就再往左判断,直到可以解析为止,如
test.php.jpegxtxt.xtie 解析成 test.php