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时,发现利用路径大致是这样的:
- 触发溢出:构造一个特殊的Binder事务,让
num_fds和extra_size组合后产生整数溢出,分配一个很小的缓冲区。 - 破坏元数据:溢出数据覆盖了
binder_buffer结构体中的free字段或data_size字段。 - 控制流劫持:通过精心构造的溢出数据,修改某个函数指针或对象指针,最终实现任意内核地址读写。
- 提权:拿到内核读写能力后,替换当前进程的
cred结构体,获得root权限。
避坑指南:我曾经在调试类似漏洞时犯过一个错误——只关注了溢出本身,没注意后续的释放逻辑。Binder缓冲区有引用计数和延迟释放机制,如果溢出破坏了引用计数,可能导致UAF(释放后使用)。所以分析时一定要把整个生命周期走一遍。
10.4 漏洞修复:Google是怎么改的?
Google在Android 10的安全补丁中修复了这个漏洞。修复方式很直接:
- 在乘法运算前检查
num_fds是否过大,防止溢出。 - 使用
size_mul和size_add这类安全宏,它们会在溢出时返回错误。 - 增加了额外的边界检查,确保分配的大小不会超过实际需要。
说白了,就是「别相信用户传进来的任何数值,每一步运算都要防溢出」。
10.5 知识体系图:CVE-2020-0041核心逻辑
下面这张图展示了漏洞从触发到利用的完整链路:
10.6 实战建议与避坑指南
最后,给做内核安全研究的朋友几点建议:
- 关注乘法溢出:Binder驱动里有很多
num_* * sizeof(...)的模式,每一个都可能是漏洞点。 - 别只看分配:整数溢出不一定只影响分配大小,还可能影响循环次数、偏移量计算等。
- 利用时要小心:Binder缓冲区有自己的一套内存管理机制,和普通的slab分配器不太一样。我刚开始研究时,用常规的堆利用手法去试,结果全失败了。后来仔细看了
binder_alloc的代码,才发现需要针对Binder的free list做特殊处理。
一个小技巧:如果你在分析Binder漏洞时觉得代码太复杂,可以先用bpftrace或ftrace跟踪binder_transaction的调用参数,看看实际传入的size值是多少。这能帮你快速定位可疑的输入。
好了,关于CVE-2020-0041的分析就到这里。这个漏洞虽然已经修复了,但它代表的「整数溢出 + 堆破坏」模式,在Binder驱动里依然存在。嗯,值得大家花时间去深挖。
公众号:蓝海资料掘金营,微信deep3321