服务端原型污染:无需导致DoS的黑盒检测技术
服务端原型污染很难在不导致拒绝服务(DoS)的情况下进行黑盒检测。本文介绍了一系列安全的检测技术,并已实现为开源的Burp Suite扩展。您很快就可以在我们新的Web Security Academy主题中的交互式、故意易受攻击的实验环境中亲自尝试这些技术。
这项研究曾在NullCon Berlin 2023和OWASP Global AppSec Dublin 2023现场展示。
引言
检测服务端原型污染是一个巨大的挑战。其工作原理的本质可能会半永久性地破坏服务器的功能。本文展示了如何通过无害的请求检测原型污染,这些请求会导致响应中的细微差异,从而证明攻击成功。
如果您想亲自尝试本文中提到的技术,我们构建了一些Web Security Academy实验环境,帮助您磨练原型污染技能。
我们将快速回顾什么是原型污染及其发生方式。如果您已经熟悉基础知识,可以跳到“DoS问题”部分。
什么是服务端原型污染?
原型污染是一种漏洞,当JavaScript库在两个或多个对象上执行递归合并而未首先清理键时发生。这可能导致攻击者获得修改全局原型(如Object原型)的能力。攻击者可能利用此修改来控制后来在接收器中使用的“gadget”属性。根据接收器的功能,这可能使攻击者能够在服务端执行任意代码。
原型继承
JavaScript遵循原型继承系统,使用原型(对象)扩展其他对象。该原型从对象的构造函数继承,继承一直持续到JavaScript引擎到达null原型,表示原型链的结束。JavaScript中的几乎所有对象都通过String、Array或Number等子对象从Object.prototype继承。
JSON.parse()
原型污染的一个常见原因是JSON.parse()。通常,当您创建对象obj时,obj.__proto__是一个getter/setter,引用obj.constructor.prototype。但是,当您使用JSON.parse()时,__proto__的行为类似于常规JavaScript属性,没有特殊的getter/setter。
易受攻击的库
原型污染漏洞最可能发生在具有合并对象方法的JavaScript库中。一个这样的库是Lodash,它有一个名为merge()的方法,接受目标对象和源对象。如果您可以控制源对象的__proto__属性,则可能导致原型污染。
原型污染的影响
原型污染可能导致应用程序配置、行为的改变,甚至导致远程代码执行(RCE)。已有各种关于原型污染的公开报告。其中突出的是Michał Bentkowski在Kibana中的错误和Paul Gerste在Blitz框架中的错误。这两者都导致了远程代码执行。
DoS问题
在测试客户端原型污染时,只需刷新浏览器即可移除对Object原型的修改。但服务端原型污染不同。一旦修改了全局原型之一,此更改将在Node进程的整个生命周期内持续。这意味着如果您破坏了站点的核心功能,可能会阻止应用程序为您和所有其他访问者正确工作。某些向量甚至可以完全关闭Node进程。
即使您没有导致DoS,由于您无法像在客户端运行时那样访问控制台中的错误消息,也很难知道您的污染尝试是否成功。为了可靠且安全地测试服务端原型污染,我们需要一系列非破坏性技术,这些技术仍会触发服务器行为的明显和可预测变化。
导致DoS的检测方法
这些方法是在我寻找原型污染技术的过程中创建的。它们不应用于测试您不拥有的实时站点,因为它们可能导致DoS。
编码
我发现的第一个检测原型污染的方法是使用encoding属性。我通过修补自己的Node版本来查找正在读取的属性。当我尝试污染此属性时,整个Node进程关闭。
构造函数
接下来,我决定采用不同的方法,不是修改Object.prototype,而是修改Object构造函数。
Expect
之后,我开始寻找更细微的检测原型污染的方法。此时,我已经熟悉检查Node和调试应用程序。我查看了请求头,发现expect头很有趣。如果我能控制它,我将能够获得417“expectation failed”响应,这将是一个很好的检测方法。
请求体重写
如果不在某处涉及XSS,这就不是我的帖子,今天也不例外。我想知道是否可以通过原型污染将响应更改为XSS负载。经过一段时间的测试,我找到了一种利用测试应用程序的方法。
手动测试的安全检测方法
我们已经看到了失败的尝试,但如何有一些非破坏性的方法来检测原型污染。回顾一下:我们不想关闭服务器,不想破坏功能,理想情况下我们希望能够打开和关闭它。
在本节中,我将记录用于手动检测原型污染的有用技术。这些技术范围有助于确认漏洞或与其他攻击类(如缓存中毒)结合使用。
参数限制
我的第一次成功尝试是使用Express的参数限制功能。使用此选项,您可以设置查询字符串中允许的最大参数数。
忽略查询前缀
Express有一个名为ignoreQueryPrefix的选项。通过设置此选项,Express将允许您在参数名称中使用问号,并完全忽略它。它也不太可能破坏站点,因为站点不太可能在参数名称中使用问号。
允许点
这是Express中另一个迷人的选项,允许您从查询字符串参数创建对象。当此选项打开时,您可以在参数名称中放置点以构造对象。
内容类型
在2000年代初期,UTF-7在Web安全中是一件大事,因为您可以使Internet Explorer和其他浏览器使用字符编码呈现网页。您还可以将脚本呈现为UTF-7,这真的很有趣。我认为通过滥用UTF-7与Express,我可以带回那些美好的时光。通过污染内容类型,您可以使Express将JSON呈现为UTF-7,即使使用UTF-8字符集提供服务。
安全自动检测方法
作为这项研究的一部分,我发布了一个开源工具,实现为Burp Suite扩展,以查找服务端原型污染。在本节中,我将解释如何发现它使用的技术及其工作原理。
JSON空格
到目前为止,我已经非常擅长评估特定技术是否是一个好的检测方法,我找到的第一个好候选是使用Express中的json spaces选项。此选项允许您控制JSON属性之间的间距。这很好,因为向JSON响应添加额外的空格不太可能破坏站点功能。
暴露头
在找到第一个检测原型污染的好技术后,我查看了其他常见模块。CORS似乎是一个好目标,因为许多应用程序会使用此API向其JSON端点添加CORS配置。我很快使用我的自定义Node版本(或我喜欢称之为Node Invader)找到了有趣的属性。其中一个突出的属性是exposedHeaders属性。此属性允许您定义哪些头在Access-Control-Expose-Headers指令中返回。
状态
再次使用Node Invader,我找到了多个感兴趣的属性。其中之一status似乎是一个好候选。我不知道它在代码中的何处发生,所以我再次使用前面提到的defineProperty()技巧来获取属性读取的堆栈跟踪。我发现它源自Node中的http-errors核心模块。
OPTIONS
回到查看Express,我找到了另一种 subtly检测原型污染的方法。这个使用OPTIONS请求来查看HEAD方法是否从响应中排除。
JSON反射
到目前为止,我已经介绍了 subtly改变服务器行为以检测原型污染的技术。也可以使用JSON对象的反射来可靠地检测它。我找到了两种不同的方法。
不可变原型
如前所述,Paul Gerste有一篇优秀的帖子,发现了Blitz框架中的原型污染。它的工作方式是JSON中的一个属性引用JSON结构中其他地方的路径。
OAST
我读了一篇由Mikhail Shcherbakov、Musard Balliu和Cristian-Alexandru Staicu撰写的关于利用原型污染的优秀论文。在论文中,他们详细介绍了如何利用Node接收器,如fork()、exec()、execSync()等。
检测JavaScript引擎
作为这项研究的一部分,我问自己以下问题:如果在参数中使用有效的JavaScript属性会发生什么?你能泄漏代码吗?你能检测他们使用什么JavaScript引擎吗?我扩展了服务端扩展,以在使用请求中的有效JavaScript属性时查找响应中的本机代码。
调试Node应用程序
如果您有源代码,可以使用–inspect或–inspect-brk命令行标志测试Node应用程序。通过使用这些运行您的Node应用程序,您可以使用Chrome的开发人员工具进行调试。
防止服务端原型污染
使用Map/Set
为了防止服务端原型污染,您可以使用Map和Set对象;这些提供了安全的API用于查找不从Object原型继承的属性。
删除__proto__
Node提供了一种完全删除__proto__属性的方法,尽管这不会完全防止原型污染,因为您仍然可以使用constructor.prototype执行攻击。然而,这是一个良好的深度防御措施。
Null原型
如果您必须使用常规对象,则需要确保它使用null原型。这意味着它不从Object原型继承。您可以通过使用Object.create(null)初始化对象来实现这一点。
致谢
在进行这项研究时,我阅读了一些优秀的论文,包括Olivier Arteau的论文,特别具有启发性。以及Mikhail Shcherbakov、Musard Balliu和Cristian-Alexandru Staicu关于利用原型污染的论文。Paul Gerste关于利用Blitz的帖子和Michał Bentkowski关于利用Kibana的帖子。我还意识到Daniel Thatcher和@BitK_正在研究同一主题,因此我们决定协调我们的研究。
结论
我已经证明,通过使用服务器行为的细微差异,安全的黑盒原型污染检测是可能的。使用这些各种技术,我展示了您可以自动化发现原型污染缺陷,并提供了一个开源工具包来帮助您在自己的应用程序中找到它们。我还展示了如何通过使用安全的API编写安全代码。最后,在阅读本文之后,我相信您会兴奋地尝试这些技术,为了帮助您,我们构建了一些Web Security Academy实验环境,使您能够练习新技能。