红蓝对抗中HTTP分块传输攻击实战

by LauCyun Oct 28 23:35:23 3,488 views

说明:文章编写于2019年6月10日。

分块传输绕过 WAF 的技能是在年初时从《利用分块传输吊打所有WAF》中学到的,不过没有深入研究。

最近 HW 行动中看到红方有人使用该技能过 WAF,所以总结了对分块传输进行防御的一些心得和方法。

0x0 分块传输

分块传输编码(Chunked transfer encoding)是 HTTP 中的一种数据传输机制,允许 HTTP 由网页服务器发送给客户端应用( 通常是网页浏览器)的数据可以分成多个部分。分块传输编码只在 HTTP 协议 1.1 版本(HTTP/1.1)中提供。

通常,HTTP 应答消息中发送的数据是整个发送的,Content-Length 消息头字段表示数据的长度。数据的长度很重要,因为客户端需要知道哪里是应答消息的结束,以及后续应答消息的开始。然而,使用分块传输编码,数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小。通常数据块的大小是一致的,但也不总是这种情况。

估计都没完全明白啥意思,举个栗子加以说明,如下图所示:


图1 HTTP分块编码(请求)

如图1所示,将数据id=119631509进行分块编码,分成3块:

  • 块1:长度为2,内容是id
  • 块2:长度为0xA(16进制),内容是=119631509
  • 块3:结束块,长度为0(表示编码结束),内容为两个空行(表示数据包结束)。

如果黑客通过分块传输的方式把 Payload 发送至服务器,使得 WAF 无法识别出 Payload,从而达到绕过的效果,如下图所示:


图2 分块传输绕过WAF的原理图 

0x1 红队攻击

1 环境

介绍一下具体攻击环境,具体如下:

  • 靶机(Web服务器)
    • 系统:Windows 2008 r2
    • IP地址:192.168.0.109
    • phpStudy:phpStudy 2018版
      • 中间件:Apache 2.4
      • 数据库:MySQL 5.5.53
      • PHP:5.4.45
    • WAF
      • 名称:网站安全狗
      • 版本:V4.0(Apache版)
    • 注入点:http://192.168.0.109/sqlinject.php
  • 攻击机
    • IP地址:192.168.0.102
    • 工具:
      • HackBar
      • BurpSuite

sqlinject.php源码如下:

<html>
<head>
    <title>SQL Injection Demo</title>
</head>
<body>
    <?php
    if ($_REQUEST['id']) {
        $id = $_REQUEST['id'];
        // Setup the connection to the database
        $mysqli = new mysqli('localhost', 'root', 'root', 'sql_injection_example');
        // Check connection before executing the SQL query
        if ($mysqli->connect_errno) {
            echo "Connect failed: %s\n" . $mysqli->connect_error;
            exit();
        }
        // SQL query vulnerable to SQL injection
        $sql = "SELECT username FROM users WHERE id = $id";
        // Select queries return a result
        if ($result = $mysqli->query($sql)) {
            while ($obj = $result->fetch_object()) {
                echo "<p>Hello " . $obj->username . "! </p>";
            }
        }
        // If the database returns an error, print it to screen 
        elseif ($mysqli->error) {
            echo "Error: " . $mysqli->error;
        }
    }
    ?>
</body>
</html>

数据库sql_injection_exampleusers表的结构和数据如下:

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `first_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `last_name` varchar(50) CHARACTER SET utf8 NOT NULL DEFAULT '',
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `users` VALUES (1,'laucyun','$2a$10$SakFH.Eatq3QnknC1j1uo.rjM4KIYn.o8gPb6Y2YBnNNNY.61mR9K','Lau','Cyun'),(2,'maryjohnson','$2a$10$hA/hwCzhr6F23BsbRZBjdOA5eqTgV01cv30sy/O2EcL2/zG9k0aGy','Mary','Johnson'),(3,'jameswilliams','$2a$10$OkV5tCMMsy91pkkMXHa94OgcunNtuhxsQcxaOW6tJimuaCO0FMDZm','James','Williams'),(4,'lindabrown','$2a$10$2NgAjstT9NcN58zMcF/Rq.pYt5bg3iQ6OmdRgR3YWfT.ZVgmJR4FK','Linda','Brown');

WAF 配置为拦截 URL 和 POST 的and  or注入,如下图所示:


图3 WAF的检测规则

2 SQL注入攻击

利用 HackBar 工具发送 POST 请求 Payload 均被拦截,如下图所示:


图4 WAF成功拦截SQL注入

3 利用分块传输绕过waf

先在 POST 请求头中加入Transfer-Encoding: chunked,然后将数据部分id=1 or 1=1进行分块编码(注意:长度值必须为十六进制数),而且每块里长度值独占一行,数据占一行,如下图所示:

注意:
 1. 分块编码传输需要将关键字andorselectunion等关键字拆开编码,不然仍会被 waf 拦截。
 2. 最后用0表示编码结束,并在0后空两行表示数据包结束,不然点击提交按钮后会看到一直处于 waiting 状态。


图5 分块传输绕过WAF

从上图可知通过对id=1 or 1=1进行分块传输,可以绕过 WAF 检查。

但有一些较好的 WAF(比如:Imperva、360等)可以防御分块传输类攻击,它是把所有的块组合成一个完整的 HTTP 数据包,那上面的方法就会被 WAF 识别并阻断。

正所谓“道高一尺,魔高一丈”,可以通过在每个分块长度标识处加上分号;作为注释即可绕过,如下图所示:


图6 加注释的分块传输

几乎所有能识别分块传输的 WAF,都没处理分块数据包中长度标识处的注释,所以也可以绕过。

推荐使用 @c0ny1 大佬的工具,工具地址:http://github.com/c0ny1/chunked-coding-converter

0x2 蓝队防御

站在防御者的角度,如何检测分块传输呢?

首先,该插件利用的请求是post方法。如果是服务器是get方法的,需要把get请求改为post请求,同时要满足服务器能接受post请求并正确响应。

再者,请求包中消息头带有Transfer-Encoding: chunked,这是分块传输的标志。目前web应用很少能见到分块传输,更多的是断点续传,如果我们没有利用分块传输的web应用,而IDS、IPS或者流量分析系统检测到http请求头有Transfer-Encoding: chunked,则说明有人尝试利用分块传输发动攻击。

最后,消息主体其特征值是分块传输的规则。基本格式是第一行表示下一行内容的长度,第二行则是内容,最末尾倒数第三行为0,然后最后两行为空行。所以要把传输的块拼接起来就是,偶数行拼接。而加入的注释干扰项是在表示下一行长度的这行尾部添加;(数字+字母大小写随机字符串)。

0x3 参考

Tags