Una función cambiada por otra, dos veces, y un CVSS de 9.8
Antes: kfree(). Después: kfree_rcu(). Ese es el arreglo completo para CVE-2026-98432, repetido en dos funciones distintas, en dos archivos distintos. Si lees primero la puntuación CVSS, el fallo suena abstracto: 9.8, crítico, sin privilegios, sin interacción del usuario. Si lees primero el diff, la abstracción desaparece. El fallo es un solo nombre de función, cambiado por el equivocado, en exactamente dos lugares.
CVE-2026-98432 es un use-after-free en cómo el stack IPv4 TCP del kernel de Linux maneja las opciones IP de una conexión, presente desde un commit de 2015 y ya corregido en todas las ramas estables mantenidas.[1] El propio CNA de NVD lo puntuó con 9.8, AV:N/AC:L/PR:N/UI:N: remoto, baja complejidad, sin privilegios, sin interacción del usuario. Tres semanas después de la divulgación, el boletín de Red Hat todavía no nombra ninguna compilación corregida, y el propio tracker de Ubuntu sigue listando cada versión soportada como vulnerable o en curso.[2][3] Según se informa, un modelo de investigación de Anthropic aún no publicado convirtió esto en una vía funcional de zero-click a root en menos de una hora.[4] Más sobre esa afirmación más adelante, con cuidado, porque un detalle que circula junto a ella en internet no es algo que Anthropic haya dicho realmente.
Leí el commit de corrección yo mismo, no una paráfrasis de él. git.kernel.org devolvió un HTTP 403 a una solicitud directa, así que leí el mismo commit exacto, por su hash, desde el espejo público del árbol estable que mantiene Greg Kroah-Hartman en GitHub.[5] No he reproducido esto en un laboratorio. Este artículo está construido leyendo fuentes primarias de principio a fin, no ejecutando un exploit, y esa distinción importa para todo lo que sigue.
// The actual kfree -> kfree_rcu swap, per the fixing commit (dossier
// S2). Surrounding code is an illustrative reconstruction of shape, not
// a literal excerpt.
net/ipv4/af_inet.c, inet_sock_destruct():
- kfree(opt)
+ kfree_rcu(opt, rcu)
net/ipv4/tcp_ipv4.c, tcp_v4_reqsk_destructor():
- kfree(opt)
+ kfree_rcu(opt, rcu)Lo que se supone que te da «liberarlo después»
Esta es la invariante en la que se apoya buena parte del código de red del kernel sin pensarlo dos veces: un lector dentro de rcu_read_lock() puede asumir que la memoria que está mirando sigue ahí, porque la convención es que los escritores posponen su liberación real hasta que cada lector haya terminado, demostrablemente. Eso es RCU, read-copy-update, y es la respuesta del kernel a un problema difícil: ¿cómo dejas que los lectores corran rápido y sin bloqueos, mientras los escritores aún pueden borrar cosas, sin que un lector llegue a tocar memoria que ya desapareció?
Un TCP request socket, el objeto del kernel para una conexión semiabierta durante el handshake, puede llevar opciones IP: datos provistos por el atacante o por la red, adjuntos a la cabecera IP del paquete SYN. El kernel guarda esas opciones detrás de un puntero (ireq_opt en el request socket, inet_opt una vez que se promueve a un socket completo) y las lee más tarde, cuando construye el SYN-ACK o el ACK, dentro de ip_options_build(). Esa lectura ocurre bajo RCU. Tiene que ser así: una segunda CPU que maneja un SYN retransmitido de la misma conexión puede, legítimamente, estar dentro de esa función en el mismo instante en que otra CPU está desmontando la conexión.[1][5]
Dos destructores, un atajo, cero período de gracia
Dos puntos de llamada rompieron el contrato del que depende RCU: inet_sock_destruct() en net/ipv4/af_inet.c, y tcp_v4_reqsk_destructor() en net/ipv4/tcp_ipv4.c; ambos liberaban el mismo puntero de opciones IP con un kfree() normal, de inmediato, en lugar del kfree_rcu() diferido por RCU.[5] Un kfree() normal pone la memoria disponible para reutilizarse en el instante en que se ejecuta: sin período de gracia, sin esperar a que los lectores terminen. Cualquier CPU que siga dentro de ip_options_build(), leyendo el puntero que ese destructor acaba de liberar, está leyendo ahora memoria que puede reasignarse y reescribirse debajo de ella.
El lado lector usaba RCU: barato, sin bloqueos, y tolerante a liberaciones concurrentes siempre que esas liberaciones se diferían. Dos puntos de llamada del lado escritor no cumplieron su parte del trato. Ese desajuste es exactamente el tipo de fallo que un WAF, una comprobación de autenticación o cualquier control a nivel de aplicación nunca va a ver, porque vive por completo dentro de la propia gestión de memoria del kernel, a dos llamadas de función de una sola línea de profundidad en una ruta de destructor.
// Illustrative timeline of the SYN-retransmit race (one of two trigger
// paths), reconstructed from the fixing commit's own description -- not
// a captured trace, and not exploit code.
CPU A (processing the connection) CPU B (retransmitted SYN)
---------------------------------- ----------------------------------
child socket created, inherits the
ireq_opt pointer -- not copied
tcp_v4_reqsk_destructor() runs:
kfree(ireq_opt) <-- freed now,
no grace period
still inside rcu_read_lock(),
ip_options_build() dereferences
the same pointer -- already
freed, possibly reusedEl SYN que retransmites, o el request socket que migras
El commit de corrección describe exactamente dos maneras de caer dentro de esa ventana, y ninguna necesita nada fuera de lo común. La primera: tcp_v4_syn_recv_sock() entrega las opciones IP de un request socket a su socket hijo recién creado sin copiarlas antes, de modo que una segunda CPU que todavía está procesando un SYN retransmitido, comportamiento normal de TCP, puede leer esas opciones después de que ya han sido liberadas.[1][5] Los SYN retransmitidos ocurren constantemente en redes reales, así que un atacante no necesita inventar esta condición. Solo necesita calcular el momento.
El segundo camino necesita una condición adicional: que la migración de request socket de TCP esté explícitamente habilitada, ya sea mediante net.ipv4.tcp_migrate_req=1 o un programa BPF adjunto que use SK_REUSEPORT_SELECT_OR_MIGRATE.[5] Eso no es el valor por defecto en ningún sitio. Cuando está activado, un request socket clonado comparte el mismo puntero de opciones que el original, y el mismo destructor lo libera por debajo de cualquiera de los dos que todavía lo esté usando. Si operas un stack mixto de IPv4/IPv6, un detalle importa aquí: el commit de corrección afirma que IPv6 no se ve afectado por la carrera de retransmisión, porque IPv6 duplica las opciones de su request socket en lugar de compartirlas. Un problema relacionado de migración en IPv6 se deja para un parche aparte.[1][5] Este arreglo es exclusivo de IPv4.
Cuarenta y ocho minutos hasta un puntero, cinco más hasta root
Esta es la parte de la historia que de verdad se volvió viral, y vale la pena contarla con exactitud. La propia publicación de investigación de Anthropic, firmada por su Frontier Red Team, afirma que un modelo de investigación interno aún no publicado encontró y encadenó fallos en un exploit remoto de zero-click contra servidores Linux que ejecutaban un listener multipath TCP: sin credenciales, sin interacción del usuario, root a través de la red.[4][6][7] La publicación cita CVE-2026-98432 por su número. En palabras de la propia Anthropic, tardaron 48 minutos en filtrar un puntero del kernel, y cinco minutos más después de eso en llegar a la ejecución de comandos de shell.[4]
Un detalle que circula junto a esa afirmación en realidad no está en ella. El nombre en código «Project Glasswing» no aparece en ningún lugar de la publicación de Anthropic. Se remonta a una fuente secundaria sin verificar, no a las palabras de Anthropic, y no lo voy a repetir aquí como si Anthropic hubiera confirmado un nombre que nunca usó. Lo que sí está corroborado de forma independiente: dos medios secundarios reportan el mismo número de CVE, la misma puntuación CVSS, y el mismo tiempo de 48 y 5 minutos, y ambos remiten a la propia publicación de Anthropic como fuente.[6][7] Uno de ellos añade una frase que merece una pausa antes de la siguiente sección: «el registro público de esa CVE describe un arreglo para IPv4 TCP, no para multipath TCP, y ambos están vinculados solo por la publicación de Anthropic».[7]
El aviso de Red Hat nunca dice MPTCP. Tampoco lo dice el parche.
Esa frase es toda la historia, y es justo donde falla la mayoría de los consejos de segunda mano. Lee el propio commit de corrección del kernel, y describe dos vías de activación en términos puramente de IPv4 TCP, retransmisión de SYN y migración de request socket, sin mencionar Multipath TCP en ningún lugar.[1][5] Lee el propio boletín de Red Hat, RHSB-2026-016, y es lo mismo: «sistemas que aceptan conexiones TCP entrantes» como población expuesta, una descripción general de condición de carrera, sin MPTCP en la precondición, el vector de ataque ni la guía de mitigación.[2] Dos fuentes primarias. Ninguna de las dos menciona lo que la mitad de internet está diciendo que hay que deshabilitar.
Esto es mi propia lectura al poner ambas fuentes lado a lado, no una frase que ninguna de las dos diga literalmente, así que lo diré con claridad: un listener MPTCP es una manera de llegar a este fallo de forma confiable. No es la precondición que define el fallo. Si un exploit demostrado en concreto usó MPTCP como punto de entrada elegido, y según la sección anterior eso es un detalle plausible pero todavía sin verificar de un exploit reportado, eso te dice algo sobre un camino práctico para ganar la carrera, no sobre lo que realmente requiere la vulnerabilidad en sí misma.[4][7] Deshabilitar MPTCP (net.mptcp.enabled=0) elimina ese tipo concreto de listener como superficie de activación. No toca el use-after-free subyacente, que sigue siendo completamente alcanzable a través del manejo ordinario de conexiones TCP que no usa MPTCP.
// What the vulnerability actually requires (b), versus what one
// reported demonstration used (c). Don't let (c) stand in for (b).
(b) Real preconditions, per the kernel's own commit and Red Hat's
advisory -- neither names MPTCP:
- A listener processing IPv4 TCP SYNs and retransmitted SYNs
(ordinary behavior, any TCP listener), OR
- Request-socket migration explicitly enabled
(net.ipv4.tcp_migrate_req=1, or a BPF
SK_REUSEPORT_SELECT_OR_MIGRATE program) -- not the default.
- Precise timing to win the race window.
- No credentials, no user interaction (PR:N/UI:N).
(c) What one reported demonstration reportedly used to trigger it
reliably:
- A reachable multipath TCP (MPTCP) listener.
Disabling MPTCP removes (c). It leaves (b) exactly where it was.Corregido en el kernel. Todavía no en el kernel que la mayoría ejecuta.
El arreglo upstream ya está disponible, y tiene fecha precisa: las ramas estables quedan libres a partir de 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113 y 6.18.56, con 7.2.10 y el arreglo original etiquetado para la rama mainline 7.3, todo coincidiendo exactamente con los datos que el propio proveedor aportó a NVD.[1] Esa es la parte fácil de esta historia. Si el kernel bajo tu carga de trabajo real coincide con uno de esos números es la parte que requiere comprobación, no suposición.
El propio boletín de Red Hat, actualizado por última vez el 2026-10-30, califica esto como «impacto crítico», pero no nombra ninguna compilación corregida, ninguna errata, ni puntuación CVSS alguna. Solo indica que Red Hat «está acelerando la publicación de arreglos».[2] El propio tracker de Ubuntu, al momento de escribir esto, lista cada versión soportada desde 16.04 hasta 26.10 como Vulnerable o Vulnerable, en curso. Solo 14.04, ya fuera del soporte estándar, está marcada como libre.[3] La mitigación provisional de Red Hat, si estás esperando una compilación, es una regla de nftables que descarta los paquetes IPv4 TCP entrantes cuya cabecera IP lleve opciones, lo cual deja intactas las opciones normales de la capa TCP y se indica explícitamente que no sustituye al parcheo.[2]
Un dato más importa aquí, porque los titulares insisten mucho en la urgencia: al momento de escribir esto, CVE-2026-98432 no está en el catálogo de Vulnerabilidades Explotadas Conocidas de CISA, comprobado por dos vías independientes, y ninguna de las fuentes revisadas aquí documenta explotación confirmada en el mundo real.[8][9] Eso no es lo mismo que decir «seguro para no priorizar». Un 9.8 sin autenticación y sin interacción del usuario se gana urgencia real por sí mismo. Esta es la escala honesta: crítico y sin parchear en la mayoría de las distribuciones, pero no confirmado bajo ataque activo.
Unaffected starting at (NVD vendor data, matches the kernel's own
per-branch fixes exactly):
5.10.272 5.15.223 6.1.190 6.6.159
6.12.113 6.18.56 7.2.10 (original fix tagged for mainline 7.3)
Distro status, read from each vendor's own tracker as of this writing:
Red Hat (RHSB-2026-016): Critical impact. No fixed version or
errata named. "Expediting the release
of fixes." Last updated 2026-10-30.
Ubuntu (security tracker): every supported release, 16.04-26.10,
Vulnerable or Vulnerable, work in
progress. Only 14.04 (out of support)
is clear.Lo que está confirmado, y lo que todavía es solo una afirmación en internet
Circulan otras dos cosas sobre esta CVE, y no voy a resolver ninguna de las dos, porque el registro no me lo permite. Una versión de los hechos dice que Red Hat puntuó esto inicialmente con 5.9, después con 8.1, después con 9.8, atribuyendo parte de la escalada a la divulgación de Anthropic sobre los tiempos del exploit. El boletín actual de Red Hat simplemente no muestra ninguna puntuación, ningún vector, ni ningún historial de puntuaciones en absoluto, así que esa historia concreta de 5.9 a 8.1 a 9.8 no queda confirmada ni refutada por la única fuente primaria disponible para comprobarla.[2]
La otra es si existe realmente en público una prueba de concepto de exploit funcional. Una fuente afirma que hay PoC en GitHub desde el 2026-10-29; otras dicen que el exploit nunca se publicó. No localicé ni verifiqué un repositorio de PoC funcional en ninguno de los dos sentidos. Si alguien te dice con seguridad cuál de las dos es cierta, pídele el repositorio, no la afirmación.
Qué comprobar de verdad esta semana
En el orden que realmente importa, no en el orden que hace el mejor titular.
Patch and verify, in order:
[ ] Confirm the kernel actually RUNNING, not the changelog: 5.10.272 /
5.15.223 / 6.1.190 / 6.6.159 / 6.12.113 / 6.18.56 / 7.2.10 or later.
[ ] If your distro hasn't shipped a fixed build yet, check its own
tracker directly -- don't trust a blog post's snapshot of it,
this one included. Apply Red Hat's interim nftables rule in the
meantime: drop inbound IPv4 TCP with IP header length > 5.
[ ] Check net.ipv4.tcp_migrate_req. If it's 1, or a BPF
SK_REUSEPORT_SELECT_OR_MIGRATE program is attached, the second
trigger path is open regardless of MPTCP.
[ ] Disable MPTCP if you want to (net.mptcp.enabled=0) -- a
reasonable attack-surface reduction, but record it as exactly
that, not as the fix for this CVE.
[ ] Don't use CISA's KEV list as a signal of how exposed you are
here. As of this writing this CVE isn't on it, and that's a
statement about confirmed in-the-wild use, not about your risk.El aviso decía «use-after-free». El diff decía exactamente dónde.
Todos los avisos sobre esta CVE dicen las mismas tres palabras: use-after-free, kernel, IPv4. Ninguno de ellos, por sí solo, te dice que el error real fue que dos destructores eligieron kfree() en lugar de kfree_rcu(), ni que el consejo sobre MPTCP que circula trata la puerta elegida por un exploit como si fuera la única puerta del edificio. El diff me lo dijo. El aviso no lo hizo, y no lo intentaba: para eso no está pensado un aviso.
Esa es exactamente la brecha que busco en una revisión de código segura: no si un changelog dice «corregido», sino si el diff real resuelve la causa raíz o solo el único camino que alguien encontró primero por casualidad. Si mantienes código en C o C++ con ese mismo patrón de propiedad compartida y vida útil diferida, con RCU o de otra forma, y quieres que alguien lea las rutas de los destructores con esa pregunta en mente, ese es el tipo de revisión que hago. Dime qué estás ejecutando, y definimos el alcance.
Fuentes
- [1] NVD: página de detalle de CVE-2026-98432
- [2] Red Hat: boletín de seguridad RHSB-2026-016
- [3] Canonical (Ubuntu): tracker de seguridad para CVE-2026-98432
- [4] Anthropic (Frontier Red Team): «Discovering vulnerabilities with Claude»
- [5] Árbol estable del kernel de Linux: el commit de corrección, leído a través de su espejo público en GitHub
- [6] AI Weekly: informe sobre la afirmación de Anthropic sobre el exploit encadenado
- [7] The Hacker News: informe sobre la afirmación de Anthropic sobre el exploit encadenado
- [8] CISA: catálogo de Vulnerabilidades Explotadas Conocidas, consulta filtrada
- [9] CISA: catálogo de Vulnerabilidades Explotadas Conocidas, feed JSON
¿Necesita que se compruebe esto en su propio producto?
Revisión de seguridad del código¿Quiere aprender a encontrar vulnerabilidades como esta por su cuenta?
Curso individual de revisión de código seguro