CVE-2019-2000—android内核binder漏洞分析(1)
字数 2712 2025-08-05 19:10:07
Android内核Binder漏洞CVE-2019-2000深度分析
一、漏洞背景
CVE-2019-2000是Android内核Binder驱动中的一个use-after-free漏洞,由Project Zero团队的Jann Horn发现并披露。该漏洞源于内核文件描述符处理优化与Binder机制的交互问题,可能导致权限提升等严重后果。
二、基础知识
1. Linux文件系统关键数据结构
struct task_struct: 表示进程/线程的内核数据结构struct file: 表示打开的文件struct inode: 表示文件本身
重要关系:多次打开同一文件会创建多个struct file指向同一个struct inode
2. 文件描述符表(fdtable)演变
- 早期实现:
task_struct中直接包含struct file*的定长数组 - 2.6.14改进:引入
struct fdtable作为files_struct的间接成员- 将fd、max_fds、close_on_exec等成员移入fdtable
- 便于RCU机制实现,支持fdtable整体替换
文件描述符查找路径:current->files->fdt->fd[fd],实际使用rcu_dereference保证安全
3. task work机制
- 功能:允许内核向指定进程添加任务函数,在进程返回用户态时执行
- 实现:通过
task_struct中的task_works链表头管理待执行任务 - 关键函数:
task_work_add: 添加任务到链表task_work_run: 执行添加的任务
4. fdget/fdput优化机制
问题背景:
fget会增加文件引用计数f_count,fput减少计数- 频繁访问导致包含
f_count的缓存行频繁修改,引发cache line bouncing
优化方案:
fdget检查文件描述符表引用计数:- 如果为1(当前任务独有所有权),不增加
f_count - 设置标志位指示是否获取了
f_count
- 如果为1(当前任务独有所有权),不增加
fdput根据标志决定是否执行fput
使用规则:
- 在
fdget和fdput之间不能复制当前task - 必须在系统调用结束前调用
fdput - 在
fdget和fdput之间不能对相同fd调用filp_close
5. Android Binder机制
Service Manager核心角色:
- Binder上下文管理者
- 管理系统Service组件
- 向Client提供Service代理对象
关键流程:
binder_open: 打开/dev/binderbinder_become_context_manager: 声明为上下文管理者binder_loop: 进入消息循环
Binder驱动关键函数:
binder_ioctl: 处理各种Binder命令BINDER_WRITE_READ: 最重要的命令之一binder_ioctl_write_read: 处理读写请求binder_thread_write/binder_thread_read: 具体读写操作binder_free_buf: 释放缓冲区(漏洞相关)
文件描述符传递:
BINDER_TYPE_FDA: 文件描述符数组类型binder_transaction_buffer_release: 释放时调用ksys_close关闭fd
三、漏洞原理
违反的规则
违反了fdget/fdput的第三条规则:在fdget和fdput之间对相同文件描述符调用filp_close
具体场景
- Manager向Client发送包含
BINDER_TYPE_FDA的消息(含文件描述符) - Client读取
binder_buffer_object获取文件描述符Y - Client使用
dup2(X,Y)用/dev/binder覆盖Y - Client取消用户态binder内存映射(
/dev/binder引用计数=2) - Client关闭文件描述符X(引用计数=1)
- Client对X调用
BC_FREE_BUFFER释放消息(引用计数=0)
问题本质
ksys_ioctl开始调用fdget,而binder_transaction_buffer_release会调用ksys_close->filp_close,导致在fdget/fdput之间关闭文件描述符。
四、漏洞利用
利用步骤
- Manager发送包含
BINDER_TYPE_FDA的binder消息 - Client读取并获取文件描述符Y
- 使用
dup2(X,Y)覆盖Y - 取消binder内存映射(引用计数=2)
- 关闭X(引用计数=1)
- 创建子进程复制文件描述符表(引用计数=2)
- 调用
BC_FREE_BUFFER(引用计数=1) - 子进程调用
close(X)(引用计数=0,释放) - Client尝试获取
binder_proc触发UAF
关键点
- 利用
fput的task work机制绕过KASAN检测 - 通过子进程操作确保引用计数正确归零
五、补丁分析
补丁commit: 80cd795630d6526ba729a089a435bf74a57af927
修复方案
- 将
ksys_close替换为binder_deferred_fd_close - 新函数流程:
- 调用
__close_fd_get_file获取struct file* - 使用
task_work_add添加binder_do_fd_close任务
- 调用
__close_fd_get_file与旧版区别:- 保存fd对应的
struct file*到binder_task_work_cb - 延迟到
task_work_run执行时才真正调用ksys_close
- 保存fd对应的
修复效果
确保文件关闭操作在ioctl返回后执行,不再违反fdget/fdput规则。
六、总结
CVE-2019-2000展示了内核优化机制与子系统交互时可能产生的微妙漏洞。理解此类漏洞需要深入掌握:
- Linux文件描述符管理机制
- 内核性能优化技术(如fdget/fdput)
- Android Binder IPC实现细节
- 内核同步和引用计数机制
该漏洞的修复方案采用了延迟执行的策略,是处理类似竞态条件的典型方法。