填补盾牌的裂缝:堆分配器中的MTE

前言 2018年,随着ARMv8.5-A的发布,一个全新的芯片安全特性MTE(Memory Tagging Extensions) 横空出世。时隔五年后的2023年,市场上第一款支持此特性的手机发布 —— Google Pixel 8,宣告着MTE正式走入了消费者群体。虽然该特性在手机上还未默认启用,但开发者可以自行开启体验。 MTE作为一个强大的内存破坏防御手段,对于它的防御边界、防御能力,和它对性能的影响,目前网上还未有对其全面的分析。此前,Google Project Zero发表了一系列关于MTE的文章,其聚焦于较为底层的MTE安全性。然而MTE对于真实的软件安全性究竟有多大的影响仍是个未解之谜。想要讨论这个话题,各大堆分配器是一个很好的切入点。堆上的内存破坏问题已经逐渐成为二进制漏洞中的主流类型,参考MSRC于CppCon2019的议题内容: 通常普通开发者并不会直接使用MTE相关的汇编指令,而是依靠堆分配器自带的MTE支持间接使用,堆分配器就像盾牌一样,扛起了保护软件的任务。MTE提供了细颗粒管控内存的基础支持,如何基于硬件MTE能力实现高级安全功能的重任,留给了软件开发者。开源社区主流堆分配器积极响应,实现了基于MTE特性的安全增强,提高了堆空间的内存安全性。 本文将以MTE的三个主要玩家:Chrome中的PartitionAlloc、Glibc中的Ptmalloc、Android中的Scudo为目标,对其中MTE相关的实现分别进行讨论,并对它们进行对比。我们在研究中发现了PartitionAlloc中实现的问题,并将此问题报告给了Google,得到了Chrome团队的确认。 MTE概述 已了解MTE原理的读者可跳过此章节。 MTE利用ARMv8的TBI (Top-Byte Ignore) 特性,使用指针的高4 bits存储tag,在每个进程中有一段专用的内存用于存储tag。当为内存指定了某个tag后,程序必须带上正确的tag访问内存,若tag错误,程序抛出错误信号SIGSEGV,如下图所示: 指令集提供了系列指令来操作tag,此处举例说明MTE的基本用法: ; x0 is a pointer irg x1, x0 stg x1, [x1] ldr x0, [x1] IRG (Insert Random Tag) 指令为指针x0生成一个随机tag,将结果保存至x1中。 STG (Store Allocation Tag) 指令将tag应用至内存中,生效的长度取决于颗粒度,一般为16字节。 LDR (Load Register) 使用带有tag的指针读取内存。 可以看到指令集中提供了底层的支持,但各个指令的使用有很大的自由度,MTE具体如何使用,很大程度上仍然取决于软件开发者。 Allocator Chrome - PartitionAlloc 分配 PartitionAlloc中的分配可以大致分为三种情况: 从ThreadCache中分配,不变动tag直接返回。 从空闲的SlotSpan中分配,不变动tag直接返回。 若以上两种情况均不满足,分配一个新的SlotSpan,对其中所有空闲的堆块打上随机的tag if (PA_LIKELY(use_tagging)) { // Ensure the MTE-tag of the memory pointed by other provisioned slot is // unguessable....

January 3, 2024 · 436 words · DARKNAVY

Exploiting the libwebp Vulnerability, Part 2: Diving into Chrome Blink

前言 当我们把这样一个在三方库中的漏洞放到真实的环境中再看时,会发现漏洞所处的环境往往有很多复杂的变量,想要利用这个漏洞并非想象般那么容易。 我们所已知的信息有: 我们溢出的变量huffman_tables,大小为0x2f28 该堆块在renderer的ThreadPool中分配,而大多数对象在主线程中分配 我们可以以8字节倍数的offset,写入一个部分可控的4字节int Chrome中不同大小的堆块被存储在不同的bucket当中,不同大小的对象因为这个机制被安全地隔离开。通常来说,在chrome中的堆利用需要找到同样大小的对象进行布局,再通过UAF或是OOB篡改其他的对象,从而造成信息泄露或者控制流劫持。接下来我们会分享我们所发现的对象,同时试图去绕过这个机制。 信息泄露 寻找对象 我们首先想要寻找的是一个合适的对象能够被OOB所改写,由于我们的越界写并不能很好地控制值,所以写指针基本被排除,最好的情况是能够改掉类似length这样的字段,对于值没有精确的要求,但是能够引发进一步更好利用的内存问题。 HuffmanCode在libwebp中是用malloc分配的,在chrome中实际是被PartitionAlloc最终接管分配。在renderer中一共有四个partition,LayoutObject partition, Buffer partition, ArrayBuffer partition, FastMalloc partition。FastMalloc实际上最终调用的就是malloc,因此我们想要找的对象可以用FastMalloc来分配。 我们首先用了Man Yue Mo在博客中提到的codeql查询,由于溢出在0x3000的bucket当中,可以选择的对象大小范围为0x2800 - 0x3000。但非常遗憾的是,查询结果为空,这个size下的对象几乎完全不存在。另一个思路是用溢出对象本身,但是这个对象被改掉后不会产生特别的破坏效果,libwebp中也没有其他好的候选对象。到了这里似乎令人觉得有些绝望,利用的第一步就被卡住了。 那么我们还有什么思路呢,一个想法是使用可变长的对象,如Man Yue Mo提到过的AudioArray,但是这个对象是纯数据,被改掉也没有用。查看所有FastMalloc的调用,最终我们发现了这个对象 class CORE_EXPORT CSSVariableData : public RefCounted<CSSVariableData> { USING_FAST_MALLOC(CSSVariableData); 此对象的大小为动态的 wtf_size_t bytes_needed = sizeof(CSSVariableData) + (original_text.Is8Bit() ? original_text.length() : 2 * original_text.length()); // ... snip ... void* buf = WTF::Partitions::FastMalloc( bytes_needed, WTF::GetStringWithTypeName<CSSVariableData>()); 该对象代表了CSS中的变量,可以通过以下方式来定义 element { foo: var(--my-var, bar); } blink会根据CSS变量的内容动态分配CSSVariableData的内存。还有一个好消息是,JavaScript中也可以便捷地操作CSS变量。 // add a CSS variable element....

November 3, 2023 · 286 words · DARKNAVY

Exploiting the libwebp Vulnerability, Part 1: Playing with Huffman Code

漏洞定位 在初始的漏洞分析阶段,由于缺少现成的PoC或详细分析报告,我们首先尝试阅读并理解webmproject/libwebp上游仓库针对CVE-2023-4863的修复代码。然而,WebM Project官方的修补过程相对复杂,这使得我们难以精确地锁定漏洞的原始触发点。 于是,我们将目光转向了Apple对CVE-2023-41064的官方补丁,并使用BinDiff对更新前后的ImageIO组件进行了对比。我们注意到Apple的补丁代码变更相对较少,易于理解,并且非常“简单粗暴”: 简而言之,Apple的修复方案是在WebP解码器中增加了一项检查:如果在构建霍夫曼编码表 (Huffman Table) 时越界,就会直接返回错误,而不是继续解码。 diff --git a/src/dec/vp8l_dec.c b/src/dec/vp8l_dec.c index 45012162..06b142bc 100644 --- a/src/dec/vp8l_dec.c +++ b/src/dec/vp8l_dec.c @@ -438,6 +438,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, goto Error; } + bound = &huffman_tables[num_htree_groups * table_size]; huffman_table = huffman_tables; for (i = 0; i < num_htree_groups_max; ++i) { // If the index "i" is unused in the Huffman image, just make sure the diff --git a/src/utils/huffman_utils....

November 3, 2023 · 804 words · DARKNAVY