参数污染 | JSON 注入

Reading time: 11 minutes

参数污染

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks

HTTP 参数污染 (HPP) 概述

HTTP 参数污染 (HPP) 是一种技术,攻击者通过操纵 HTTP 参数来以意想不到的方式改变 Web 应用程序的行为。这种操纵是通过添加、修改或重复 HTTP 参数来完成的。这些操纵的效果对用户并不直接可见,但可以显著改变服务器端应用程序的功能,并在客户端产生可观察的影响。

HTTP 参数污染 (HPP) 示例

一个银行应用程序的交易 URL:

  • 原始 URL: https://www.victim.com/send/?from=accountA&to=accountB&amount=10000

通过插入一个额外的 from 参数:

  • 操纵后的 URL: https://www.victim.com/send/?from=accountA&to=accountB&amount=10000&from=accountC

交易可能错误地计入 accountC 而不是 accountA,展示了 HPP 操纵交易或其他功能(如密码重置、2FA 设置或 API 密钥请求)的潜力。

特定技术的参数解析

  • 参数的解析和优先级取决于底层 Web 技术,影响 HPP 的利用方式。
  • Wappalyzer 这样的工具有助于识别这些技术及其解析行为。

PHP 和 HPP 利用

OTP 操纵案例:

  • 背景: 一个需要一次性密码 (OTP) 的登录机制被利用。
  • 方法: 通过使用 Burp Suite 等工具拦截 OTP 请求,攻击者在 HTTP 请求中重复了 email 参数。
  • 结果: 本应发送到初始电子邮件的 OTP 被发送到操纵请求中指定的第二个电子邮件地址。这个缺陷允许通过绕过预期的安全措施获得未授权访问。

这个场景突显了应用程序后端的一个关键疏漏,该后端处理第一个 email 参数以生成 OTP,但使用最后一个进行发送。

API 密钥操纵案例:

  • 场景: 一个应用程序允许用户通过个人资料设置页面更新他们的 API 密钥。
  • 攻击向量: 攻击者发现通过向 POST 请求附加一个额外的 api_key 参数,可以操纵 API 密钥更新功能的结果。
  • 技术: 利用像 Burp Suite 这样的工具,攻击者构造一个包含两个 api_key 参数的请求:一个合法的和一个恶意的。服务器只处理最后一个出现的参数,将 API 密钥更新为攻击者提供的值。
  • 结果: 攻击者控制了受害者的 API 功能,可能未经授权访问或修改私有数据。

这个例子进一步强调了安全参数处理的必要性,特别是在像 API 密钥管理这样关键的功能中。

参数解析:Flask 与 PHP

Web 技术处理重复 HTTP 参数的方式各不相同,影响其对 HPP 攻击的易受攻击性:

  • Flask: 采用遇到的第一个参数值,例如在查询字符串 a=1&a=2 中优先考虑初始实例而非后续重复。
  • PHP (在 Apache HTTP 服务器上): 相反,优先考虑最后一个参数值,在给定示例中选择 a=2。这种行为可能无意中通过优先考虑攻击者操纵的参数而促进 HPP 利用。

按技术的参数污染

结果来自 https://medium.com/@0xAwali/http-parameter-pollution-in-2024-32ec1b810f89

PHP 8.3.11 和 Apache 2.4.62

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*l_Pf2JNCYhmfAvfk7UTEbQ.jpeg

  1. 忽略参数名称中的 %00 之后的任何内容。
  2. 将 name[] 视为数组。
  3. _GET 不意味着 GET 方法。
  4. 优先考虑最后一个参数。

Ruby 3.3.5 和 WEBrick 1.8.2

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*kKxtZ8qEmgTIMS81py5hhg.jpeg

  1. 使用 & 和 ; 分隔符来分割参数。
  2. 不识别 name[]。
  3. 优先考虑第一个参数。

Spring MVC 6.0.23 和 Apache Tomcat 10.1.30

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*llG22MF1gPTYZYFVCmCiVw.jpeg

  1. POST RequestMapping == PostMapping & GET RequestMapping == GetMapping。
  2. POST RequestMapping & PostMapping 识别 name[]。
  3. 如果 name 和 name[] 同时存在,优先考虑 name。
  4. 连接参数,例如 first,last。
  5. POST RequestMapping & PostMapping 识别带有 Content-Type 的查询参数。

NodeJS 20.17.0 Express 4.21.0

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*JzNkLOSW7orcHXswtMHGMA.jpeg

  1. 识别 name[]。
  2. 连接参数,例如 first,last。

GO 1.22.7

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NVvN1N8sL4g_Gi796FzlZA.jpeg

  1. 不识别 name[]。
  2. 优先考虑第一个参数。

Python 3.12.6 和 Werkzeug 3.0.4 和 Flask 3.0.3

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*Se5467PFFjIlmT3O7KNlWQ.jpeg

  1. 不识别 name[]。
  2. 优先考虑第一个参数。

Python 3.12.6 和 Django 4.2.15

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rf38VXut5YhAx0ZhUzgT8Q.jpeg

  1. 不识别 name[]。
  2. 优先考虑最后一个参数。

Python 3.12.6 和 Tornado 6.4.1

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*obCn7xahDc296JZccXM2qQ.jpeg

  1. 不识别 name[]。
  2. 优先考虑最后一个参数。

JSON 注入

重复键

ini
obj = {"test": "user", "test": "admin"}

前端可能会相信第一个出现的键,而后端则使用第二个出现的键。

键冲突:字符截断和注释

某些字符在前端可能无法被正确解析,但后端会解析它们并使用这些键,这可能有助于绕过某些限制

json
{"test": 1, "test\[raw \x0d byte]": 2}
{"test": 1, "test\ud800": 2}
{"test": 1, "test"": 2}
{"test": 1, "te\st": 2}

注意在这些情况下,前端可能认为 test == 1,而后端则认为 test == 2

这也可以用来绕过值限制,例如:

json
{"role": "administrator\[raw \x0d byte]"}
{"role":"administrator\ud800"}
{"role": "administrator""}
{"role": "admini\strator"}

使用注释截断

ini
obj = {"description": "Duplicate with comments", "test": 2, "extra": /*, "test": 1, "extra2": */}

在这里,我们将使用每个解析器的序列化器来查看其各自的输出。

序列化器 1(例如,GoLang 的 GoJay 库)将产生:

  • description = "Duplicate with comments"
  • test = 2
  • extra = ""

序列化器 2(例如,Java 的 JSON-iterator 库)将产生:

  • description = "Duplicate with comments"
  • extra = "/*"
  • extra2 = "*/"
  • test = 1

或者,直接使用注释也可以有效:

ini
obj = {"description": "Comment support", "test": 1, "extra": "a"/*, "test": 2, "extra2": "b"*/}

Java的GSON库:

json
{ "description": "Comment support", "test": 1, "extra": "a" }

Ruby的simdjson库:

json
{ "description": "Comment support", "test": 2, "extra": "a", "extra2": "b" }

不一致的优先级:反序列化与序列化

ini
obj = {"test": 1, "test": 2}

obj["test"] // 1
obj.toString() // {"test": 2}

浮点数和整数

数字

undefined
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

可以解码为多种表示形式,包括:

undefined
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9.999999999999999e95
1E+96
0
9223372036854775807

可能会导致不一致

参考文献

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks