10、Binder整数溢出漏洞:CVE-2020-0041漏洞分析、binder_transaction中的size校验缺陷、利用手法

各位好,今天我们来聊一个很有意思的漏洞——CVE-2020-0041。这个漏洞出在Binder驱动里,具体位置是binder_transaction函数。嗯,说白了,就是一个整数溢出导致的堆破坏问题。我在做内核安全审计时,这类漏洞见得不少,但Binder里的这个案例特别典型,值得拿出来好好拆解。

10.1 漏洞背景:Binder的缓冲区管理

先简单回顾一下Binder的数据传递机制。当进程A要往进程B发送数据时,Binder驱动会在目标进程的内核空间里分配一块缓冲区,然后把数据拷贝过去。这块缓冲区的大小,由用户态传进来的size参数决定。

关键点来了:binder_transaction函数里有一段代码,负责校验这个size是否合法。但校验逻辑存在一个缺陷——它没有考虑整数溢出的情况。

核心问题:当多个size值相加时,如果结果超过size_t的最大值,就会发生整数回绕。校验通过后,实际分配的内存却远小于预期,后续拷贝操作就会越界写入。

10.2 漏洞分析:binder_transaction中的size校验缺陷

我们直接看漏洞代码。在binder_transaction函数中,有一段类似这样的逻辑:

// 简化后的漏洞代码
uint32_t num_fds = ...;  // 用户可控
size_t fds_size = num_fds * sizeof(struct binder_fd_object);
size_t extra_size = ...; // 用户可控
size_t total_size = fds_size + extra_size;

// 校验:total_size 不能超过某个阈值
if (total_size > MAX_SIZE) {
    return -EINVAL;
}

// 分配缓冲区
binder_buffer = binder_alloc_new_buf(proc, total_size, ...);

问题出在哪?num_fds * sizeof(struct binder_fd_object)这个乘法可能溢出。如果num_fds很大,比如0x40000001,乘以sizeof(struct binder_fd_object)(假设是16字节),结果会回绕成一个很小的值。那么total_size也会很小,校验轻松通过。

但实际拷贝数据时,驱动会按照用户传入的原始num_fds去遍历,把数据写到那块很小的缓冲区里。结果就是——堆溢出。

我个人习惯:在审计这类代码时,我会特别关注乘法运算和加法运算的结果是否可能溢出。尤其是当操作数来自用户态时,一定要用check_mul_overflow这类安全函数。

10.3 利用手法:从堆破坏到权限提升

这个漏洞能用来做什么?嗯,最直接的后果是堆溢出。攻击者可以覆盖相邻的Binder缓冲区元数据,或者覆盖其他内核对象。

我记得在分析这个漏洞的PoC时,发现利用路径大致是这样的:

  1. 触发溢出:构造一个特殊的Binder事务,让num_fdsextra_size组合后产生整数溢出,分配一个很小的缓冲区。
  2. 破坏元数据:溢出数据覆盖了binder_buffer结构体中的free字段或data_size字段。
  3. 控制流劫持:通过精心构造的溢出数据,修改某个函数指针或对象指针,最终实现任意内核地址读写。
  4. 提权:拿到内核读写能力后,替换当前进程的cred结构体,获得root权限。

避坑指南:我曾经在调试类似漏洞时犯过一个错误——只关注了溢出本身,没注意后续的释放逻辑。Binder缓冲区有引用计数和延迟释放机制,如果溢出破坏了引用计数,可能导致UAF(释放后使用)。所以分析时一定要把整个生命周期走一遍。

10.4 漏洞修复:Google是怎么改的?

Google在Android 10的安全补丁中修复了这个漏洞。修复方式很直接:

  • 在乘法运算前检查num_fds是否过大,防止溢出。
  • 使用size_mulsize_add这类安全宏,它们会在溢出时返回错误。
  • 增加了额外的边界检查,确保分配的大小不会超过实际需要。

说白了,就是「别相信用户传进来的任何数值,每一步运算都要防溢出」。

10.5 知识体系图:CVE-2020-0041核心逻辑

下面这张图展示了漏洞从触发到利用的完整链路:

CVE-2020-0041 漏洞分析链路 用户态构造输入 num_fds = 0x40000001 内核校验缺陷 乘法溢出 → total_size 很小 分配小缓冲区 实际大小 < 预期 拷贝数据 → 堆溢出 覆盖相邻 binder_buffer 元数据 利用路径 ① 修改 free 字段 → 任意地址释放 ② 修改 data_size → 越界读取 ③ 最终:任意内核读写 → 提权 漏洞触发条件 需要 BINDER 权限 图:CVE-2020-0041 从输入到利用的完整链路

10.6 实战建议与避坑指南

最后,给做内核安全研究的朋友几点建议:

  • 关注乘法溢出:Binder驱动里有很多num_* * sizeof(...)的模式,每一个都可能是漏洞点。
  • 别只看分配:整数溢出不一定只影响分配大小,还可能影响循环次数、偏移量计算等。
  • 利用时要小心:Binder缓冲区有自己的一套内存管理机制,和普通的slab分配器不太一样。我刚开始研究时,用常规的堆利用手法去试,结果全失败了。后来仔细看了binder_alloc的代码,才发现需要针对Binder的free list做特殊处理。

一个小技巧:如果你在分析Binder漏洞时觉得代码太复杂,可以先用bpftraceftrace跟踪binder_transaction的调用参数,看看实际传入的size值是多少。这能帮你快速定位可疑的输入。

好了,关于CVE-2020-0041的分析就到这里。这个漏洞虽然已经修复了,但它代表的「整数溢出 + 堆破坏」模式,在Binder驱动里依然存在。嗯,值得大家花时间去深挖。


公众号:蓝海资料掘金营,微信deep3321