GRE-Bond Confusion (CVE-2026-43456): cómo Core-Admin detecta y mitiga el fallo del kernel Linux


#1

Introducción

GRE-Bond Confusion es una vulnerabilidad de escalada local de privilegios (LPE) del kernel Linux divulgada en mayo de 2026 que permite a un usuario local sin privilegios obtener root en prácticamente cualquier distribución. El fallo es una confusión de tipos (type confusion) en el driver de bonding (agregación de enlaces) del kernel, y su característica más alarmante es el rango de afectación: el código defectuoso lleva presente desde 2007 (kernel 2.6.24) y solo se corrige a partir de marzo de 2026. Es decir, casi cualquier servidor Linux que hoy esté en producción lo contiene.

El fallo fue premiado con unos 80.000 dólares a través del programa kernelCTF de Google, y el investigador reporta una fiabilidad de explotación superior al 99 % en menos de un segundo. No es un race frágil: es una primitiva determinista que escala a root completo (fuga de KASLR + escritura/ejecución en el kernel).

Mientras los parches del kernel llegan a través de los canales habituales de cada distribución —y en muchas máquinas EOL no llegarán nunca—, Core-Admin aplica de forma automática una mitigación efectiva que cierra el camino de explotación sin reiniciar y sin tocar la agregación de enlaces.

Este artículo explica cómo funciona el fallo, por qué su alcance es tan amplio, a qué distribuciones afecta y cómo Core-Admin lo gestiona a través del checker security_and_mitigations.

Cómo funciona el fallo

El driver de bonding permite agrupar varias interfaces físicas (slaves) bajo una interfaz lógica bondX para redundancia y ancho de banda. Cuando se esclaviza el primer dispositivo a un bond, el kernel copia una serie de propiedades del slave al bond. El problema está en la función bond_setup_by_slave() (net/bonding/bond_main.c), que copia el puntero a las operaciones de cabecera sin comprobar que ambos tipos de dispositivo sean compatibles:

bond_dev->header_ops = slave_dev->header_ops;

Para un slave Ethernet normal esto es inocuo (comparten eth_header_ops). Pero si el dispositivo esclavizado es no-Ethernet —un túnel GRE o IP6GRE—, el bond adopta las funciones de cabecera del túnel (ipgre_header(), etc.). Esas funciones hacen netdev_priv(dev) esperando su propia estructura privada (struct ip_tunnel), pero sobre el dispositivo bond netdev_priv() devuelve struct bonding. Se lee y escribe un área de memoria interpretándola como un tipo que no es: confusión de tipos.

El exploit público encadena esto en tres fases:

  1. Fuga de KASLR — con un slave IP6GRE, la confusión solapa struct bonding::recv_probe con struct ip6_tnl::parms.laddr (ambos en el offset 0x38), filtrando punteros del kernel a través de paquetes IPv6 recibidos.
  2. Manipulación de flags — a través de ipgre_header(), un desbordamiento escribe greh->flags sobre skb_shared_info->flags, activando indebidamente SKBFL_ZEROCOPY_ENABLE.
  3. Ejecución de código — se corrompe el callback zero-copy (uarg->callback) para saltar a una dirección de kernel controlada por el atacante.

Para afinar el layout de memoria, el exploit llega a construir 329 dispositivos GRE encadenados que ajustan bond->needed_headroom a un valor exacto. Es un trabajo de ingeniería fino, pero perfectamente reproducible: de ahí el >99 % de éxito.

Tres condiciones necesarias (y cerrar una basta)

El ataque necesita las tres cosas a la vez; bloquear cualquiera de ellas lo cierra por completo:

  1. bonding alcanzable — cargado o autocargable, para que exista un dispositivo bond que confundir.
  2. Un módulo de túnel GRE alcanzable (ip_gre y/o ip6_gre) — para poder crear el slave no-Ethernet que dispara la confusión. La cadena publicada usa ambos: IP6GRE para la fuga y GRE para la ejecución.
  3. User namespaces sin privilegio disponibles — conceden al atacante un CAP_NET_ADMIN local al namespace, que es lo que exige crear el bond y los túneles.

Core-Admin mitiga bloqueando los módulos GRE (ip_gre, ip6_gre), por las razones que se explican más abajo. Aunque la raíz no es estrictamente exclusiva de GRE (cualquier slave cuyas header_ops dereferencien netdev_priv() como un tipo ajeno podría confundir el bond), GRE/IP6GRE son el vector práctico y demostrado: son los túneles con header_ops propias alcanzables vía CAP_NET_ADMIN.

Por qué su alcance es tan amplio

Es de los peores casos posibles en cuanto a superficie:

  • 19 años de ventana (2.6.24 → 6.12.77). No es un bug de una feature reciente que afecte a unas pocas versiones: está en casi todo kernel Linux en producción.
  • Ingredientes ubicuos: bonding + GRE están presentes (aunque sea como módulos autocargables) en prácticamente todos los kernels de distribución. No requiere hardware ni configuración especial.
  • No exige root previo: basta un usuario local sin privilegios en las distros con user namespaces abiertos.
  • El largo rabo de sistemas EOL: buena parte del parque son distribuciones que nunca recibirán el fix. Ahí, un mecanismo que cierre el vector sin depender del vendor es la única red de seguridad disponible.

Por qué es especialmente peligroso en Proxmox / Docker / LXC

Los contenedores comparten el kernel del host. Un contenedor (LXC, Docker), incluso no privilegiado, suele disponer de CAP_NET_ADMIN en su propio network namespace, justo la capacidad que el exploit necesita para montar el bond y los túneles GRE. Si los módulos bonding e ip_gre/ip6_gre están cargados en el host, la confusión de tipos se dispara sobre el kernel del host, lo que convierte el fallo en un potencial escape de contenedor a root del host.

Por el contrario, una máquina virtual (KVM/QEMU, como las de OpenStack Nova) ejecuta su propio kernel y está aislada por el hipervisor: un guest que explota su kernel se queda root dentro del guest.

La mitigación se aplica en el kernel del host, así que un solo bloqueo en el anfitrión protege simultáneamente a todos los contenedores que corren sobre él. Un contenedor sin privilegios no puede saltarse el modprobe.d del host (cargar módulos requiere CAP_SYS_MODULE en el namespace inicial, que no tiene).

Distribuciones afectadas

El código defectuoso entró con la herencia de cabeceras del bonding en el commit 1284cd3a2b74 (kernel 2.6.24, 2007) y se corrige en el commit 950803f72547 (marzo de 2026), que introduce funciones envoltorio bond_header_ops que redirigen al slave activo usando su dispositivo, de modo que netdev_priv() siempre ve el tipo correcto. Las ramas corregidas son posteriores a 6.12.77 más los retroportes de cada distribución.

En la práctica esto significa que todas las plantillas soportadas por Core-Admin están dentro de la ventana vulnerable: la más antigua (lenny, 2.6.26) ya está por encima del punto de introducción, y la más nueva (noble, 6.8) muy por debajo del de corrección. Ninguna se libra por versión.

id Plantilla Kernel típico ¿Afectada? Camino de parche userns no-priv por defecto
239/240 debian-lenny 2.6.26 EOL — nunca off
242/368 debian-squeeze 2.6.32 EOL — nunca off
243 debian-wheezy 3.2 EOL — nunca off
238 debian-jessie 3.16 EOL — nunca off
283 debian-stretch 4.9 EOL — nunca off
324 debian-buster 4.19 ELTS (Freexian) off
395 debian-bullseye 5.10 LTS — vía update off
471 debian-bookworm 6.1 Soportada — vía update on
244 ubuntu-precise (12.04) 3.2 EOL — nunca on
245 ubuntu-xenial (16.04) 4.4 ESM (Pro) on
287 ubuntu-bionic (18.04) 4.15 ESM (Pro) on
358 ubuntu-focal (20.04) 5.4 Soportada — vía update on
452 ubuntu-jammy (22.04) 5.15 Soportada — vía update on
490 ubuntu-noble (24.04) 6.8 Soportada — vía update on (AppArmor restringe)
246 centos-63 2.6.32 EOL — nunca off
451 centos-7 3.10 EOL (jun-2024) off (max_user_ns=0)

Lectura de la matriz:

  • Vulnerables por código: todas. El filtro de versión del checker (≥ 2.6.24) se cumple en cada una, así que la detección se apoya en la accesibilidad real de los módulos (bonding + GRE), no en el número de versión.
  • Donde la mitigación es la ÚNICA protección: las EOL (lenny, squeeze, wheezy, jessie, stretch, precise, centos-63, centos-7). Nunca recibirán el kernel corregido → el bloqueo del autoload de GRE del checker es su único cierre real del vector. Es donde más valor aporta.
  • Soportadas (buster-ELTS, bullseye, bookworm, focal, jammy, noble): acabarán trayendo el fix retroportado, pero uname no lo distingue del kernel vulnerable, así que el checker aplica el bloqueo igualmente (cinturón y tirantes, inofensivo).
  • Explotabilidad práctica (matiz, no cambia el veredicto): el exploit necesita CAP_NET_ADMIN vía user namespaces sin privilegio. En Debian ≤ bullseye y en RHEL/CentOS eso viene deshabilitado por defecto, lo que sube el listón; en Ubuntu y Debian bookworm+ viene habilitado. El checker mitiga el bug del kernel con independencia del estado de userns, que es lo correcto.

Cómo lo mitiga Core-Admin

Core-Admin incluye el checker security_and_mitigations, diseñado como punto único donde se concentran las detecciones y mitigaciones de fallos críticos del kernel mientras llegan los parches oficiales. Este checker ya cubría CopyFail (CVE-2026-31431), Dirty Frag (CVE-2026-43284 / CVE-2026-43500), ssh-keysign-pwn (CVE-2026-46333) y pedit COW (CVE-2026-46331), y ahora también GRE-Bond Confusion.

Estrategia de mitigación

De las tres condiciones necesarias, Core-Admin elige bloquear los módulos de túnel GRE (ip_gre, ip6_gre) en lugar de bloquear el módulo bonding o deshabilitar los user namespaces. Las tres opciones cerrarían el fallo, pero el coste colateral es muy distinto:

  • Bloquear bonding rompería la agregación de enlaces (bond0) en el siguiente arranque → posible pérdida de red en servidores físicos con NIC teaming. Es la recomendación literal de algún aviso, pero demasiado peligrosa para aplicarla de forma desatendida.
  • Deshabilitar user namespaces es muy invasivo (rompe runtimes de contenedores y sandboxes de navegador). Core-Admin ya rechaza este compromiso en el handler de pedit COW.
  • Los módulos GRE son el ingrediente concreto que convierte el bond en un dispositivo con tipos confundidos, y los túneles GRE están esencialmente sin uso en servidores de hosting, correo, web o base de datos. En esos hosts ip_gre/ip6_gre son meramente autocargables (no cargados), de modo que bloquear su autoload tiene coste cero. El bonding se deja intacto y sigue funcionando.

La acción se compone de dos pasos:

  1. Despliegue de /etc/modprobe.d/disable-gre-bond-confusion.conf con el siguiente contenido:

    install ip_gre /bin/true
    install ip6_gre /bin/true
    
    blacklist ip_gre
    blacklist ip6_gre
    

    La directiva blacklist por sí sola no basta: solo bloquea la carga en arranque. La autoload que dispara request_module() cuando se crea un dispositivo de tipo GRE seguiría funcionando. La regla install ... /bin/true sustituye la carga del módulo por un no-op, cerrando ese camino.

  2. rmmod ip_gre / rmmod ip6_gre si están cargados, en modo best-effort. El fichero modprobe.d se escribe primero, de modo que aunque el rmmod falle (módulo ocupado por un túnel activo o por el demux gre), las cargas futuras ya quedan bloqueadas. El verificador avisa con CRITICAL si tras la mitigación algún módulo sigue cargado, indicando que se requiere reinicio.

Coste funcional de la mitigación

Software / rol ¿Afectado por el bloqueo? Notas
Bonding / agregación de enlaces (bond0) No El handler NO toca bonding — sigue funcionando
Apache, Nginx, Postfix, Dovecot, MySQL/MariaDB No No usan túneles GRE
OpenSSH, rsync, OpenVPN, WireGuard No WireGuard no es GRE
iptables / nftables (NAT, firewall) No El NAT/firewall no depende de ip_gre
VLANs, bridges (vmbr*), veth No Ninguno usa GRE
Contenedores LXC / Docker (red bridge/veth, VXLAN) No Docker overlay usa VXLAN, no GRE
Túneles GRE / GRETAP / IP6GRE Site-to-site GRE, PPTP con GRE de datos → ver skip
OVS con túneles GRE (tenant networks GRE) Neutron tunnel_types = gre → ver skip

A efectos prácticos, en un servidor típico de hosting / correo / web / virtualización, bloquear los módulos GRE es una operación de riesgo cero. Las únicas excepciones reales son los hosts que terminan túneles GRE de verdad o los nodos de red OpenStack cuya red tenant usa GRE como tipo de encapsulado.

Nota de campo. En el despliegue sobre nuestro parque, ni un solo nodo tenía GRE en uso: los web, los Proxmox con LXC/Docker, un firewall sensible y los nodos OpenStack (controlador y cómputo) salieron todos con ip_gre/ip6_gre sin cargar, cero netdevs GRE, cero puertos GRE en OVS y tunnel_types vacío en el ML2 de Neutron (red tenant por VLAN/flat, no por túnel). El bloqueo fue inocuo en todos. El caso que pide la opción de exclusión es estrictamente el de túneles GRE reales o Neutron con tunnel_types = gre.

Lógica de detección

detect_gre_bond_confusion() actúa en tres pasos:

  1. Filtro de versión: si el kernel es anterior a 2.6.24 (donde entró la herencia de cabeceras del bonding), el sistema se declara no vulnerable. En la práctica todos los kernels que gestiona Core-Admin son más nuevos, así que este filtro casi siempre se cumple; un fix retroportado mantiene el número de versión y no puede distinguirse aquí, de ahí que la detección se apoye en la accesibilidad de los módulos.
  2. Accesibilidad de bonding: si está blocked o absent no hay bond que confundir → no vulnerable.
  3. Accesibilidad de los módulos GRE (ip_gre, ip6_gre): clasifica cada uno en loaded / autoloadable (alcanzable) o blocked / absent (seguro). Si bonding es alcanzable y al menos un módulo GRE es alcanzable → vulnerable.

Cualquier comando que reemplace el insmod (/bin/true, /bin/false, /bin/:) cuenta como bloqueo válido — esto permite que la mitigación coexista con reglas escritas por otras herramientas de hardening o por la propia distribución.

Lógica de verificación

verify_gre_bond_confusion() confirma tres invariantes independientes tras aplicar la mitigación:

  1. /etc/modprobe.d/disable-gre-bond-confusion.conf existe y contiene las reglas install ip_gre /bin/true e install ip6_gre /bin/true.
  2. Ni ip_gre ni ip6_gre aparecen en lsmod.
  3. modprobe -n -v está bloqueado para ambos (o el módulo no existe en este kernel).

Si alguno falla, el checker devuelve CRITICAL indicando exactamente qué invariante no se cumple.

Salida del checker

En una máquina vulnerable donde Core-Admin acaba de aplicar la mitigación:

[OK] GRE-Bond Confusion (CVE-2026-43456): vulnerable, mitigation applied and verified
     (modprobe.d rules; ip_gre/ip6_gre unloaded; dry-run blocked)

En las pasadas siguientes, ya en estado estacionario (comportamiento idempotente, no se reescribe nada):

[OK] GRE-Bond Confusion (CVE-2026-43456): system not vulnerable
     (kernel 6.8.0, bonding autoloadable but GRE tunnel modules not reachable
      (ip_gre blocked; ip6_gre blocked))

En una máquina donde bonding no está disponible (no hay bond que confundir):

[OK] GRE-Bond Confusion (CVE-2026-43456): system not vulnerable
     (kernel 5.10.0, bonding absent (no reachable bond device to confuse))

Opción de exclusión: skip_gre_bond_confusion_mitigation

Para los pocos hosts donde bloquear GRE tiene coste —los que terminan túneles GRE/GRETAP/IP6GRE reales, o los nodos OpenStack cuya red tenant usa GRE— Core-Admin expone la opción skip_gre_bond_confusion_mitigation desde la configuración del checker:

  • Por defecto: false → la mitigación se aplica.
  • Si se activa: la detección sigue ejecutándose, pero no se aplica la mitigación. El checker devuelve OK con el mensaje informativo “GRE-Bond Confusion (CVE-2026-43456) detected, system vulnerable but mitigation skipped by operator option” y deja un aviso en el log del agente.

Es útil también en hosts que el operador sabe ya parcheados, donde uname no permite distinguir el kernel corregido del vulnerable. En ningún caso esta opción tiene que ver con el bonding: la agregación de enlaces nunca se ve afectada por este handler.

Verificación manual

Si quieres comprobar manualmente el estado de un servidor sin pasar por el checker:

# 1. Versión del kernel (bug presente desde 2.6.24; fix tras 6.12.77)
uname -r

# 2. ¿Es alcanzable bonding? (precondición: hace falta un bond)
lsmod | awk '$1=="bonding"{print "bonding cargado refcount="$3}'

# 3. ¿Están los módulos GRE cargados o en uso? (mira "Used by" y los netdevs)
lsmod | grep -E '^(ip_gre|ip6_gre) '
ip -o link show type gre 2>/dev/null; ip -o link show type ip6gre 2>/dev/null
ovs-vsctl --columns=name find interface type=gre 2>/dev/null

# 4. ¿La autoload de GRE está bloqueada?
for m in ip_gre ip6_gre; do
  modprobe -n -v $m 2>&1 | grep -q '/bin/true\|/bin/false\|/bin/:' \
    && echo "$m: blocked" \
    || echo "$m: AUTOLOAD ABIERTA"
done

# 5. ¿Existe el fichero de mitigación de Core-Admin?
test -f /etc/modprobe.d/disable-gre-bond-confusion.conf \
  && echo "mitigación Core-Admin presente" \
  || echo "mitigación Core-Admin NO desplegada"

Si el paso 3 no muestra módulos GRE cargados, ni netdevs GRE, ni puertos GRE en OVS, el bloqueo es seguro. Si ip_gre/ip6_gre tienen refcount > 0, hay netdevs GRE en estado UP, aparecen interfaces type=gre en OVS, o Neutron tiene tunnel_types = gre, es un host con GRE real: activa skip_gre_bond_confusion_mitigation.

Un one-liner de inventario para barrer por SSH y decidir “seguro / revisar”:

echo "$(hostname): ipgre_ref=$(lsmod|awk '$1=="ip_gre"{print $3}') ip6gre_ref=$(lsmod|awk '$1=="ip6_gre"{print $3}') gre_netdevs=$(ip -o link show type gre 2>/dev/null|grep -c .) ovs_gre=$(ovs-vsctl --columns=name find interface type=gre 2>/dev/null|grep -c .) neutron_tun=$(grep -hoE 'tunnel_types.*' /etc/neutron/plugins/ml2/*.ini 2>/dev/null|tr -d ' ')"

Si en un nodo ipgre_ref/ip6gre_ref > 0, gre_netdevs/ovs_gre > 0, o neutron_tun contiene gre, ese nodo va con skip hasta confirmar; el resto es aplicar directo con impacto cero.

Aplicar la mitigación a mano (sin Core-Admin)

Si el checker no se puede ejecutar por algún motivo, los pasos manuales son:

# 1. Bloquear cargas futuras
cat > /etc/modprobe.d/disable-gre-bond-confusion.conf <<'EOF'
install ip_gre /bin/true
install ip6_gre /bin/true

blacklist ip_gre
blacklist ip6_gre
EOF
chmod 0644 /etc/modprobe.d/disable-gre-bond-confusion.conf

# 2. Descargar los módulos si están cargados (no fallará si nada los usa)
for m in ip_gre ip6_gre; do lsmod | awk '{print $1}' | grep -qx $m && rmmod $m; done

# 3. Verificar
for m in ip_gre ip6_gre; do
  modprobe -n -v $m 2>&1 | grep -q '/bin/true' && echo "$m blocked"
  lsmod | grep -qx $m && echo "$m AÚN CARGADO - requiere reinicio" || echo "$m descargado"
done

Nótese que estos pasos no tocan el módulo bonding: la agregación de enlaces permanece operativa.

Revertir la mitigación tras el kernel parcheado

Cuando la distribución publique el kernel corregido y el servidor se haya reiniciado sobre la versión nueva, o si necesitas volver a usar túneles GRE, retira la mitigación:

rm /etc/modprobe.d/disable-gre-bond-confusion.conf
# La siguiente operación que necesite los módulos GRE los cargará normalmente.

Si además has puesto skip_gre_bond_confusion_mitigation, recuerda que mientras esté activo el checker no recreará el fichero.

Preguntas frecuentes

P: ¿Esta mitigación rompe mi agregación de enlaces / bond0?

No. Es la duda más importante y la respuesta es rotunda: el handler no toca el módulo bonding. Bloquea únicamente los túneles GRE (ip_gre, ip6_gre), que son el ingrediente que dispara la confusión de tipos. Tu NIC teaming / link aggregation sigue funcionando exactamente igual, incluido tras reiniciar.

P: ¿Necesito reiniciar el servidor para que la mitigación surta efecto?

No. Es efectiva de inmediato: el fichero modprobe.d bloquea cualquier carga futura, y rmmod descarga los módulos en caliente. Solo se requiere reinicio si algún módulo GRE está ocupado por un túnel activo en el momento de la mitigación (caso muy poco habitual), y el propio checker lo indica con CRITICAL.

P: ¿La mitigación rompe mi servidor web / correo / base de datos / contenedores?

No. Apache, Nginx, Postfix, Dovecot, MySQL/MariaDB, PHP, OpenSSH, OpenVPN, WireGuard, iptables/nftables, las VLANs, los bridges y la red bridge/veth de LXC y Docker no usan túneles GRE. La red overlay de Docker usa VXLAN, no GRE. La mitigación es transparente para todos ellos.

P: Tengo un host Proxmox con contenedores. ¿Tengo que mitigar dentro de cada contenedor?

No. El bloqueo se aplica en el kernel del host, que comparten todos los contenedores. Una sola mitigación en el anfitrión Proxmox protege a todas las LXC y a los Docker anidados a la vez, y ningún contenedor sin privilegios puede saltársela.

P: ¿Cuándo NO debo aplicar la mitigación automática?

En hosts que terminan túneles GRE / GRETAP / IP6GRE reales (por ejemplo, enlaces site-to-site GRE), o en nodos de red OpenStack cuya red tenant usa GRE como tipo de encapsulado (tunnel_types = gre en el ML2 de Neutron). En esos casos activa skip_gre_bond_confusion_mitigation. También en hosts que sabes ya corren un kernel con el fix retroportado.

P: ¿Y los nodos OpenStack normales?

Depende del tipo de red tenant. Si usan VLAN, flat, VXLAN o Geneve (OVN usa Geneve), ip_gre/ip6_gre no intervienen y el bloqueo es seguro. Solo los despliegues con red tenant GRE necesitan la opción de exclusión. En nuestro parque, controladores y nodos de cómputo tenían tunnel_types vacío (VLAN/flat) y se mitigaron sin coste.

P: ¿Por qué bloquear GRE y no el bonding, si el aviso original dice “deshabilitar bonding”?

Porque bloquear el bonding rompería la agregación de enlaces en servidores que sí la usan —un riesgo operativo inaceptable para una acción automática y desatendida—, mientras que GRE está sin uso en la inmensa mayoría de servidores. Bloquear GRE cierra el mismo vector (sin túnel GRE no hay slave no-Ethernet que confunda al bond) con coste cero. Es el mismo criterio de “elegir el bloqueo menos invasivo entre opciones equivalentes” que aplicamos en pedit COW.

P: ¿Qué pasa si mi distribución ya incluye una mitigación oficial?

El checker reconoce reglas install con cualquier comando no-op (/bin/true, /bin/false, /bin/:). Si la propia distribución ha desplegado un fichero de hardening con una de esas reglas sobre ip_gre/ip6_gre, Core-Admin lo detectará como mitigación válida y reportará OK sin escribir su propio fichero.

P: ¿Cómo puedo saber si mi sistema fue explotado antes de aplicar la mitigación?

Es difícil por medios estáticos. Los indicios más fiables son a nivel de sistema: kernel oops o cuelgues relacionados con el bonding en dmesg, procesos root con padres inesperados, o accesos shell no justificados en los logs de autenticación. Reiniciar el servidor descarta el estado de kernel manipulado (pero no elimina otras puertas traseras que el atacante haya podido instalar ya con root).

P: ¿Tengo que volver a ejecutar el checker manualmente?

No. Core-Admin ejecuta los checkers periódicamente como parte del ciclo normal de monitorización. La mitigación se aplica en la primera pasada sobre cada servidor afectado, y a partir de ahí las pasadas siguientes simplemente verifican que sigue en su sitio.

Referencias