Exploit Archeology - Exploiting an old unknown Server Side Browser
2024-05-08 • Exploits
我最近在一个漏洞赏金目标上进行黑客攻击,发现了一个有趣的API端点,该端点可以渲染用户提供的HTML并执行任何包含的JavaScript。利用服务器端浏览器漏洞是我过去几年的一个重点,因此我开始着手利用这个新发现的功能。这篇博客文章详细描述了我研究和利用一个十年之久的服务器端浏览器的旅程。
侦察
Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) Safari/538.1
……这真的没什么帮助。你通常期望至少在某处看到Chrome/124.0.0.0、Firefox/125.0或类似内容,这可以揭示浏览器引擎和版本。但这里没有这样的运气。
然而,由于我有JavaScript执行权限,我可以通过浏览器特定的API具体识别引擎,例如Chrome的Error.captureStackTrace、Firefox的document.preferredStyleSheetSet和WebKit(Safari)的webkitConvertPointFromPageToNode。在这种情况下,它似乎是一个基于WebKit的引擎。
由此,我的第一个(也是错误的)假设是它可能是PhantomJS。我之前为CVE-2014-1303编写过一个漏洞利用,因此将其打包并发送到HTML服务。没有结果……尽管为了安全起见,我又尝试了两三次(或七次)。
不情愿地接受它不是PhantomJS后,我开始研究它运行的WebKit的大致版本。为此,caniuse.com的浏览器比较功能非常有用。选择几个Safari版本,我可以轻松比较每个版本的功能支持:
caniuse.com和MDN的数据存在一些差异,使用这两个来源可以创建一个基本脚本来确定使用的WebKit的大致版本:
|
|
这揭示了该服务使用的WebKit对应于Safari ~8,绝对日期大约在2014年到2015年。是的,将近十年了!是时候动手并拿出我的漏洞挖掘工具了。
研究
既然知道了大致的WebKit版本和代码开发时间框架,我可以开始挖掘旧的漏洞报告、PoC和漏洞利用。搜索那个时期的WebKit漏洞出现了许多关于索尼PlayStation越狱的结果。PS Vita于2011年发布,PS4于2013年发布,越狱这些设备的常见入口点是WebKit。这对我来说是个好消息,因为在我寻找的那个时期,有相当数量的PS越狱利用了WebKit版本。
花了几个小时阅读各种越狱社区网站、论坛、wiki和GitHub仓库后,我发现了几个可行的漏洞候选,作者名如qwertyoruiop、Fire30和RKX1209。不幸的是,对于我来说,这些~2015年PS越狱场景中的主要角色都喜欢编写难以理解的漏洞利用,使用最小(有时完全无法理解)的注释和未定义的静态偏移量遍布各处。有些代码真的来自另一个时代。
我最终挖掘出一个看起来可行的漏洞利用,由一名名为xyz的黑客提供了清晰的撰写。该漏洞利用针对WebKit JSArray::sortCompactedVector函数中的Use After Free(UaF)漏洞。进一步的搜索没有识别出与此问题相关的CVE,因此我带着一些担忧接近。
xyz的JSArray::sortCompactedVector漏洞利用撰写包括一个基本的PoC,可用于确认目标服务是否易受攻击:
|
|
因此,我将这个打包在一个<script>标签中,并发送到目标服务。我没有得到任何响应。这可能是因为服务崩溃了,或者是因为我没有包含任何外部日志记录:facepalm:。在放置日志记录后,我能够确认目标在处理此脚本时会崩溃,太棒了!
就在这时,我意识到仅使用远程服务获得可靠的漏洞利用将1)大大增加开发和调试时间,2)导致远程服务频繁崩溃。我需要一个本地开发环境来PoC这个漏洞,然后再测试实时环境。
幸运的是,我的老朋友PhantomJS也易受此JSArray::sortCompactedVector UaF的影响,因此我可以用它作为漏洞利用开发阶段的替代目标。
开发
我的第一个任务是实际编译带有符号的PhantomJS(以使我不可避免的漏洞利用调试不那么摧毁理智)。PhantomJS的最后一个公开版本来自2016年(!),因此拉取代码并从那个时期的库开始似乎是一个好的起点。经过多次尝试,我能够使用以下命令编译PhantomJS:
|
|
幸运的是,Ubuntu 14.04的Ubuntu包仓库仍然活跃,这使得这个过程比原本可能的情况要少很多痛苦。完成后,是时候开始开发PoC了。
遵循xyz的漏洞利用,一切进展顺利,直到我遇到:
有趣的是,这在Vita上成功破坏了一个JSArray对象,但在Linux上崩溃,命中了一个保护页。但这没关系,因为我们不是在利用Linux。
但我在利用Linux xyz!我现在该怎么办??!?在PS Vita上,该漏洞可用于扫描连续内存页以破坏目标,以获得基本的读、写和addrOf原语。在Linux上的问题是,保护页(没有rw权限的内存页)被放置在rw页之间,打破了连续内存区域。如果你尝试读取或写入保护页,将引发SIGSEGV,杀死进程,这不太理想。
对于那些不熟悉浏览器漏洞利用的人来说,这些原语是大多数漏洞利用的基本构建块。读原语允许读取任何内存地址,写原语允许将任意值写入任何内存地址,addrOf原语披露给定JavaScript对象的内存地址。从这三个原语,最终可以构建任意shellcode执行。
此时,我留下了一个损坏的ArrayWithDouble(一个所有元素都是double类型的数组),长度为0x80000000,允许对损坏数组之后的内存进行越界(oob)读取和写入。通过在内存中损坏数组之后放置一个对象数组,可以轻松创建addrOf原语,留下读和写原语。要创建读和写原语,可以损坏Float64Array的支持地址指针,使任意内存地址可引用。不幸的是,要损坏这个指针需要指针解引用,因此不能简单地使用损坏的oob数组读/写来完成。我需要一个任意内存写入来创建我的任意内存写入,似乎:facepalm:。
扫描损坏数组的oob内容,我注意到一个指向当前内存区域开始的指针。使用这个,可以可靠地计算损坏数组的地址。此时,一个想法形成。如果我能让一个Float64Array在内存中位于oob数组之后(并使用addrOf原语学习其地址),并且它的TypedArray指针(持有支持存储指针的结构)也在oob数组之后,我可以使用oob数组的相对读取和写入来损坏支持存储,而不会碰到那些讨厌的保护页。经过一些修订和咒骂后,这种技术给了我最终的读和写原语。
这是利用Linux上这个问题的最有效方法吗?我不知道!正如你现在可能已经注意到的,我绝不是利用古老WebKit问题的专家。如果你知道一个更简单的方法,可以在Twitter上给我详细信息!
利用
有了基本原语,最后一步是使用它们来获得任意shellcode执行。在我学习(咳嗽 复制 咳嗽)的PlayStation越狱中,返回导向编程(ROP)总是在最后阶段用于执行所需的shellcode。在这种情况下,我无法访问目标可执行文件,只有我的基于PhantomJS的开发环境,这对于ROP链开发来说不够用。因此,我想出了一个新的大想法,我会动态扫描可执行内存,寻找我需要的ROP小工具来映射rwx内存,复制shellcode并跳入其中……这能有多难?
此时有几个注意事项1)它会很难!2)如果我停下来思考,而不是盲目地跟随前人所做的,我会节省自己很多痛苦。
玩了很长时间的ROP后,我对实现shellcode执行需要哪些小工具有了感觉,因此我只需要在JavaScript中编写ROP小工具发现和ROP链编译函数。实现了基本的小工具发现后,我针对目标运行它,看看它是否有效,但它没有!前几十个内存读取按预期工作,但随后返回了奇怪的结果,这些结果似乎与我的预期不一致。
JIT是浏览器开发者给漏洞利用作者的礼物。它不仅极其复杂,在浏览器引擎中引入了许多漏洞,还必然需要具有rwx保护的内存(或至少是rw-,随后更新为r-x)。
在这个WebKit版本中,JIT页留下了rwx内存保护。因此,尽管我进行了所有ROP研究和开发,我最终获得shellcode执行的实际方法只是强制一个函数被JIT编译,然后覆盖内存中生成的函数指令,然后调用该函数。容易多了!
结论
在经过艰苦的努力产生这个可靠的远程代码执行PoC并确认在目标上执行后,我提交了漏洞赏金报告,非常希望(如果不是实际期望)获得大笔支付……你能猜到接下来会发生什么吗?漏洞赏金计划迅速回应了我的报告,赞扬了显然投入此问题的努力量,并告知我漏洞实际上存在于第三方服务中。哦。继续向第三方报告问题,他们没有漏洞赏金计划……并且从未回应我多次尝试向他们披露问题的电子邮件。
哦,好吧……继续下一个。