-
-
[翻译] Double Free in Standard PHP Library Double Link List [CVE-2016-3132]
-
发表于: 2017-10-20 20:28 5209
-
[翻译] Double Free in Standard PHP Library Double Link List [CVE-2016-3132]
我在标准PHP库(SPL)中发现了一个双重释放的漏洞。 在编写explopit的时候,我似乎发现很少关于PHP如何在内部管理堆的write up。 通过这个博客,我将介绍一下这个话题,以及我在PHP中利用这样一个双重释放漏洞的方法。
根本原因分析
当传入无效索引时,该漏洞位于SplDoublyLinkedList::offsetSet ( mixed $index , mixed $newval )。举个例子:
当无效索引(在这种情况下为100)被传递到函数中时,DateTime对象在第833行中首次被释放:
当在Zend/zend_vm_execute.h:855 清除调用栈时,第二次释放发生。
PHP内部堆管理
在PHP内部,当通过调用诸如ealloc()进行堆分配时,根据大小分为3类:
Small heap allocation (< 3072 bytes)
Large heap adllocation ( < 2 megabytes)
Huge heap allocation ( > 2 megabytes)
让我们探索小堆分配/释放,因为我们将在这个exploit中使用到。 当处理由小堆分配器处理的内存时,每个内存块根据其大小分为“bin”。 例如:
Bin #1 : contains chunk sizes from 1 - 8 bytes
Bin #2: contains chunk sizes from 9 - 16 bytes
Bin #3: contains chunk sizes from 17 - 24 bytes
Bin #YouGetTheIdea....
当内存块通过调用诸如efree()之类时,memory chunk在php内部被释放,如果所讨论的块是由小堆deallocator处理的,而不是将其释放回到操作系统,它会将其缓存并放入适当的“Bin”。 该Bin被实现为单链表,其中释放的内存块被链接在一起。
每个释放的块的前几个字节可以被认为是它的头,它包含一个指向下一个释放的块的指针。
在内存中的外观看起来是这样的:
Exploitation
步骤0:评估这是否可行。 通过尝试向对象插入无效的SplDoublyLinkedList索引来触发此漏洞。 这会触发致命错误:
致命错误后,PHP立即退出。 这样可以防止我们在双重释放后在“userland”中运行任何用户代码。 因此,为了让我们利用这个漏洞,我们需要捕捉错误,使PHP不会在双重释放之后立即退出。 这可以通过set_exception_handler()完成;
步骤1:我们需要决定我们想要触发双重释放的对象。由于以下原因我选择使用SplFixedArray:
SplFixedArray的大小足够小,以至于它被小堆分配器管理
SplFixedArray的大小是PHP内部不常用的大小,因此干扰较小。 在这种情况下,SplFixedArray的大小为0x78,并适合Bin#12。
SplFixedArray在内部定义为结构体,在特定的偏移处有一个成员可用于开发。 我将在步骤3中详细介绍这一点。
步骤2:我们需要做一些堆massaging。 首先让我们做一些清理和清除与SplFixedArray的大小相关联的Bin中的任何空闲内存块。 我们可以通过分配对象的许多实例来做到这一点。 这也可以确保分配的SplFixedArrays在连续的内存块中。
在上面的第5行,我们释放了第50个SplFixedArray。 这在分配的内存的连续块中创建一个孔,并且可以被可视化为:
我们立即分配一个新的SplFixedArray(使它占据第50个slot)并触发双重释放漏洞:
所有这些堆操作的原因是确保在受控的连续大块内存的位置触发双重释放漏洞。 这给了我对内存布局的相当好的控制,以及对其相邻的内存块的引用(第49和第51个SplFixedArray)。
第一次释放之后,这是内存的样子:
第二次释放之后,这是内存的样子:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)