快速上手原型污染漏洞
自我们发现原型污染漏洞以来,关于其本质和利用方式一直存在困惑。本文将讨论在真实环境中识别原型污染漏洞的最简单方法,这些漏洞可能引发各种利用方式!
原型污染可能存在于服务端或客户端。服务端原型污染可用于修改应用程序控制流,利用过程既有趣又有回报。当客户端存在原型污染漏洞时,可被用来实施跨站脚本(XSS)攻击。在这两种情况下,利用原型污染都高度依赖于具体环境。幸运的是,识别这类漏洞通常并不困难。
我们将通过几个示例来展示识别这种漏洞的简易程度,然后深入探讨这些漏洞产生的原因,并研究其他识别和利用方法。
发现服务端原型污染
识别此漏洞的最简单方法之一是向Burp Suite的Repeater发送API方法,并将JSON格式的请求体包装在__proto__
对象中。
下面的示例展示了一个针对虚构披萨餐厅网站的有效请求。用户提供他们最喜欢的披萨类型和电话号码以注册奖励计划。
|
|
在后端,API解析请求体并确保所有必需参数都存在。如果我们发送以下请求,将收到400错误请求响应。
|
|
我们可以利用这种输入验证。然后将有效的请求体包装在__proto__
对象中,如下所示,应用程序返回200 OK响应。
|
|
这看起来可能很奇怪,但这是我们发现此处存在漏洞的第一个指标。此时,请求被接受有两个潜在原因:
- API在请求体中搜索有效请求键
- 正在执行不安全的合并操作(稍后详述)
我们可以发送以下请求来排除第一个选项。如果应用程序正在搜索有效键,那么我们应该能够将__proto__
对象更改为任何其他内容,应用程序将响应200 OK。例如,我们将__proto__
更改为false_positive
。
|
|
由于应用程序拒绝了此请求,我们可以假设__proto__
对象在应用程序中具有特殊含义。可以尝试变体如_proto_
或__proto
,响应都应返回400错误请求。
如果仅在对象名称为__proto__
时收到200 OK响应,那么恭喜,你刚刚发现了服务端原型污染漏洞!稍后我们将深入探讨其工作原理以及如何利用。
测试客户端原型污染
PortSwigger已将自动化原型污染识别和利用功能添加到其浏览器工具DOM Invader中。该工具可以识别接收器和工具,甚至创建概念验证利用!
接收器是代码中可以修改原型对象的位置,例如应用程序未安全处理的URL参数。工具是可以利用污染对象进行利用的位置。DOM Invader使查找接收器和工具变得容易,只需确保拥有最新版本的Burp Suite并按照以下步骤操作:
- 在Burp中打开DOM Invader(Proxy > Intercept > Open Browser)
- 转到浏览器扩展,启用Burp Suite扩展
- 在扩展中打开DOM Invader和原型污染
- 重新加载页面并打开检查器,然后导航到新添加的"DOM Invader"选项卡
- 如果工具识别出接收器,则重新打开扩展并启用工具扫描
- 重新加载并导航回检查器的"DOM Invader"选项卡。顶部应看到进度条。如果为先前找到的接收器识别出任何工具,则应看到生成概念验证利用的选项
DOM Invader扩展在搜索客户端代码方面非常强大和有效,这些代码通常被压缩且在真实环境中难以阅读。如果你对手动方法感兴趣,强烈建议查看PortSwigger Academy的原型污染课程。
原型污染的工作原理
那么,为什么__proto__
对象很特殊?如果你熟悉面向对象编程,“继承"这个词应该很耳熟。当创建新对象时,它们会从其类以及任何父类获得属性。在JavaScript中,存在"原型"对象的概念,这本质上是所有对象继承的根父对象。
在JavaScript中,原型对象是可写的,甚至在运行时也是如此。如果向原型添加任何属性,那么每个新创建的对象都将具有该属性。这允许我们修改开发人员从未打算(或期望)我们控制的变量!
考虑一个服务端合并函数,它获取一个对象的属性并在另一个对象中更新它们。你可能希望保存目标对象中存储的一些属性,仅更新源对象中描述的值。此外,包含在其他对象中的对象需要以相同方式复制。下面的代码片段可能是此问题的解决方案。
|
|
这里的漏洞可能不明显。如果攻击者包含一个__proto__
键,其值设置为对象,递归不仅会写入当前对象的原型,还会写入全局对象的原型。这意味着所有新创建的对象都将继承攻击者定义的属性。其后果仅受攻击者想象力的限制。
服务端原型污染利用
由于无法涵盖所有可能的情况,我们仅看几个如何利用此漏洞的示例。
建立环境
在利用原型污染时,环境至关重要。利用服务端任何内容的困难在于通常无法看到另一侧的情况。首先,尽可能进行发现。使用Gobuster和SecLists中的发现词表来查找服务器上的隐藏文件和位置。如果幸运,可能会找到源代码仓库。如果项目本身是开源的,你就可以开始深入挖掘了。
由于全局原型将在线程的整个生命周期中被污染,你可能能够利用应用程序使用的开源库。尝试通过模糊测试强制服务器返回错误消息。我最喜欢的工具选择是Burp Suite Intruder和wfuzz中提供的词表。攻击完成后,可以按长度和状态代码对响应进行排序,甚至可以搜索错误消息关键词。
收到的错误消息可能会返回部分(如果不是详细的话)堆栈跟踪,这可以帮助你映射后端源代码。此外,如果能够识别应用程序使用的任何库,你可能能够利用源提供的环境。
示例
作为示例,假设应用程序在收到POST请求时执行以下步骤:
- 服务器检查请求头中提供的会话令牌并接受或拒绝请求。用户ID存储在线程内存中供以后使用
- URL与更新用户配置文件的函数匹配
- 函数(不安全地)将请求数据合并到对象中以保存用户提供的数据
- 服务器验证所有必需属性是否包含
- 应用程序根据数据库中存储的信息创建用户对象
- 对用户对象执行权限检查,其中仅对管理用户设置属性
- 函数通过写回用户数据库并返回200 OK来完成其例程
在这种情况下,攻击者可以简单地向__proto__
对象添加管理属性,从而提升请求的权限。请记住,在利用原型污染后创建的每个对象都会受到影响。
结论
随着JavaScript API的普及、语言本身的奇特特性以及漏洞的非明显性质,我认为我们尚未完全发现其危险性和利用潜力。
最后,我想向你推荐一些资源,这些资源将帮助你进一步探索这个主题。Olivier Arteau就这个主题写了一篇精彩的研究论文,其中包括从真实JavaScript库中发现和利用的示例。
Changhui Xu关于这个主题的另一篇精彩文章,非常清晰地描述了问题并提供了更多示例。
这两个先前来源都被官方CWE引用:不当控制的对象原型属性修改(‘原型污染’)。
PortSwigger有一个关于服务端原型污染的新课程(截至本文撰写时),可在其学院网站上获得。
最后,我为上面部分描述的示例创建了一个GitHub仓库。该示例大约100行代码,在一个文件中,没有依赖项,可以在Node.js或浏览器控制台中运行。可以将其用作参考或实验场。
快乐黑客, Isaac