CVE-2022-2588 学习记录
-
简介
netlink 协议 中的 CLS_ROUTE4 模块中存在 double free。
影响范围:linux 5.19 之前
利用条件:linux 用户有 CAP_NET_ADMIN 权限
内核编译选项:
//下面三个可以触发 poc CONFIG_NET_CLS_ROUTE4=y CONFIG_DUMMY=y // 或使用的其他设备 CONFIG_NET_SCH_QFQ=y // 或使用的其他设备 // 跑 exp 还需要,并且 qemu 内存得大 CONFIG_NET_CLS_BASIC=y
-
漏洞分析
漏洞触发点位于 route4_change 中,会生成并初始化一个 route4_filter 结构,如果存在相同 handle 的 route4_filter,则会申请新的释放旧的:
申请新的 route4,fold 指向已经存在的 route4_filter,然后通过 route4_set_parms 更新 handle。
如果新老 handle 不同,则会删除并释放之前的 handle,最后执行的释放函数是 route4_delete_filter_work。
注意到这里的判断条件是不一样的:将旧的 route_filter 从列表中删除要求 handle 不为 0:
而释放旧的 route_filter 只需要判断 fold 存在即可:
这样的话可以构造 handle 为 0 的 route4_filter,释放后的 route4_filter 还会在链表中,就可以实现 double free 了。
在 route4_delete_filter_work 中调用 route4_destroy 释放内存,
释放的结构为 route4_filter:
struct route4_filter {//大小 144 struct route4_filter __rcu *next; u32 id; int iif; struct tcf_result res; struct tcf_exts exts; u32 handle; struct route4_bucket *bkt; struct tcf_proto *tp; struct rcu_work rwork; };
然后在 route4_delete 函数中也会调用 route4_delete_filter_work 任务,所有也可以用 route4_delete 功能实现 double free。
除了 route4_filter 结构,在 tcf 初始化的时候还会申请一个 f->exts->action 的空间:
static inline int tcf_exts_init(struct tcf_exts *exts, struct net *net, int action, int police) { ··· ··· exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *), //这里申请32个指针类型共256字节 GFP_KERNEL); ··· ··· } struct tc_action {//大小192 const struct tc_action_ops *ops; __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ struct tcf_idrinfo *idrinfo; u32 tcfa_index; refcount_t tcfa_refcnt; atomic_t tcfa_bindcnt; int tcfa_action; struct tcf_t tcfa_tm; struct gnet_stats_basic_packed tcfa_bstats; struct gnet_stats_basic_packed tcfa_bstats_hw; struct gnet_stats_queue tcfa_qstats; struct net_rate_estimator __rcu *tcfa_rate_est; spinlock_t tcfa_lock; struct gnet_stats_basic_cpu __percpu *cpu_bstats; struct gnet_stats_basic_cpu __percpu *cpu_bstats_hw; struct gnet_stats_queue __percpu *cpu_qstats; struct tc_cookie __rcu *act_cookie; struct tcf_chain __rcu *goto_chain; u32 tcfa_flags; u8 hw_stats; u8 used_hw_stats; bool used_hw_stats_valid; }; // 但参与释放的 tc_action 不是结构体本身,而是 32 个结构体指针的空间,共 256 字节
-
漏洞利用
使用了 Dirty Cred 方法。
这里可以 double free 的两个结构,一个是 kmalloc-192 的 route4_filter,另一个是一个指针数组空间,大小属于 kmalloc-256。struct file 大小为 232,可以利用 256 的 chunk 进行 double free,让两个文件描述符指向同一个 struct file。
cross cache attack
struct file 的结构体申请内存由 kmem_cache_zalloc 实现,使用的 slab 是 filp_cache。
cross cache attack 的思路是:当一个 slab 页面全部被释放的时候会进行回收,这时被回收的页面是可以被其他种类的 slab 使用从而实现绕过 slab 种类的限制。
- 首先堆喷该大小的堆块,然后 double free 目标的指针指向其中一个,然后 free(第一次 free)。
- 将喷射的这些堆块都 free 掉,那么 double free 掉的堆块所在的 slab 页面中所有的堆块大概率都会被释放掉,该 slab 页面为空然后被系统回收。
- 喷射其他类型的 slab 堆块(filp等),让这个 slab 页面被其他类型的 slab 申请到,而此时 double free 目标的指针仍然指向其中的一个 struct。
- 此时进行第二次 free,成功实现不同种类 slab 的 double free。
流程图:
文件写入
写入一个文件需要顺序执行:
- 文件权限检查
- 写入数据
那么可以通过在这两个步骤之间进行竞争,在中间对 struct file 进行替换,将具体写入操作写到无写权限的文件之中,更细节的处理见 Dirty Cred 的分析。
-
参考文献