【kernel exploit】CVE-2022-2588 Double-free 漏洞 DirtyCred 利用
影响版本:Linux v3.17 (commit) ~v5.19.1。 v5.19.2已修补。
测试版本:Linux-5.19.1 exploit及测试环境下载地址—https://github.com/bsauce/kernel-exploit-factory
编译选项:
CONFIG_BINFMT_MISC=y (否则启动VM时报错)
CONFIG_USER_NS=y (触发漏洞需要 User Namespace)
CONFIG_NET_CLS_ROUTE4=y (漏洞函数所在的模块)
CONFIG_DUMMY=y CONFIG_NET_SCH_QFQ=y (breezeO_o 提供的两个编译选项,触发poc需要用到)
CONFIG_NET_CLS_ACT / CONFIG_NET_CLS_BASIC (默认已开启)
CONFIG_NET_SCH_SFQ (exp中触发漏洞需用到 sfq随机公平队列)
CONFIG_NET_EMATCH_META (exp中堆喷对象时需要用到)
在编译时将.config
中的CONFIG_E1000
和CONFIG_E1000E
,变更为=y。参考
$ wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.19.1.tar.xz
$ tar -xvf linux-5.19.1.tar.xz
# KASAN: 设置 make menuconfig 设置"Kernel hacking" ->"Memory Debugging" -> "KASan: runtime memory debugger"。
$ make -j32
$ make all
$ make modules
# 编译出的bzImage目录:/arch/x86/boot/bzImage。
漏洞描述:和 CVE-2021-3715 (参见 BlackHat 2021-Europe-Your Trash Kernel Bug, My Precious 0-day 16页)类似,由于将 route4_filter
对象从链表中删除和释放时的检查条件不一致,导致该对象被释放后仍存于链表中,后面可以触发 Double-Free。需要 User Namespaces
才能触发。采用 DirtCred 方法进行提权。
补丁:patch
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index a35ab8c27866e..3f935cbbaff66 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -526,7 +526,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,rcu_assign_pointer(f->next, f1);rcu_assign_pointer(*fp, f);- if (fold && fold->handle && f->handle != fold->handle) {+ if (fold) {th = to_hash(fold->handle);h = from_hash(fold->handle >> 16);b = rtnl_dereference(head->table[th]);
保护机制:KASLR/SMEP/SMAP/KPTI
利用总结:exp中主要是两个函数完成漏洞利用, run_exp()
记为进程1,exploit()
记为进程2。进程之间是通过pipe进行通信,以确定运行顺序;注意每次新生成子进程,都要先绑定到 CPU0 上运行。喷射次数 middle
和 end
值可以适当调整,本文采用的是 middle=38 / end=38+40
。DirtyCred 的详细原理可参考 【bsauce读论文】 DirtyCred-内核凭证替换利用技术 和 【kernel exploit】CVE-2021-4154 错误释放任意file对象-DirtyCred利用。
进程1 | 进程2 |
---|---|
0. 绑定到CPU 0 上运行,设置子进程内存、工作目录、Namespace,启动进程2; | |
1. 去碎片化,打开10000个文件,消耗 filp cache,为 cross-cache 作准备;
|
|
2. 喷射 (middle+3)*32 kmalloc-192 & kmalloc-256;(和漏洞对象位于同一cache,便于进行 cross-cache 被 file 对象复用)
|
|
3. 分配1个 route4_filter 漏洞对象,还有1个kmalloc-256 的漏洞对象;
|
|
4. 再喷射 (end-middle-2)*32 kmalloc-192 & kmalloc-256;
|
|
5. 释放 (end-24)*32 kmalloc-192 & kmalloc-256;
|
|
6. 第1次释放漏洞对象 kmalloc-192 & kmalloc-256; | |
7. 释放 (end-middle+1) kmalloc-192 & kmalloc-256;(避免连续释放同一对象,触发内核 double-free 的检测)
|
|
8. 喷射 4000 个低权限 file 对象(通过打开 exp_dir/data 文件);
|
|
9. 第2次释放漏洞对象 kmalloc-192 & kmalloc-256 | |
10. 喷射 5000 个低权限 file 对象,采用 kcmp 调用检查是否和前4000个 file 重合,重合的两个 file 记为 overlap_a / overlap_b ;
|
|
11. 发起3个利用线程,线程1写入大量数据来占用文件锁;线程2往 overlap_a 写入恶意数据;
|
|
12. **线程3关闭 overlap_a / overlap_b ,喷射 4096*2 个高权限 file 对象(通过打开 /etc/passwd 文件);**未区分CPU
|
|
13. 最后检查 /etc/passwd 文件是否被写入恶意数据。
|
1. 漏洞分析
1-1. 漏洞原理
漏洞分析:漏洞函数是 route4_change(),用于初始化和替换 route4_filter
对象。使用 handle
作为id来区分不同的 route4_filter
,如果存在某个 handle 之前已被初始化过(fold
变量非空),就会移除旧的 filter,添加新的 filter;否则直接添加新的 filter
。
先在 [4]
处将 old filter 从 list 中删除,再在 [6]
处释放 old filter。漏洞位于 [4]
处,检查条件是需同时满足旧filter 的 handle
非0且新旧 filter 的 handle
不相等,这和 [6]
处的条件不一致,[6]
只检查旧 filter 是否存在。因此,如果用户创建的 filter 的 handle 为0,则 old filter 在 [4]
处不会被移除,但是在 [6]
处会被释放。
漏洞触发序列:SYSCALL-write -> ksys_write() -> vfs_write() -> new_sync_write() -> call_write_iter() -> sock_write_iter() -> sock_sendmsg() -> sock_sendmsg_nosec() -> netlink_sendmsg() -> netlink_unicast() -> netlink_unicast_kernel() -> rtnetlink_rcv() -> netlink_rcv_skb() -> rtnetlink_rcv_msg() -> tc_new_tfilter() -> route4_change()
static int route4_change(struct net *net, struct sk_buff *in_skb,struct tcf_proto *tp, unsigned long base, u32 handle,struct nlattr **tca, void **arg, bool ovr,bool rtnl_held, struct netlink_ext_ack *extack)
{struct route4_filter *fold, *f1, *pfp, *f = NULL;fold = *arg; // fold 来自上层函数,根据句柄调用 route4_get() 找到现有的 route4_filter...f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); // [0] 分配新的 route4_filter 对象err = tcf_exts_init(&f->exts, net, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); // route4_filter->exts.action 分配 256 字节的空间...if (fold) { // [1] fold 非空, 表示对应handle 的 filter 已存在, 将 old filter 的信息拷贝到 new filter 并在 [2] 初始化 new filterf->id = fold->id;f->iif = fold->iif;f->res = fold->res;f->handle = fold->handle;f->tp = fold->tp;f->bkt = fold->bkt;new = false;}// initialize the new filtererr = route4_set_parms(net, tp, base, f, handle, head, tb, // [2] 初始化 new filtertca[TCA_RATE], new, flags, extack);if (err < 0)goto errout;// insert the new filter to the listh = from_hash(f->handle >> 16); // [3] 将 new filter 插入到 listfp = &f->bkt->ht[h];for (pfp = rtnl_dereference(*fp);(f1 = rtnl_dereference(*fp)) != NULL;fp = &f1->next)if (f->handle < f1->handle)break;tcf_block_netif_keep_dst(tp->chain->block);rcu_assign_pointer(f->next, f1);rcu_assign_pointer(*fp, f);// remove fold filter from the list if fold existsif (fold && fold->handle && f->handle != fold->handle) {// [4] 若存在 old filter, 则从 list 中移除th = to_hash(fold->handle);h = from_hash(fold->handle >> 16);b = rtnl_dereference(head->table[th]);if (b) {fp = &b->ht[h];for (pfp = rtnl_dereference(*fp); pfp;fp = &pfp->next, pfp = rtnl_dereference(*fp)) {if (pfp == fold) {rcu_assign_pointer(*fp, fold->next); [5]// remove the old from the linked listbreak;}}}}...// free the fold filter if it exists // [6] 释放 old filterif (fold) {tcf_unbind_filter(tp, &fold->res);tcf_exts_get_net(&fold->exts);tcf_queue_work(&fold->rwork, route4_delete_filter_work); // [7] 启动内核任务,调用 oute4_delete_filter_work() 释放 old filter}...
}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*8=256 大小的空间, 32个 tc_action 结构体指针GFP_KERNEL);··· ···
}
1-2. 漏洞对象
漏洞对象:有两个漏洞对象,我们用的是 kmalloc-256 这个对象,因为 file
对象的大小是 232,二者比较接近,便于进行 cross-cache
。
- route4_filter —— 大小为 144,属于
kmalloc-192
; - tc_action —— 大小为 192,漏洞对象并非
tc_action
对象,而是存储32个tc_action
结构体指针的空间,也即大小为 256 的空间,属于kmalloc-256
。
漏洞释放链:route4_delete_filter_work() -> __route4_delete_filter() -> tcf_exts_destroy()
static void route4_delete_filter_work(struct work_struct *work)
{struct route4_filter *f = container_of(to_rcu_work(work),struct route4_filter,rwork);rtnl_lock();__route4_delete_filter(f); // <-------rtnl_unlock();
}static void __route4_delete_filter(struct route4_filter *f)
{tcf_exts_destroy(&f->exts); // <-------tcf_exts_put_net(&f->exts);kfree(f); // 释放 tc_action 对象
}void tcf_exts_destroy(struct tcf_exts *exts)
{#ifdef CONFIG_NET_CLS_ACT // 编译时勾选 CONFIG_NET_CLS_ACT —— 默认是开启的if (exts->actions) {tcf_action_destroy(exts->actions, TCA_ACT_UNBIND);kfree(exts->actions); // 释放 tc_action 对象}exts->nr_actions = 0;
#endif
}
EXPORT_SYMBOL(tcf_exts_destroy);
另一处释放链:route4_delete() -> route4_delete_filter_work()
static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,bool rtnl_held, struct netlink_ext_ack *extack)
{struct route4_head *head = rtnl_dereference(tp->root);struct route4_filter *f = arg;struct route4_filter __rcu **fp;struct route4_filter *nf;struct route4_bucket *b;...fp = &b->ht[from_hash(h >> 16)];for (nf = rtnl_dereference(*fp); nf;fp = &nf->next, nf = rtnl_dereference(*fp)) {if (nf == f) {...tcf_queue_work(&f->rwork, route4_delete_filter_work); // <--------...}}...
}
2. 漏洞利用
思路:由于已被释放的 fold
仍位于链表上,就可以再次释放 fold
,触发 route4_filer
对象的 Double-free,如果编译内核时开启了 CONFIG_NET_CLS_ACT
,那么 route4_filter->exts.actions
对象也会 Double-free。我们可以利用这两种漏洞对象来进行 DirtyCred 攻击,分别替换进程凭证(task credentials,利用 kmalloc-192)和文件凭证(open file credentials,利用 kmalloc-256)。
根据 DirtyCred 的思路,我们在文件写许可检查之后,替换 file
结构,就能往只有读许可的文件写数据。理论上,exp能通杀各个漏洞版本的内核(有些老版本内核中,msg_msg
隔离在 kmalloc-rcl-*
中,所以需要使用不同的堆喷对象)。作者测试后能在以下版本中提权:
- CentOS 8/Stream (4.18.0-80.el8.x86_64 ~ xxx)
- Debian 11 (5.10.0-8-amd64 ~ xxx)
- Fedora 33 (5.8.15-301.fc33.x86_64 ~ xxx)
- Manjaro 18 (xxx ~ xxx)
- RHEL 8 (4.18.0-80.el8.x86_64 ~ xxx)
- Ubuntu 17 (4.10.0-19-generic ~ xxx)
- Ubuntu 18 (xxx ~ xxx)
- Ubuntu 19 (5.0.0-38-generic ~ xxx)
- Ubuntu 20 (xxx ~ xxx)
问题:Double-free 触发崩溃。我们用到的漏洞对象是 kmalloc-256
,但漏洞同时也会把一个 kmalloc-192
释放两次,就会触发内核 Double-free 检测,导致崩溃。解决办法是,在两次释放 kmalloc-192
之间间隔释放同一页的其他 slab,就能避免崩溃。
static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
{unsigned long freeptr_addr = (unsigned long)object + s->offset;#ifdef CONFIG_SLAB_FREELIST_HARDENEDBUG_ON(object == fp); // 检测 double-free
#endiffreeptr_addr = (unsigned long)kasan_reset_tag((void *)freeptr_addr);*(void **)freeptr_addr = freelist_ptr(s, fp, freeptr_addr);
}
cross-cache:我们将释放某个 kmalloc-256
cache page,将该页归还给页管理器,然后分配 file
结构来复用该页(filp
cache)。步骤如下:
- (1)分配一堆
kmalloc-256
堆块,包含漏洞对象; - (2)利用漏洞第1次释放漏洞对象,并释放一堆
kmalloc-256
,以归还漏洞对象所在的页; - (3)分配大量低权限
file
对象来占据漏洞对象(cross-cache attack); - (4)利用漏洞第2次释放漏洞对象,堆喷高权限
file
对象来替换低权限file
对象。
测试截图:
3. exp适配
错误1:漏洞触发出错。在我编译的 Linux-v5.19.1 上测试exp总是失败,经过调试发现连漏洞都无法触发,在 tc_new_tfilter() tcf_proto_create()处就报错返回了,查阅后发现我这个内核版本中默认加载的流量控制队列只有以下几种:
$4 = 0xffffffff82b13f30 <pfifo_fast_ops+16> "pfifo_fast"$5 = 0xffffffff82b14470 <pfifo_qdisc_ops+16> "pfifo"$6 = 0xffffffff82b143b0 <bfifo_qdisc_ops+16> "bfifo"$7 = 0xffffffff82b142f0 <pfifo_head_drop_qdisc_ops+16> "pfifo_head_drop"$8 = 0xffffffff82b14170 <mq_qdisc_ops+16> "mq"$9 = 0xffffffff82b13ff0 <noqueue_qdisc_ops+16> "noqueue"$10 = 0xffffffff82b14230 <blackhole_qdisc_ops+16> "blackhole"$11 = 0xffffffff82b14530 <qfq_qdisc_ops+16> "qfq"
并未加载 sfq随机公平队列 (exp中就是采用的这种队列),sfq
代码实现位于 net/sched/sch_sfq.c,查看 Makefile
文件才发现 sfq
对应的编译选项是 CONFIG_NET_SCH_SFQ
,编译时并未勾选这个选项,只有 CONFIG_NET_SCH_QFQ
默认是勾选上了的,所以需重新编译内核。
错误2:堆喷出错。 tcf_em_tree_validate() -> tcf_em_validate() 这里根据传入的 em_hdr->kind
(0x4)来寻找对应的 ops 结构,但是没找到。exp中赋值为 hdr->kind = TCF_EM_META
,搜索源码发现 TCF_EM_META
出现在 net/sched/em_meta.c,查看 Makefile
发现没有勾选 CONFIG_NET_EMATCH_META
选项。
参考
CVE-2022-2588-exploit
CVE-2022-2588-POC
[漏洞分析] CVE-2022-2588 route4 double free内核提权
[kernel] 编译能复现指定poc的内核的排错过程
【kernel exploit】CVE-2022-2588 Double-free 漏洞 DirtyCred 利用相关推荐
- c++堆栈溢出怎么解决_Windows Kernel Exploit 内核漏洞学习(2)-内核栈溢出
这是 Windows kernel exploit 系列的第二部分,这一篇我们通过内核空间的栈溢出来继续深入学习 Windows Kernel exploit ,看此文章之前你需要有以下准备: Win ...
- Linux Privilege Escalation Kernel Exploits | Linux本地内核提权漏洞复现 CVE-2015-1328
Linux Privilege Escalation Kernel Exploits | Linux本地内核提权漏洞复现 CVE-2015-1328 文章目录 Linux Privilege Esca ...
- linux media v4l2,Linux kernel drivers/media/v4l2-core/videobuf2-v4l2.c拒绝服务漏洞(CVE-2016-4568)...
Linux kernel drivers/media/v4l2-core/videobuf2-v4l2.c拒绝服务漏洞(CVE-2016-4568) 发布日期:2016-05-19 更新日期:2016 ...
- kernel exploit 有用的结构体
一.可用于 Leak/AAR/AAW/RIP劫持的结构体 说明目前缺少kmalloc-8.kmalloc-16.kmalloc-64.kmalloc-512结构体. 1. shm_file_data ...
- Double free 漏洞复现与利用
2018体系结构安全大作业 申明:转载请注明出处 选题:2015 年 0ctf 题 题目描述:提供记事本功能的二进制文件,找出其漏洞,get shell 第一章 操作说明 为方便老师审核作业,本报告将 ...
- android 漏洞发布,CVE发布2016年软件漏洞排行榜报告:安卓以523处位居第一
IT之家讯 1月3日消息,近期CVE Details公布了2016年软件漏洞数量最新报告,根据报告显示,漏洞存在数量最多的是安卓系统,以523处漏洞位居第一,排名第二的为Debian Linux系统, ...
- linux kernel 4.4.1 uaf提权漏洞,条件竞争在Kernel提权中的应用
Double-Fetch漏洞简介 随着多核CPU硬件的普及,并行程序被越来越广泛地使用,尤其是在操作系统.实时系统等领域.然而并行程序将会引入并发错误,例如多个线程都将访问一个共享的内存地址.如果其中 ...
- Polkit权限提升漏洞(CVE-2021-4034)利用及修复
Polkit本地权限提升漏洞(CVE-2021-4034)利用及修复 文章目录 Polkit本地权限提升漏洞(CVE-2021-4034)利用及修复 漏洞说明 危害等级 影响版本 修复版本 漏洞利用 ...
- 【网络安全】MS17-010“永恒之蓝”漏洞的利用
导语 最近学期末进行网络安全实训,老师要求每个小组选择一个方向进行研究,本篇将讲述"永恒之蓝"漏洞的简单利用. 一.实验原理 Eternalblue通过TCP端口445和139来利 ...
最新文章
- crytojs加密 java解密,使用CryptoJS在Javascript中加密并在Java中解密
- 【深度学习】基于Torch的Python开源机器学习库PyTorch概述
- TimePicker的使用
- java 里如何实现逻辑返回值_☆技术问答集锦(五)
- HUE 打开 WorkFlow异常 Operation category READ is not supported in state standby
- Windows下Nginx的启动、停止等基本命令
- 【Nowcoder - 5670 B Graph】2020 牛客暑期多校训练营(第五场)【最小异或生成树、Boruvka 思想】
- 利用VBA将表格保存为PDF文件
- 关于SVN状态图标不显示的解决办法
- 联通专线切换成移动专线问题故障解决
- 读取TXT文档数据生成词云图
- 技术岗的职业规划_技术人员职业规划精选范文
- linux宝塔下如何强制ssl,宝塔面板一键安装SSL证书强制HTTPS访问设置
- linux安装教程黑屏,解决SUSE Linux安装黑屏
- 【支付架构】跨境支付
- JavaScript基本原理常识
- 遗传基因科普(5):DNA双螺旋结构的发现
- 阿里实习生内推电面总结
- 提升认知能力 | 塑造大脑,重新认识你自己
- python简易问答(持续更新)
热门文章
- 思维导图软件 XMind 8 和 XMind 2020的选择
- 国内如何下载并使用LINE(免费提供apk安装包)
- 视频分辨率、帧率和码率三者之间关系详解
- 联想x3650服务器安装硬盘,IBM x3650 M2服务器系统安装攻略(组图)
- 软件测试面试题全网独家没有之一的资深测试工程师面试题集锦
- 抖音终于活成了自己讨厌的样子
- ASO干货丨6招解决APP上架时内购频繁被拒问题
- 健合集团携手企企通,打造智慧采购商城管理平台
- 一名武大同学的春招48小时
- 《校园宿舍管理系统》之数据库程序设计/GUI/java/eclipse/MySQL/JDBC