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_countfput减少计数
  • 频繁访问导致包含f_count的缓存行频繁修改,引发cache line bouncing

优化方案

  • fdget检查文件描述符表引用计数:
    • 如果为1(当前任务独有所有权),不增加f_count
    • 设置标志位指示是否获取了f_count
  • fdput根据标志决定是否执行fput

使用规则

  1. fdgetfdput之间不能复制当前task
  2. 必须在系统调用结束前调用fdput
  3. fdgetfdput之间不能对相同fd调用filp_close

5. Android Binder机制

Service Manager核心角色

  • Binder上下文管理者
  • 管理系统Service组件
  • 向Client提供Service代理对象

关键流程

  1. binder_open: 打开/dev/binder
  2. binder_become_context_manager: 声明为上下文管理者
  3. 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的第三条规则:在fdgetfdput之间对相同文件描述符调用filp_close

具体场景

  1. Manager向Client发送包含BINDER_TYPE_FDA的消息(含文件描述符)
  2. Client读取binder_buffer_object获取文件描述符Y
  3. Client使用dup2(X,Y)/dev/binder覆盖Y
  4. Client取消用户态binder内存映射(/dev/binder引用计数=2)
  5. Client关闭文件描述符X(引用计数=1)
  6. Client对X调用BC_FREE_BUFFER释放消息(引用计数=0)

问题本质

ksys_ioctl开始调用fdget,而binder_transaction_buffer_release会调用ksys_close->filp_close,导致在fdget/fdput之间关闭文件描述符。

四、漏洞利用

利用步骤

  1. Manager发送包含BINDER_TYPE_FDA的binder消息
  2. Client读取并获取文件描述符Y
  3. 使用dup2(X,Y)覆盖Y
  4. 取消binder内存映射(引用计数=2)
  5. 关闭X(引用计数=1)
  6. 创建子进程复制文件描述符表(引用计数=2)
  7. 调用BC_FREE_BUFFER(引用计数=1)
  8. 子进程调用close(X)(引用计数=0,释放)
  9. Client尝试获取binder_proc触发UAF

关键点

  • 利用fput的task work机制绕过KASAN检测
  • 通过子进程操作确保引用计数正确归零

五、补丁分析

补丁commit: 80cd795630d6526ba729a089a435bf74a57af927

修复方案

  1. ksys_close替换为binder_deferred_fd_close
  2. 新函数流程:
    • 调用__close_fd_get_file获取struct file*
    • 使用task_work_add添加binder_do_fd_close任务
  3. __close_fd_get_file与旧版区别:
    • 保存fd对应的struct file*binder_task_work_cb
    • 延迟到task_work_run执行时才真正调用ksys_close

修复效果

确保文件关闭操作在ioctl返回后执行,不再违反fdget/fdput规则。

六、总结

CVE-2019-2000展示了内核优化机制与子系统交互时可能产生的微妙漏洞。理解此类漏洞需要深入掌握:

  1. Linux文件描述符管理机制
  2. 内核性能优化技术(如fdget/fdput)
  3. Android Binder IPC实现细节
  4. 内核同步和引用计数机制

该漏洞的修复方案采用了延迟执行的策略,是处理类似竞态条件的典型方法。

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 fdput 根据标志决定是否执行 fput 使用规则 : 在 fdget 和 fdput 之间不能复制当前task 必须在系统调用结束前调用 fdput 在 fdget 和 fdput 之间不能对相同fd调用 filp_close 5. Android Binder机制 Service Manager核心角色 : Binder上下文管理者 管理系统Service组件 向Client提供Service代理对象 关键流程 : binder_open : 打开 /dev/binder binder_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 修复效果 确保文件关闭操作在 ioctl 返回后执行,不再违反 fdget / fdput 规则。 六、总结 CVE-2019-2000展示了内核优化机制与子系统交互时可能产生的微妙漏洞。理解此类漏洞需要深入掌握: Linux文件描述符管理机制 内核性能优化技术(如fdget/fdput) Android Binder IPC实现细节 内核同步和引用计数机制 该漏洞的修复方案采用了延迟执行的策略,是处理类似竞态条件的典型方法。