
Reproducción y análisis de causa raíz de CVE-2023-32233, un use-after-free en nf_tables del kernel de Linux que permite escalada local de privilegios, con PoC y notas de explotación.
Qué es este repositorio: mi reproducción práctica y estudio de CVE-2023-32233, un use-after-free en el subsistema nf_tables de Netfilter del kernel de Linux que permite escalada de privilegios local, divulgado públicamente en mayo de 2023 por Patryk Sondej y Piotr Krysiuk.
Crédito: el PoC (exploit.c) y el write-up original son de
@Liuk3r. Mi trabajo en este repositorio
es reproducir el exploit en mi propio laboratorio (Ubuntu 23.04, kernel 6.2.0-20)
y documentar la causa raíz y la cadena de explotación a continuación. Todas las pruebas
se realizaron en máquinas de laboratorio dedicadas.
nf_tables procesa las actualizaciones de configuración como un lote atómico. La validación de cada operación contra los cambios de estado de las operaciones previas en el mismo lote es insuficiente. Concretamente:
nft_rule que contiene una expresión lookup sobre un
anónimo que contiene algunos elementos.nft_setNFT_MSG_DELRULE — elimina la regla, lo que implícitamente elimina la
expresión lookup y el nft_set anónimo;NFT_MSG_DELSETELEM — elimina un elemento del conjunto anónimo
ya eliminado.nf_tables_commit_release() encola recursos
en nf_tables_destroy_list, procesados más tarde por
nf_tables_trans_destroy_work():
primero nft_commit_release() → nf_tables_rule_destroy() →
nft_lookup_destroy() → nft_set_destroy() → kvfree() libera el
nft_set;
luego, para NFT_MSG_DELSETELEM, nf_tables_set_elem_destroy() llama
a nft_set_elem_ext() que desreferencia el nft_set liberado:
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
Si set->ops->elemsize está corrompido, una ubicación de memoria elegida por el atacante
se interpreta como un nft_set_ext — la primitiva sobre la que se construye todo lo demás.
nf_tables_trans_destroy_work() ejecutándose en un
hilo worker en segundo plano: insertar una operación grande de destrucción de conjunto como un
retardo controlado, fijar otras CPUs ocupadas y reasignar el chunk
nft_set liberado desde la misma CPU con un nft_set de un tipo
diferente (diferente elemsize) → confusión de tipos.nft_set_ext corrompidas con offsets fuera de rango para que
nf_tables_set_elem_destroy() recorra chunks adyacentes como una lista de
nft_expr a destruir.nft_log con NFTA_LOG_PREFIX controlado;
nft_log_destroy() libera priv->prefix, proporcionando una primitiva de
asignación superpuesta en kmalloc-{8..192} (limitada por byte NULL al principio).nft_object->udata para eliminar la restricción del byte NULL
en la lectura colgante.nft_dynset para dos tipos de expresiones con estado:
nft_counter — filtra nft_counter_ops → base de nf_tables.ko
(derrota KASLR para el módulo);nft_quota — el puntero consumed proporciona lectura arbitraria
(NFT_MSG_GETSETELEM → nft_quota_do_dump() → NFTA_QUOTA_CONSUMED)
y escritura arbitraria (nft_overquota() añade skb->len a
*priv->consumed en tráfico de loopback).modprobe_path ("/sbin/modprobe" → "//tmp/modprobe") →
ejecución de proceso root con contenido controlado por el atacante.Notablemente, las primitivas elegidas evitan cualquier cosa que CFI bloquearía — no se necesita secuestro de llamadas indirectas en ningún paso.
SLAB_FREELIST_HARDENED), INIT_ON_FREE y restricciones de reutilización de slab
por CPU atacan el paso 1; CFI (donde está presente) restringe el paso 3+.nf_tables que contengan DELRULE+DELSETELEM sobre conjuntos anónimos; triaje de
fallos para nft_set_elem_ext / nf_tables_trans_destroy_work en la pila.nftables requiere CAP_NET_ADMIN, pero
los namespaces de usuario sin privilegios lo otorgan — por esto
kernel.unprivileged_userns_clone=0 es una palanca de endurecimiento significativa.Probado en Ubuntu 23.04 (Lunar Lobster), kernel 6.2.0-20-generic.
sudo apt install gcc libmnl-dev libnftnl-dev
gcc -Wall -o exploit exploit.c -lmnl -lnftnl
El perfil integrado apunta a los kernels binarios de Ubuntu 23.04
(linux-image-6.2.0-20-generic 6.2.0-20.20). Para probar otros kernels,
extraer los símbolos a un archivo profile:
modprobe nf_tables
egrep ' (nft_counter_ops|nft_counter_destroy|free_percpu|modprobe_path)(\s|$)' /proc/kallsyms > profile
El diseño de código máquina de nft_counter_destroy() varía según el compilador y
las opciones; ver ORIGIN.md para la referencia completa de parámetros
(nft_counter_destroy_call_offset/mask/check) y ajustes de afinación de carrera
(race_lead_sleep etc.). La probabilidad de éxito reportada es ≥80% en sistemas
Intel bare-metal inactivos; algunas microarquitecturas (p. ej. Alder Lake) necesitan
ajuste adicional.
El PoC deja el kernel en un estado inestable con memoria corrompida tras una ejecución exitosa. Probar solo en un sistema dedicado y desechable o en una instantánea de VM — nunca en algo con datos que te importen.
exploit.c — código fuente del PoC (por @Liuk3r)ORIGIN.md — write-up original de vulnerabilidad y explotación (por Liuk3r)README.md — este archivo: reproducción + mis notas de análisisInvestigación realizada con fines defensivos/educativos en un entorno de laboratorio aislado.