导语:今天我们要分析一个经典的DOM UAF漏洞案例:Firefox浏览器HTMLSelectElement Use-After-Free漏洞,2018年12月,Mozilla通过mfsa2018-29 发布了Firefox 64版本,该版本最初是由Nils发现并报告的。
每个匆忙的周末,都需要一道轻腻的回锅肉,来充实一下自己的莫名躁动!今天,我们就来带大家重新咂摸一下一个经典的DOM UAF漏洞!来
重新分析一个经典的DOM UAF漏洞案例:Firefox浏览器HTMLSelectElement Use-After-Free漏洞,2018年12月,Mozilla通过mfsa2018-29 发布了Firefox 64版本,该版本最初是由Nils发现并报告的。这个版本修复了几个安全问题,其中包括CVE-2018-18492,这是一个与select属性相关的释放后重用(use-after-free, UAF)漏洞。我们之前已经讨论过UAF 漏洞,并且我们已经看到供应商已采取措施,试图彻底消除这个漏洞。不过,即使这样,在Web浏览器中发现与UAF相关的漏洞并不罕见,因此了解它们对于发现和修复这些漏洞至关重要。在这篇文章中,我向你揭露一些关于这个特定UAF漏洞以及为解决这个漏洞而发布的补丁的更多细节。
漏洞的触发过程
下面的概念证明可以用来触发这个漏洞:
在受影响的Firefox版本上运行这个概念验证,可以得到以下崩溃和堆栈跟踪:
正如你所看到的,取消引用填充0xe5e5e5e5的内存地址时会产生读取访问冲突。这是jemalloc用来毒化已释放内存的值。所谓“毒化”,我们的意思是用可识别的模式填充已释放的内存,以便进行漏洞诊断。最好,填充模式不对应于可访问的地址,因此任何尝试取消引用从填充的存储器加载的值都将导致立即崩溃,例如,在释放后重用的情况下 。
根本原因分析
PoC由6行组成,让我们逐行分解:
1. 创建div属性;
2. 创建选项属性;
3. option属性被附加到div属性中,这样div现在是option属性的父级了;
4. 将DOMNodeRemoved事件侦听器添加到div属性,这意味着如果删除了option属性,我们将调用我们放在这里的函数;
5. 创建一个select属性。
当用JavaScript创建一个select属性时,函数xul.dll!NS_NewHTMLSelectElement将接收控件。它为这个select属性分配一个0x118字节的对象:
如你所见,最后,跳转到mozilla :: dom :: HTMLSelectElement :: HTMLSelectElement函数。
在这个函数中,初始化新分配对象的各个字段。注意,还分配了0x38字节的另一个对象,并将其初始化为HTMLOptionsCollection对象。因此,默认情况下,每个select属性都有一个option集合。让我们看看PoC的最后一行。
6. 将步骤2中创建的option属性移动到select属性的option集合中,在JavaScript中执行此操作将导致调用mozilla :: dom :: HTMLOptionsCollection :: IndexedSetter函数(你可以看到此函数在图2中所示的堆栈跟踪中调用)。
这里的一些检查是由浏览器完成的,例如,如果option索引大于option集合的当前长度,则通过调用mozilla :: dom :: HTMLSelectElement :: SetLength函数来扩大option集合。在我们的PoC中,由于第6行中的[0],它是零(参见图1),然后在图5中的蓝色块处进行检查。如果要设置的索引不等于option集合的option计数,则采用右分支。在我们的PoC中,所需的索引值为0,而选项计数也为0,因此采用了左分支。这样,执行就可以到达nsINode :: ReplaceOrInsertBefore函数,如下面的红色块中所示:
在nsINode::ReplaceOrInsertBefore函数中,执行对nsContentUtils::MaybeFireNodeRemoved函数的调用,如果父级函数正在侦听这样的事件,则会通知父级函数删除子函数。
当我们在PoC的第4行div属性上设置一个DOMNodeRemoved事件侦听器时(参见图1),我们放置在那里的函数就会被触发。在这个函数中,首先将sel变量设置为0。这将删除对select属性的最后一个引用。接下来,该函数创建了一个巨大的数组缓冲区。这会产生内存压力,导致垃圾收集器启动。此时,select 属性对象被释放,因为不再有对它的任何引用。这个释放的内存将被0xe5e5e5e5毒化。最后,该函数调用alert来刷新挂起的异步任务。从nsContentUtils::MaybeFireNodeRemoved函数返回时,释放的select对象用于读取一个指针,该指针会触发读取访问冲突。
这里有一个有趣的发现,如果采用了右分支,将调用完全相同的函数(nsINode::ReplaceOrInsertBefore),但就在调用之前,AddRef函数将用于增加select对象的引用计数。因此,不会出现释放后重用的情况:
补丁
Mozilla通过changeset d4f3e119ae841008c1be59e72ee0a058e3803cf3修补了这个漏洞,原理就是,option集合中对select属性的弱引用被一个强引用替换。
总结
尽管UAF 漏洞是一个众所周知的漏洞,但对它的预防仍然需要改进。就在几个月前,针对谷歌Chrome的攻击就使用了UAF漏洞。研究人员称,此次Chrome的漏洞编号为CVE-2019-5786,受影响的系统包括微软Windows、苹果macOS和Linux系统。该漏洞存在于API FileReader中,它旨在允许web应用程序异步读取存储在用户计算机上的文件(或者原始数据缓冲区)内容。
另外,UAF也不管只发生在浏览器之外。Linux内核发布了一个补丁来解决由UAF引起的拒绝服务情况。了解UAF是如何发生的,才能方便研究人员检测出它们。与缓冲区溢出漏洞类似,我们不太可能找到预防UAF的终极答案。然而,正确的编码和安全开发可以帮助我们消除或至少减少UAF的影响。
本文翻译自:https://www.thezdi.com/blog/2019/7/1/the-left-branch-less-travelled-a-story-of-a-mozilla-firefox-use-after-free-vulnerability 如若转载,请注明原文地址: https://www.4hou.com/vulnerable/18950.html