建议结合 upload-labs 笔记一起看

常见突破方式

客户端验证

  1. 禁用 js 。
  2. 删除、更改 js 或 js 的触发条件。

MIME类型检测

​ 抓包,改 Content-Type 。建议改为 image/gif,方便打组合拳。

服务端扩展名黑名单

  1. 抓住 Windows 的特点:

    • 对大小写不敏感
    • 自动去除扩展名末尾的空格、点、::$DATA

    限制:php-版本号-nts+Apache 无法解析。

  2. 抓住 PHP 的特点:

    • <5.3.4 版本存在 00 截断漏洞
    • user.ini 可覆盖 php.ini
    • move_upload_file 函数会忽略扩展名末尾的 /.
  3. 抓住 Apache 的特点:

    • .phtml、.php2、.php3、.php5、.pht 等可做为 .php 解析,但需要配置文件进行针对性修改
    • .htaccess 文件可覆盖 Apache 配置文件 httpd.conf,也需要配置文件进行针对性修改
  4. 分号截断。修改为如 filename="1.jpg;.php" ,有一些 waf 遇 ; 会识别为请求字段的结束,从而忽略 ; 后面的内容。

  5. 换行。修改为如:

    1
    2
    3
    filename="1.p
    h
    p"

    换行符会在后端程序中自动转换为 \n ,则 filename 的值为 1.p\nh\np ,从而绕过 waf 。

    在 Windows 中,这里的换行符会自动去掉,最终文件名还是 1.php

文件内容检测

PHP 一句话木马:<?php eval($_POST['cmd']); ?>

  1. 图片马绕过。制作:执行命令 copy/b 图片名+文件名 图片马名

  2. 文件幻术绕过。制作:在 <?php ?> 前添加文件幻术 ,并将 .php 改为对应扩展名。

    GIF 文件幻术:47 49 46 38 39 61 (GIF89a)

    注意:

    • 只检测文件头(前2个字节):文件幻术、图片马均可突破
    • 使用 getimagesize 函数:需图片马
    • 使用 exif_imagetype 函数:需图片马

代码逻辑漏洞

  1. 条件竞争
  2. 二次渲染
  3. 其他

数据溢出

​ 请求包中加入大量垃圾数据(数字、字母等字符组合)来干扰 waf 的检测,从而绕过 waf 。

  1. Content-Disposition 与 name 之间加入垃圾数据。
  2. name 与 filename 之间加入垃圾数据。

​ 注意,垃圾数据后面要加上分号,防止吞噬垃圾数据后面的请求字段。

符号变异

​ 如果 waf 会对请求包中的 filename 值进行检测,那么可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
filename="1.php
Content-Type: image/jpeg

filename='1.php
Content-Type: image/jpeg

filename=1.php
Content-Type: image/jpeg

filename=""1.php
Content-Type: image/jpeg

filename="1".php
Content-Type: image/jpeg

​ 根据开发的思路,获取 filename 的值大概率是匹配引号内的值。现在将引号不闭合或者塞入非黑名单的值,那么 filename 的值将被干扰,从而绕过 waf 。

解析漏洞

概述

​ 文件解析漏洞,是指 web 容器在解析文件时将文件解析成脚本文件格式并得以执行而产生的漏洞,攻击者可以利用该漏洞实现非法文件的解析。

Apache

​ Apache 的解析漏洞依赖于一个特性: Apache 默认一个文件可以有多个以点分割的后缀,当最右边的后缀无法识别(不在 mime.types 文件内),则跳过继续向左识别,直到识别到合法后缀才进行解析。像 .rar、.gif 是 Apache 不能识别的,假如上传文件 xxx.php.bb.rar,.rar 不认识,向前解析,.bb 也不认识,向前解析,直到 .php 。

​ 影响版本:

  • Apache 2.0.x <= 2.0.59
  • Apache 2.2.x <= 2.2.17
  • Apache 2.2.2 <= 2.2.8

CVE-2017-15715

​ 上传一个后缀末尾包含换行符的文件(\x0A),可以绕过 Apache 的 FilesMatch 设置,即 xxx.php\x0A 可被解析为 xxx.php 。影响版本 Apache 2.4.0-2.4.29 。

​ 利用时,需要像 POST %00 截断一样使用 Hex 方法在 .php 后加上 \x0A ,访问该文件时为 xxx.php%0A 。

PHP CGI

​ Nginx 默认是以 CGI 的方式支持 PHP 解析的,当访问 http://x.x.x.x/phpinfo.jpg/1.php 这个URL时,配置文件(nginx.conf)中 $fastcgi_script_name 会被设置为 phpinfo.jpg/1.php,然后构造成 SCRIPT_FILENAME 传递给 CGI 。

​ 但 PHP 为什么会接受这样的参数,然后将 phpinfo.jpg 作为 PHP 文件解析呢?这就涉及到 fix_pathinfo 选项了。如果 PHP 中开启了 fix_pathinfo 这个选项,PHP 会认为 SCRIPT_FILENAME 是 phpinfo.jpg ,而 1.php 是 PATH_INFO ,所以就会将 phpinfo.jpg 作为 PHP 文件来解析了。简单来说,由于 Nginx 的特性,只要 URL 中路径名以 .php 结尾,不管该文件是否存在,直接交给 PHP 处理。

​ 注意,新版本 PHP 引入了 security.limit_extensions ,限制了可执行文件的后缀,默认只允许执行 .php 文件,使得该漏洞难以被成功利用。

​ fix_pathinfo 选项对应 phpinfo 中的 cgi.fix_pathinfo ,该值默认是开启的。PHP 里经常要获取当前请求的 URL 路径信息,一般可以通过环境变量 $_SERVER['PATH_INFO'] 获取,而配置文件中的 cgi.fix_pathinifo 选项则与这个值的获取相关。

​ 利用形式:1.jpg 文件内写入 PHP 语句,访问

  • /1.jpg/1.php

  • /1.jpg/.php

  • /1.jpg%00.php

    此方式(%00.php)影响版本:

    • Nginx 0.5.x
    • Nginx 0.6.x
    • Nginx 0.7-0.7.65
    • Nginx 0.8-0.8.37

​ 或者上传一个名为 shell.jpg 的文件,文件内容如下:

1
<?php fputs(fopen('shell.php','w'),'<?php phpinfo(); eval($_POST['cmd']); ?>'); ?>

然后访问 shell.jpg/.php,在当前目录下就会生成一句话木马 shell.php 。

IIS

​ 使用 IIS5.x-6.x 版本的服务器的网站一般比较老,开发语言一般为 asp ,该解析漏洞也只能解析 .asp 文件。

  • 如果网站目录中有一个以 .asp、.asa 结尾为名的文件夹,那么该文件夹下的所有文件都会被当作 .asp 文件来执行。
  • 解析文件名时会将分号后面的内容丢弃,所以可构造如 xxx.asp;.jpg 来绕过黑名单检测。

​ IIS6.x 默认的可执行文件除了 .asp 还包含这三种:xx.asa、xx.cer、xx.cdx ,因为这三个扩展名和 .asp 都是用同一个 asp.dll 文件来执行,此三种扩展名的文件也会解析为 asp 。可在 IIS管理器->处理程序映射 中查看:

​ IIS 7.0/7.5 中也存在 PHP CGI 解析漏洞。

修复方法

  1. 修改 Apache 配置文件,或者添加一个 .htaccess 文件并写入,禁止 .php 等扩展名的文件执行:

    1
    2
    3
    4
    <FilesMatch ~ "\.(php|.php3)$">
    Order Allow,Deny
    Deny from all
    </FilesMatch>

    Files 和 FilesMatch:都是用于匹配文件,对文件的操作进行限制。Files 一般用于单个文件,虽然也支持正则表达式,但一般用完整的单个文件名。FilesMatch 一般用于多个文件,必须用正则表达式。

    Allow 和 Deny 可以用于控制目录和文件的访问授权。常见如:

    Order Deny,Allow
    Allow from All

    注意 Deny,Allow 中间只有一个逗号,也只能有一个逗号,有空格会出错。单词的大小写不限。

    上面设定的含义是“先检查禁止设定,没有禁止的全部允许”,而第二句没有 Deny ,也就是没有禁止访问的设定,允许所有访问。这个主要是用来确保或者覆盖上级目录的设置,开放所有内容的访问权。

    按照上面的解释,下面的设定是无条件禁止访问:

    Order Allow,Deny
    Deny from All

    如果要禁止部分内容的访问,其他的全部开放:

    Order Deny,Allow
    Deny from ip1 ip2

    或者:

    Order Allow,Deny
    Allow from all
    Deny from ip1 ip2

    Apache 会按照 Order 决定最后使用哪一条规则。比如上面的第二种方式,虽然第二句 Allow 允许了访问,但由于在 Order 中 Allow 不是最后规则,因此还需要看有没有 Deny规则,于是到了第三句,符合 ip1 和 ip2 的访问就被禁止了。

    注意,Order 决定的最后规则非常重要,下面是两个错误的例子和改正方式:

    Order Deny,Allow
    Allow from all
    Deny from domain.org

    错误:想禁止来自 domain.org 的访问,但是 Deny 不是最后规则,Apache 在处理到第二句 Allow 的时候就已经匹配成功,根本就不会去看第三句。

    解决方法:Order Allow,Deny,后面两句不动即可。

    Order Allow,Deny
    Allow from ip1
    Deny from all

    错误:想只允许来自 ip1 的访问,但是,虽然第二句中设定了 Allow 规则,由于 Order 中 Deny 在后,所以会以第三句 Deny 为准,而第三句的范围中又明显包含了 ip1 ,所以所有的访问都被禁止了。

    解决方法一:直接去掉第三句。

    解决方法二:

    Order Deny,Allow
    Deny from all
    Allow from ip1

  2. 修改 php.ini 文件,将 cgi.fix_pathinfo 的值设置为 0(慎用)。

  3. 在 Nginx 配置文件中添加以下代码:

    1
    2
    3
    if ( $fastcgi_script_name ~ ..*/.*php ) {
    return 403;
    }

    解释:当匹配到类似 test.jpg/a.php 的 URL 时,将返回 403 错误代码。

  4. 不提供对上传的原文件进行访问,或对访问原文件时显示的内容经过输出程序处理。

  5. 图片单独放一个服务器上,与业务代码数据进行隔离。

  6. 升级 PHP、Apache、Nginx 等的版本。

  7. 设置文件夹访问权限。

  8. 扩展名黑名单、白名单。

  9. 重命名上传文件。

  10. 不暴露上传路径和保存文件名。