upload-labs

upload-labs

简介

漏洞环境源码下载

https://github.com/c0ny1/upload-labs

参考

Upload-labs通关手册

配置

一般直接将源码放在  www hdocs wwwroot 网站目录下

image

文件上传思维导图

文件上传分类
image
文件上传思路
image

Pass-01 客户端JS验证绕过

初步测试

上传界面
image
提示说:该文件不允许上传,请上传.jpg|.png|.gif类型的文件,当前文件类型为:.php
image
通过调试判断为本地验证
image

源代码分析(客户端js代码)

<script type="text/javascript">
    function checkFile() {
        var file = document.getElementsByName('upload_file')[0].value;
        if (file == null || file == "") {
            alert("请选择要上传的文件!");
            return false;
        }
        //定义允许上传的文件类型
        var allow_ext = ".jpg|.png|.gif";
        //提取上传文件的类型
        var ext_name = file.substring(file.lastIndexOf("."));
        //判断上传文件类型是否允许上传
        if (allow_ext.indexOf(ext_name) == -1) {
            var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
            alert(errMsg);
            return false;
        }
    }
</script>

关键代码

var allow_ext = ".jpg|.png|.gif";

只允许上传 jpg png gif

Payload

1、burpsuit抓包改包

1、更改后缀,然后burpsuit抓包
image
抓包
image
2、改包上传成功
image
3、测试能否执行和连接
http://192.168.10.11/upload-labs/upload/ma.php
image
菜刀连接
image

2、更改本地js上传或者直接禁用JS

1、Firefox禁用js
image
2、打开调试修改
image

3、上传图片马

直接上传图片马
image

Pass-02 绕过MIME检查(白名单)

初步尝试

尝试上传文件,可以看到是在服务端进行验证
image

查看源代码

服务端代码

$is_upload = false;
$msg = null;
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;

            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}

关键代码

if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) 

在服务端对数据包的MIME进行检查,即判断content-type ;修改content-type绕过:

image/jpeg
image/png
image/gif

Payload

1、上传一句话,burpsuite抓包改包
image
2、抓包后修改content-type为 image/gif
image
3、上传成功,执行成功,菜刀连接
image

知识点之 MIME

MIME简介

全名叫多用途互联网邮件扩展(Multipurpose Internet Mail Extensions),最初是为了将纯文本格式的电子邮件扩展到可以支持多种信息格式而定制的。后来被应用到多种协议里,包括我们常用的HTTP协议。

MIME的常见形式是一个主类型加一个子类型,用斜线分隔。比如text/html、application/javascript、image/png等。

在访问网页时,MIME type帮助浏览器识别一个HTTP请求返回的是什么内容的数据,应该如何打开、如何显示。

文件后缀与MIMU的区别
一个是操作系统中标注文件的,一个是邮件和HTTP协议中用来标注网络数据的。

分类(独立请求和Multipart 类型)
独立请求
类型    描述    典型示例
text    表明文件是普通文本,理论上是可读的语言    text/plain, text/html, text/css, text/javascript
image    表明是某种图像。不包括视频,但是动态图(比如动态gif)也使用image类型    image/gif, image/png, image/jpeg, image/bmp, image/webp
audio    表明是某种音频文件    audio/midi, audio/mpeg, audio/webm, audio/ogg, audio/wav
video    表明是某种视频文件    video/webm, video/ogg
application    表明是某种二进制数据    
application/octet-stream, application/pkcs12, application/vnd.mspowerpoint, application/xhtml+xml, application/xml,  application/pdf,
application/json
Multipart 类型
multipart/form-data
multipart/byteranges
常见类型
图片类型

图片类型是在网页中使用的,唯一被广泛识别以及考虑过web安全的类型:

MIME 类型    图片类型
image/gif    GIF 图片 (无损耗压缩方面被PNG所替代)
image/jpeg    JPEG 图片
image/png    PNG 图片
image/svg+xml    SVG图片 (矢量图)
application/octet-stream

这是应用程序文件的默认值。意思是 未知的应用程序文件 ,浏览器一般不会自动执行或询问执行

text/plain

文本文件默认值。意思是 未知的文本文件 ,浏览器认为是可以直接展示的

text/css

任何一个CSS文件想要在网页上被解释执行就必须为text/css 文件。但是服务器经常不会分辨出使用.css后缀的CSS文件

text/html

所有的HTML内容都应该使用这种类型。XHTML的其他MIME类型(如application/xml+html)现在基本不再使用(HTML5统一了这些格式)。

multipart/form-data

multipart/form-data 可用于HTML表单从浏览器发送信息给服务器。作为多部分文档格式,它由边界线(一个由’–’开始的字符串)划分出的不同部分组成。每一部分有自己的实体,以及自己的 HTTP 请求头,Content-Disposition和 Content-Type 用于文件上传领域,最常用的 (Content-Length 因为边界线作为分隔符而被忽略)。

参考

MIME 类型

Pass-03 上传特殊可解析后缀文件(黑名单)

初步测试

1、上传phpinfo.php失败,服务端对图片进行了后缀验证
image
2、上传houzui.xxx成功,可以判断使用了黑名单策略(不能上传.asp .aspx .php .jsp 后缀的文件),但是执行失败。
image
3、尝试上传 phtml php3 php4 php5 PHP phtm后缀的文件
成功上传 houzui.php3 ,测试能够执行,菜刀连接成功。
image

注意:
例如phtml php3 php4 php5 PHP phtm这些后缀名首先得让服务器支持这些解析为php脚本运行,httpd.conf配置文件(apache/conf/hpptd.conf)中可以查看设置。
image

服务端代码分析

$is_upload = false;
$msg = null;
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;
            }
        } else {
            $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

分析

关键代码
黑名单

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

文件名后缀在黑名单中则不允许上传

if(!in_array($file_ext, $deny_ext)) 

strrchr() 函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符

总结:

服务器端检查流程

服务端先去除文件名后面的点,然后取最后面的点后面的字符(即后缀),将其转换为小写(此处避免了大小写绕过黑名单),去除字符串::$DATA(此处见pass-8),最后去除该后缀首尾的空字符。

服务端采用黑名单机制不允许上传一些特定后缀的文件,本关是通过上传一些特殊可解析后缀( php3 php4 php5 phtml phtm)文件来绕过检查。

Pass-04 上传.htaccess绕过(黑名单)

测试过程

1、上传 houzui.php失败。
image

2、上传houzui.xxx成功,但不能执行;可以判断是黑名单机制

3、上传houzui.php3失败,黑名单可能覆盖了几乎所有的特殊后缀

4、看提示,几乎将所有特殊可执行后缀文件全部过滤掉了
本pass禁止上传

.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

后缀文件!

5、尝试上传 .htaccess文件,文件内容如下:

SetHandler application/x-httpd-php

这样使得所有文件都会解析为php

6、成功上传 houzui.xxx,且执行成功
image

服务端代码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $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");
        $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;
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

与pass-03相似,只是黑名单包含了更多特殊可执行文件

知识点之 .htaccess文件

参拷: apache的.htaccess文件作用和相关浅析

简介

在我们不能修改httpd.conf文件时,我们可以通过修改.htaccess文件;
.htaccess可以看作是httpd.conf的衍生品,它起着和httpd.conf相同的作用,

.htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令。

.htaccess的基本作用

URL重写、自定义错误页面
MIME类型配置
访问权限控制等
主要体现在伪静态的应用
图片防盗链
自定义404错误页面
阻止/允许特定IP/IP段
目录浏览与主页
禁止访问指定文件类型
文件密码保护
启用方式:

1、找到httpd.conf,查找AllowOverride。启用AllowOverride
image
改为

AllowOverride all

2、打开mod_rewrite机制,即还是在.httpd.conf中查找mod_rewrite.so;作用是在httpd.conf外重写配置
image

Pass-05 后缀大小写绕过(黑名单)

测试过程

1、上传houzui.php 失败

2、上传houzui.xxx成功,但执行失败,应该是采用了黑名单机制;

3、上传 .htaccess失败

4、上传 houzui1.Php成功,执行成功,菜刀连接
image

服务端代码分析

$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",".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 = 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;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

分析 与前面相比
缺少

$file_ext = strtolower($file_ext); //转换为小写

没有将后缀转为小写

Pass-06 后缀加空格绕过(黑名单)

测试过程

1、上传houzui.php失败

2、上传houzui.xxx成功,但执行失败,猜测是黑名单机制

3、.htaccess houzui.pHp,过滤了所有特殊可执行文件

4、后缀名后面添加空格绕过
image
image

服务端代码

$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",".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 = $_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

        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;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

分析:与之前相比
缺少

$file_ext = trim($file_ext); //首尾去空

Pass-07 后缀加点绕过(黑名单)

初步测试过程

1、上传 houzui.php失败

2、上传houzui.xxx成功,但执行失败;应该是黑名单机制

3、上传 .htaccess houzui1.Php失败,特殊可解析文件被过滤

服务端代码
$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",".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_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;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

分析与之前不同在于

$file_name = deldot($file_name);//删除文件名末尾的点

没有对后缀名进行去”.”处理,利用windows特性,会自动去掉后缀名中最后的”.”,可在后缀名中加”.”绕过:
image
image

Pass-08 ::$DATA绕过(黑名单)

初步测试过程

1、上传houzhui.php失败,上传houzhui.xxx成功,但执行失败,猜测是黑名单机制。

2、.htaccess houzui.Php 后缀加空格 后缀加点 失败

服务端源代码

$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",".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 = 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;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

与前面不同之处在没有

$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

没有对后缀名进行去”::$DATA”处理,利用windows特性,可在后缀名中加” ::$DATA”绕过:
houzuidata.php::$DATA 生成houzuidata.php <?php @eval($_POST[‘test’]); echo “success”;?>

Payload

image
image
image

知识点之 NTFS ADS简介

NTFS ADS简介

NTFS流全称为NTFS交换数据流(NTFS Alternate Data Streams),ADS的诞生是为了兼容Hierarchical File System 。

HFS–分层文件系统,是由苹果公司推出的文件系统,其工作模式是将不同数据存在在不同的分支文件,文件数据存放在数据分支而文件参数存在资源分支。类似的,NTFS流使用资源派生来维持宿主文件相关的信息。ADS有点类似文件的属性信息一样,依附于文件的传统边界之外。

ADS实列

新建一个文件,命名为 test.txt,该文件即是宿主文件;打开文件,输入内容 “test”.在该目录下执行命令

echo "This is a stream" > test.txt:stream.txt

建立后,cmd不会有任何提示且对于Windows资源管理器来说宿主文件没有发生任何变化,包括其大小,修改时间。这是因为windos下不是所有程序都能支持ADS导致的。同样 dir、type等也不能看到。Notepad能够部分支持ADS,可以打开 test.txt:stream.txt,但是notepad也不能完全支持,另存为时会出现参数错误。

**注意
1、修改宿主文件的内容不会影响流的内容

2、修改流的内容不会影响宿主文件的内容

在测试中发现,如果上传的文件名字为:test.php::$DATA,会在服务器上生成一个test.php的文件,其中内容和所上传文件内容相同,并被解析。假设我们需要上传的文件内容为:
<?php phpinfo();?>**下面是上传是会出现的现象:
上传的文件名 服务器表面现象 生成的文件内容

Test.php:a.jpg 生成Test.php 空
image
image

Test.php::$DATA 生成test.php <?php phpinfo();?>

image

image

Test.php::$INDEX_ALLOCATION 生成test.php文件夹
image

Test.php::$DATA\0.jpg 生成0.jpg <?php phpinfo();?>
image

PS: 上传test.php:a.jpg的时候其实是在服务器上正常生成了一个数据流文件,可以通过notepad test.php:a.jpg查看内容,而test.php为空也是正常的。

根据第二个现象,我们可以bypass一些黑名单验证。

后面我加\0测试的时候是想截断后面的东西,但是发现windows会无视”/””\”这两个符号前面的东西,只识别这俩符号后的字符串。(由于windows把\ /当成了目录,而上传只认识文件名所导致的)

参考 在上传文件后缀名加上::$DATA绕过检测,这个::$DATA是什么东西啊

Pass-09

测试过程

1、上传houzui.php失败,上传houzui.xxx成功,但是执行失败;猜测是黑名单机制

2、上传 .htaccess houzui1.Php houzui.php空格 houzui.php. houzuidata.php::$DATA失败

白盒测试

$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",".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;
            }
        } else {
            $msg = '此文件不允许上传';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

分析关键不同之处在于
这里再相关判断后,保存时使用的文件名称是 $file_name(去除末尾的点后)

$img_path = UPLOAD_PATH . '/' . $file_name;

上传文件名为 houzui.php. .
image
image

Pass-10 双写绕过(黑名单)

初步测试

1、上传houzui.php成功,但是发现后缀的php被替换为了空
image
2、上传houzui.pphphp成功
image

服务端代码

$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","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;
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

如果文件名在黑名单中,则替换其中的字符串为空

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

Pass-11 %00截断绕过 (白名单)

初步测试

上传houzui.php,houzui.xxx失败,应该是采用了白名单机制

服务端代码

$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类型文件!";
    }
}

关键代码

$ext_arr = array('jpg','png','gif');//采用了白名单机制

$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);

 $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

move_uploaded_file($temp_file,$img_path)

文件名直接连接在后面

strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。
注释:strrpos() 函数对大小写敏感。
substr() 函数返回字符串的一部分。

image

抓包看到上传路径在url中,在 <php 5.3.4版本中可以添加 %00 截断

后面加houzui.php%00截断

image
由于本地环境是PHP Version 5.6.28,未能成功上传

知识点之00截断

%00原理 参考 https://www.jianshu.com/p/dad4df0e0bf4
burpsuite上传截断

一、简介

截断的产生核心,就是chr(0)字符 。
下面是用 URL 编码形式表示的 ASCII 字符
image

在url中%00表示ascll码中的0,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束

比如

https://mp.csdn.net/upfiles/?filename=test.txt                                此时输出的是test.txt

加上%00

https://mp.csdn.net/upfiles/?filename=test.php%00.txt                   此时输出的是test.php

这个字符即不为空(Null),也不是空字符(“”),更不是空格!

当程序在输出含有chr(0)变量时,chr(0)后面的数据会被停止,换句话说,就是误把它当成结束符,后面的数据直接忽略,这就导致漏洞产生的原因。

二、影响
2.1 PHP

在php 5.3.4中修复了0字符,但是在之前的版本中仍然危害巨大。

简单测试一下 (PHP 版本<5.3.4)

    <?php
    $data = $_GET["filename"]`;
    echo $data;
    ?>

url中输入xx.php?filename=test.php%00.txt,实际输出为test.php.

常见利用方法:

1.上传时路径可控,使用00截断
2.文件下载时,00截断绕过白名单检查
3.文件包含时,00截断后面限制(主要是本地包含时)
4.其它与文件操作有关的地方都可能使用00截断。
2.2 ASP

在文件上传路径可控时出现:

<%
response.write(request("Filename"))
%> 

传入filename=test.asp%00.txt, 获得参数值是test.asp,asp 会自动截断 %00 后面的内容。

2.3 JSP
<%
String temp=request.getRealPath("/")+request.getParameter("path");
 out.println(temp);
 String ext = temp.substring(temp.lastIndexOf(".") + 1);
 out.println(ext);
%>

传入path=shell.jsp%00.txt 获得后缀是txt,但是操作文件的api是使用 C 实现会导致00截断。因此这个与php相似,利用场景是文件上传 文件下载等。

Pass-12 0x00截断(白名单)

初步测试

1、上传houzui.php houzui.xxx失败,猜测使用白名单机制;
2、抓包查看

image

通过post传进来的,还是利用00截断,但这次需要在二进制中进行修改,因为post不会像get对%00进行自动解码。

0x开头表示16进制,0在十六进制中是00, 0x00就是%00解码成的16进制
image

这里还是有版本的原因未能成功上传。

服务端

$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请求

$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

pass-13 文件头检查绕过(检查内容)

初步测试

image
1、上传houzui.php houzui.xxx失败,猜测使用了白名单

服务端代码

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 = "上传失败";
        }
    }
}

分析:通过读文件的前2个字节判断文件类型,因此直接上传图片马即可,制作方法:

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

image

image

pass-14 突破getimagesize() (检查内容)

初步测试

1、直接上传图片马
image

服务端代码

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        $info = getimagesize($filename);
        $ext = image_type_to_extension($info[2]);
        if(stripos($types,$ext)){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        }
        else{
            $msg = "上传失败";
        }
    }
}

关键代码:

$info = getimagesize($filename);

如果不能访问 filename 指定的图像或者其不是有效的图像,getimagesize() 将返回 FALSE 并产生一条 E_WARNING 级的错误。

$ext = image_type_to_extension($info[2]);
image_type_to_extension — 取得图像类型的文件后缀

这里用getimagesize获取文件类型,还是直接就可以利用图片马就可进行绕过

Pass-15 突破exif_imagetype() (检查内容)

初步测试

直接上传图片木马
image

服务端代码

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;
    }
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        }
        else{
            $msg = "上传失败";
        }
    }
}

关键代码

$image_type = exif_imagetype($filename);

exif_imagetype() 读取一个图像的第一个字节并检查其签名。

Pass-16 二次渲染(检查内容)

初步测试

直接上传图片马
image

服务端代码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $filename = $_FILES['upload_file']['name'];
    $filetype = $_FILES['upload_file']['type'];
    $tmpname = $_FILES['upload_file']['tmp_name'];

    $target_path=UPLOAD_PATH.basename($filename);

    // 获得上传文件的扩展名
    $fileext= substr(strrchr($filename,"."),1);

    //判断文件后缀与类型,合法才进行上传操作
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
        if(move_uploaded_file($tmpname,$target_path))
        {
            //使用上传的图片生成新的图片
            $im = imagecreatefromjpeg($target_path);

            if($im == false){
                $msg = "该文件不是jpg格式的图片!";
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".jpg";
                $newimagepath = UPLOAD_PATH.$newfilename;
                imagejpeg($im,$newimagepath);
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.$newfilename;
                unlink($target_path);
                $is_upload = true;
            }
        }
        else
        {
            $msg = "上传失败!";
        }

    }else if(($fileext == "png") && ($filetype=="image/png")){
        if(move_uploaded_file($tmpname,$target_path))
        {
            //使用上传的图片生成新的图片
            $im = imagecreatefrompng($target_path);

            if($im == false){
                $msg = "该文件不是png格式的图片!";
            }else{
                 //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".png";
                $newimagepath = UPLOAD_PATH.$newfilename;
                imagepng($im,$newimagepath);
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.$newfilename;
                unlink($target_path);
                $is_upload = true;               
            }
        }
        else
        {
            $msg = "上传失败!";
        }

    }else if(($fileext == "gif") && ($filetype=="image/gif")){
        if(move_uploaded_file($tmpname,$target_path))
        {
            //使用上传的图片生成新的图片
            $im = imagecreatefromgif($target_path);
            if($im == false){
                $msg = "该文件不是gif格式的图片!";
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".gif";
                $newimagepath = UPLOAD_PATH.$newfilename;
                imagegif($im,$newimagepath);
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.$newfilename;
                unlink($target_path);
                $is_upload = true;
            }
        }
        else
        {
            $msg = "上传失败!";
        }
    }else{
        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }
}

关键部分

imagecreatefromjpeg — 由文件或 URL 创建一个新图象。
imagecreatefrompng
imagecreatefromgif

imagejpeg
bool imagejpeg ( resource $image [, string $filename [, int $quality ]] )
imagejpeg() 从 image 图像以 filename 为文件名创建一个 JPEG 图像
imagepng
imagegif

unlink() 函数删除文件。若成功,则返回 true,失败则返回 false。

判断了后缀名、content-type,以及利用imagecreatefromgif判断是否为gif图片,最后再做了一次二次渲染。但是在代码审计后发现其实后面的

参考

https://xz.aliyun.com/t/2657

Pass-17

服务端源代码

$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 = '上传失败!';
    }
}

关键部分

rename — 重命名一个文件或目录
unlink - 删除文件

分析、获取文件名、临时文件名文件后缀、采用白名单验证文件后缀是否为图片,然后重命名文件。

测试

直接上传图片马
image

知识点之php中$_FILES变量的用法

$_FILES变量简介

$_FILES: 经由 HTTP POST 文件上传而提交至脚本的变量。类似于旧数组 $HTTP_POST_FILES 数组(依然有效,但反对使用)。详细信息可参阅 POST 方法上传。

$_FILES数组内容

$_FILES[‘myFile’][‘name’] 客户端文件的原名称。

$_FILES[‘myFile’][‘type’] 文件的 MIME 类型,需要浏览器提供该信息的支持,例如”image/gif”。

$_FILES[‘myFile’][‘size’] 已上传文件的大小,单位为字节。

$_FILES[‘myFile’][‘tmp_name’] 文件被上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir 指定。

$_FILES[‘myFile’][‘error’] 和该文件上传相关的错误代码。[‘error’] 是在 PHP 4.2.0 版本中增加的。
下面是它的说明:(它们在PHP3.0以后成了常量)

UPLOAD_ERR_OK 值:0; 没有错误发生,文件上传成功。
UPLOAD_ERR_INI_SIZE 值:1; 上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值。
UPLOAD_ERR_FORM_SIZE 值:2; 上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。(我们可以在form表单中指定input type='hidden' name='MAX_FILE_SIZE' value='附件的最大字节数')
UPLOAD_ERR_PARTIAL 值:3; 文件只有部分被上传。
UPLOAD_ERR_NO_FILE 值:4; 没有文件被上传。
文件的上传过程

文件被上传结束后,默认地被存储在了临时目录中,这时必须将它从临时目录中删除或移动到其它地方,如果没有,则会被删除。也就是不管是否上传成功,脚本执行完后临时目录里的文件肯定会被删除。所以在删除之前要用PHP的 copy()或者move_upload_file() 函数将它复制或者移动到其它位置,此时,才算完成了上传文件过程。

用form上传文件时,一定要加上属性内容 enctype=”multipart/form-data”,否则用$_FILES[filename]获取文件信息时会报异常。

> <input name="myFile" type="file"> > <input type="submit" value="上传文件"> >
默认地,表单数据会编码为"application/x-www-form-urlencoded"。就是说,在发送到服务器之前,所有字符都会进行编码(空格转换为"+" 加号,特殊符号转换为 ASCII HEX 值)。

application/x-www-form-urlencoded 在发送前编码所有字符(默认)

multipart/form-data 不对字符编码。 在使用包含文件上传控件的表单时,必须使用该值。

text/plain 空格转换为 “+” 加号,但不对特殊字符编码。

Pass-18 条件竞争

服务端代码

//index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
    require_once("./myupload.php");
    $imgFileName =time();
    $u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
    $status_code = $u->upload(UPLOAD_PATH);
    switch ($status_code) {
        case 1:
            $is_upload = true;
            $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
            break;
        case 2:
            $msg = '文件已经被上传,但没有重命名。';
            break; 
        case -1:
            $msg = '这个文件不能上传到服务器的临时文件存储目录。';
            break; 
        case -2:
            $msg = '上传失败,上传目录不可写。';
            break; 
        case -3:
            $msg = '上传失败,无法上传该类型文件。';
            break; 
        case -4:
            $msg = '上传失败,上传的文件过大。';
            break; 
        case -5:
            $msg = '上传失败,服务器已经存在相同名称文件。';
            break; 
        case -6:
            $msg = '文件无法上传,文件不能复制到目标目录。';
            break;      
        default:
            $msg = '未知错误!';
            break;
    }
}

//myupload.php
class MyUpload{
......
......
...... 
  var $cls_arr_ext_accepted = array(
      ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
      ".html", ".xml", ".tiff", ".jpeg", ".png" );

......
......
......  
  /** upload()
   **
   ** Method to upload the file.
   ** This is the only method to call outside the class.
   ** @para String name of directory we upload to
   ** @returns void
  **/
  function upload( $dir ){

    $ret = $this->isUploadedFile();

    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->setDir( $dir );
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->checkExtension();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->checkSize();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }

    // if flag to check if the file exists is set to 1

    if( $this->cls_file_exists == 1 ){

      $ret = $this->checkFileExists();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }

    // if we are here, we are ready to move the file to destination

    $ret = $this->move();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }

    // check if we need to rename the file

    if( $this->cls_rename_file == 1 ){
      $ret = $this->renameFile();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }

    // if we are here, everything worked as planned :)

    return $this->resultUpload( "SUCCESS" );

  }
......
......
...... 
};

分析

本关对文件后缀名做了白名单判断,然后会一步一步检查文件大小、文件是否存在等等,将文件上传后,对文件重新命名,同样存在条件竞争的漏洞。可以不断利用burp发送上传图片马的数据包,由于条件竞争,程序会出现来不及rename的问题,从而上传成功:

image