文章目录
- JSON 注入攻击 API
- "注入所有东西"是"聪明的"
- 发生了什么?
- 什么是 JSON 注入?
- 为什么解析器是问题所在
- 解析不一致
- JSON 解析器互操作性中的安全问题
- 处理重复密钥的方式不一致
- 按键碰撞响应不一致
- JSON 序列化(反序列化)中的不一致
- 好的。JSON 解析器很烂。我该如何利用它?
- 结论
JSON 注入攻击 API
我想给你讲一个不久前的故事。利用三星设备中的 JSON 注入漏洞可能触发攻击链,最终导致设备上的代码执行。
这听起来像是某些烂黑客电影里的情节。但事实并非如此。
这可以作为一个教训,告诉人们如何滥用盲目信任其有效负载中的 JSON 的 API。
让我们看看吧。
“注入所有东西"是"聪明的”
你知道过去几年物联网是如何风靡一时的吗?如今一切都变得"智能"。从智能灯泡到智能电视,从智能冰箱到智能集线器,从恒温器到摄像头。如果设备有电子设备,供应商就会试图用软件让它"更智能"。
问题是,许多此类设备都处于受限环境中。通常运行某种嵌入式 Linux 来驱动片上系统(SOC)硬件上的代码。这使得其 HTTP 服务器(以及支持库,如 JSON 解析器)受到一定限制。
三星 Smart Hub 就是这种情况。他们的移动应用程序可以与集线器进行远程通信并控制与其连接的任何设备。
该集线器的功能之一是能够使用 RTSP 协议连接到智能摄像头并处理其直播流。此代码在以 root video-core 身份运行的进程中运行。
通过向端点发送恶意 POST 请求 /credentials, 可以修改集线器用于连接远程服务器的凭据,并以导致 SQL 注入并最终导致远程代码执行的方式污染数据。
以 root 身份,在中央控制器上管理网络上的所有物联网。
发生了什么?
此端点在处理 JSON 主体的整个过程中未对参数进行任何清理。此外,三星所依赖的库(json-c)是使用编译的 JSON_TOKENER_STRICT=0,允许使用单引号和双引号定义字符串。
这个小事实将允许攻击者注入任意字段以在中心的内部 sqlite 数据库中创建自定义列。您可以看到这是怎么回事。它以堆叠查询和执行任意代码结束。
这是一个非常有趣的攻击链。实际上可以将过长的 ROP 链插入 camera 表中,然后向端点发送 DELETE /cameras,这最终会导致视频核心进程尝试读取数据并最终崩溃,从而导致传统的基于堆栈的缓冲区溢出。
您可以在此处阅读 Cisco TALOS 的一篇精彩文章。该漏洞成为 CVE-2018-3879,与 CVE-2018-3880 结合后,CVSS 评级为 9.9。
那么,我们在这里学到了什么?
JSON 注入 → SQL 注入 → 缓冲区溢出 → ROP = PWNED
这只是现实世界中的一个示例。还有很多其他示例。但让我们来探索一下为什么这种方法有效,以及我们在攻击 API 时如何利用这种方法。
什么是 JSON 注入?
JSON 注入是一种漏洞,允许攻击者将恶意数据插入 JSON 流,从而可能改变应用程序行为或触发意外操作。
当来自不受信任来源的数据未经过服务器正确清理并被代码直接或间接利用时,就会发生服务器端 JSON 注入。就像三星智能中心的运作方式一样。
有几种方法可以做到这一点,例如通过结构化格式注入(SFI)。我不会详尽地介绍这一点,因为我之前在利用结构化格式注入利用 API 中写过这方面的内容。
我希望你思考的是为什么会发生这种情况。没有单一的答案。它比这更微妙。
而且随着 API 用越来越多不同的语言编写,情况只会变得更糟。
这都是因为 JSON 解析器及其使用方式不一致造成的。
为什么解析器是问题所在
我认为可以说 JSON 已成为大多数 API 通信的支柱。它的简单性在威胁模型中经常被忽视,因为我们非常依赖和信任它们的结构。
然而,在现代 Web 应用程序和 API 中,请求管道中可能会使用多个解析器,每个解析器都有自己的怪癖和漏洞。解析器之间的差异加上多阶段请求处理可能会带来严重的漏洞。
但这是为什么呢?
解析不一致
即使在最好的解析器实现中,与规格的细微偏差也是不可避免的。
JSON 解析器也面临挑战,因为官方 JSON RFC 对重复键和数字表示等主题没有做出明确规定。尽管有关于互操作性的警告,但大多数 JSON 解析器用户并未意识到这些警告。
说实话…在使用库处理数据(反)序列化之前,您上次阅读 RFC 是什么时候?
最重要的是,官方 RFC 并不是唯一的规范。你还有 ECMAScript、JSON5、HJSON,甚至二进制 JSON (BSON)。
这可能让人抓狂。解析器之间的互操作性暴露了许多人甚至没有意识到的安全风险。
让我给你举几个例子。
JSON 解析器互操作性中的安全问题
JSON 解析器之间存在一些互操作性安全问题。BishopFox 对该主题进行了一些出色的研究,我在此总结一下。如果您对这些问题感兴趣,我强烈建议您查看他们关于该主题的研究。
处理重复密钥的方式不一致
我希望你考虑一下这个例子:
fu = {"bar": 1, "bar": 2}
的值是否 fu[“bar”] 等于1或2?或者会产生错误?
根据官方规范,任何结果都是完全可以接受的。如果你总是期望它以特定的方式工作,那就有问题了。
因此,如果您有一个用 Python Flask 编写的前端公开 API,它会使用最后一个键的优先级,结果为 2。
但是,如果该有效负载被转发到后端的单独微服务进行进一步处理,情况会怎样?假设它是用 Golang 编写的。那么,它使用第一键优先,结果为 1。
看到这里的问题了吗?
因此,您需要确保在依赖 JSON 对象的代码中正确理解顺序优先级。否则,这可能会成为一种可能的攻击媒介,您可以通过以特定顺序使用重复键来操纵业务逻辑,使其以意想不到的方式工作。
这就是为什么在侦察过程中检测 API 组件的编程语言非常重要。您需要了解正在使用的语言,并尝试找出 JSON 对象的解析方式。
按键碰撞响应不一致
当解析器以不一致的方式处理特殊字符或注释时,就会发生键冲突。
例如,在 Python 2.x 中,JSON 解析器在处理某些 Unicode 的方式上有所不同。
考虑以下 JSON 块:
{"bar":1,"bar\ud888":2}
默认 JSON 解析器会将其干净地处理为两个不同的键。但是,Python 中流行的 ujson 解析器会截断 Unicode,将键视为重复项,并接受 Python 的最后一个键优先级。结果如何?标准 JSON 解析器将 bar 的值视为 1,而 uJSON 解析器将 bar 的值视为 2。
查看下面的屏幕截图,了解实际操作的示例……
JSON 序列化(反序列化)中的不一致
虽然我们一直在讨论键的优先级,但我们实际上一直在展示反序列化过程中的问题。事实上,这也可能发生在序列化过程中。
有时序列化和反序列化本身是不一致的。
以 Java 的 JSON 迭代器为例……
输入:
fu = {"bar": 1, "bar": 2}
输出:
fu["bar"] // 1
fu.toString() // {"bar": 2}
所以在同一个解析器中,键的检索和序列化的值是不同的。底层数据结构保留了重复键的值,但序列化器和反序列化器之间的优先级不一致。
JSON RFC 不会阻止重复键的序列化。因此,您必须依赖于理解所有组件中数据序列化和反序列化过程中键的顺序优先顺序。
举个例子,C++ rapidjson 解析器会以不同的方式处理相同的数据:
输入:
fu = {"bar": 1, "bar": 2}
输出:
fu["bar"] // 2
fu.toString() // {"bar": 1, "bar": 2}
如您所见,相同的 JSON 对象在解析时可能会得到不同的结果。重新序列化这些对象并不能提供任何保护,因为数据可能会有所不同,从而使攻击者能够绕过清理逻辑偷偷获取值。
这可能导致业务逻辑缺陷、注入漏洞或其他安全风险。
好的。JSON 解析器很烂。我该如何利用它?
希望您能够看到如何滥用 JSON 并注入可能导致应用程序以开发人员未预料的方式运行的数据。除了我之前提到的结构化格式注入之外,当您可以操纵数据如何在 API 基础架构内遍历组件时,您就可以开始控制逻辑流程。
例如,如果您知道 JSON 对象直接序列化到数据库(例如 MongoDB、Couchbase、DynamoDB、CosmosDB 等)并反序列化到使用不同解析器的外部组件,那么就有机会污染数据并查看它如何进出 API。
考虑一个类似这样的 API 请求流程:
输入清理阻止创建管理员POST /user/create HTTP/1.1
...
Content-Type: application/json
{
"user": "dana",
"role": "administrator"
}
HTTP/1.1 401 Not Authorized
...
Content-Type: application/json
{"Error": "Assignment of internal role 'administrator' is forbidden"}
因此我们可以看到输入验证不允许我们将角色设置为管理员。
了解解析器如何处理输入允许您利用解析器的行为以您可以操纵的方式解释数据,从而潜在地绕过输入清理。
想想之前的 Python 示例。想象一下,如果您使用 Unicode 字符截断帐户创建对象中名为"role"的键中的值。突然间,“administrator\ud888"被解析为"administrator”,并且 API 内的 privesc 成为可能。
最终看起来是这样的:
JSON 注入允许绕过清理以获得管理员权限POST /user/create HTTP/1.1
...
Content-Type: application/json
{
"user": "dana",
"role": "administrator\ud888"
}
HTTP/1.1 200 OK
...
Content-Type: application/json
{"result": "OK: Created user 'dana' with the role of 'administrator'"}
这就是 JSON 注入的最高境界,这要归功于奇特的 JSON 解析器。
结论
对三星智能中心的攻击只是 JSON 注入如何导致一系列复杂的漏洞链(从 SQL 注入到远程代码执行)的一个例子。
正如我们所见,根本原因通常在于 JSON 解析器处理数据的方式不一致,尤其是当涉及具有不同怪癖的多个解析器时。这些漏洞凸显了了解 JSON 在 API 基础架构中的不同语言和组件之间解析和处理方式的细微差别的重要性。
通过彻底审查 JSON 对象的序列化、反序列化和处理方式,您可以开始弄清楚如何制作可以绕过清理过滤器并影响业务逻辑的有效负载。