Pedit COW (CVE-2026-46331): cómo Core-Admin detecta y mitiga el fallo del kernel Linux


#1

Introducción

pedit COW es una vulnerabilidad de escalada local de privilegios del kernel Linux divulgada en junio de 2026 que permite a un usuario local sin privilegios obtener root en prácticamente cualquier distribución moderna. El fallo reside en la acción de edición de paquetes (act_pedit) del subsistema de control de tráfico (traffic control, tc) del kernel.

Pertenece a la misma familia que Dirty Pipe, CopyFail (CVE-2026-31431) y Dirty Frag (CVE-2026-43284 / CVE-2026-43500): una primitiva determinista de escritura sobre la page cache del kernel que, dirigida contra un binario setuid (su, sudo, passwd), entrega root inmediato.

El CVE fue asignado por el CNA de kernel.org el 16 de junio de 2026, semanas después de que el detalle explotable ya fuera público, y un PoC armado (packet_edit_meme) apareció en GitHub al día siguiente. Por tanto, cualquier servidor Linux sin parchear con la ruta vulnerable abierta es un objetivo real, no teórico.

Mientras los parches del kernel llegan a través de los canales habituales de cada distribución, Core-Admin aplica de forma automática una mitigación efectiva que cierra el camino de explotación sin necesidad de reiniciar.

Este artículo explica cómo funciona el fallo, por qué es especialmente peligroso en hosts de virtualización, a qué distribuciones afecta y cómo Core-Admin lo gestiona a través del checker security_and_mitigations.

Cómo funciona el fallo

La acción pedit de tc permite reescribir campos de las cabeceras de un paquete (MAC, IP, TTL, puertos…) según una lista de “claves” de edición. La función del kernel tcf_pedit_act() (net/sched/act_pedit.c) calcula el rango que debe hacer escribible, el copy-on-write (COW), una sola vez, antes del bucle que aplica esas claves, usando un valor precalculado, tcfp_off_max_hint.

El problema es que ese hint no tiene en cuenta el desplazamiento de cabecera que las claves “typed” resuelven en tiempo de ejecución contra las cabeceras L2/L3/L4. Es decir, en el momento de calcular el COW se desconoce el offset real de algunas escrituras. El resultado es un COW parcial: parte de la región de destino se escribe sin haberse hecho privada, produciendo una escritura fuera de límites que cruza de página.

Y aquí está la clave de la escalada: un sk_buff puede referenciar páginas zero-copy traídas desde un fichero mediante sendfile(2) o splice(2). Esas páginas son las mismas que respaldan el fichero en la page cache compartida del sistema. La escritura fuera de límites cae, por tanto, sobre la copia en memoria de un fichero real en disco.

El exploit público encadena esto así:

  1. Mapea en la page cache la imagen de un binario setuid de root, típicamente /bin/su, vía sendfile.
  2. Instala una acción tc pedit cuidadosamente construida cuyo COW parcial escribe un pequeño payload sobre esas páginas cacheadas.
  3. Ejecuta su: el kernel sirve la imagen envenenada en memoria (no el fichero de disco), que ahora ejecuta el payload del atacante como root.

Un detalle relevante para la respuesta a incidentes: el fichero en disco nunca se modifica. Las comprobaciones de integridad de ficheros (debsums, rpm -V) siguen pasando limpias mientras el atacante ya tiene una shell de root abierta.

Dos condiciones necesarias (y cerrar una basta)

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

  1. act_pedit alcanzable cargado, o autocargable a demanda. tc lo autocarga vía request_module("act_%s", "pedit") la primera vez que se instala una acción pedit.
  2. User namespaces sin privilegio disponibles conceden al atacante un CAP_NET_ADMIN local al namespace, que es lo que tc exige para instalar la acción. Sin user namespaces, un usuario sin privilegios no puede ni alcanzar el código vulnerable.

Core-Admin mitiga bloqueando act_pedit, por las razones que se explican más abajo.

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

La page cache es única para toda la máquina y los contenedores comparten el kernel del host. Esto convierte a pedit COW en un vector de escape de contenedor:

  • Un contenedor (LXC, Docker), incluso no privilegiado, suele tener CAP_NET_ADMIN en su propio network namespace, exactamente la capacidad que necesita el exploit, y sin depender siquiera de que el host permita user namespaces sin privilegio.
  • Al disparar el bug desde dentro del contenedor, la escritura cae sobre la page cache del host, compartida. Envenenar un binario setuid del host equivale a escapar del 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; el act_pedit del host no es alcanzable desde dentro de la VM.

La buena noticia es que 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

La optimización defectuosa entró en el kernel mainline v5.18 (commit 899ee91156e5) y se corrigió en v7.1-rc7. Además, el commit defectuoso fue retroportado a ramas estables más antiguas (4.19.244+, 5.4.195+, 5.10.117+, 5.15.41+, 5.17.9+). En la práctica, la ventana de candidatos es kernel ≥ 4.19 y < 7.1.

A continuación, el estado para las distribuciones soportadas por Core-Admin:

Distribución Kernel típico ¿pedit COW? Notas
debian-lenny / squeeze / wheezy 2.6.x / 3.2 NO EOL anterior a la ventana
debian-jessie 3.16 NO EOL
ubuntu-precise (12.04) 3.2 / 3.13 NO EOL
centos-63 2.6.32 NO EOL
ubuntu-xenial (16.04) 4.4 / 4.15 HWE NO 4.15 < 4.19
debian-stretch 4.9 NO 4.9 < 4.19
centos-7 3.10 (RHEL) NO El hint de 5.18 no está en el 3.10 de RHEL
ubuntu-bionic (18.04) 4.15 / 5.4 HWE DEPENDE GA 4.15 NO; HWE 5.4 SÍ
debian-buster 4.19 LTS
ubuntu-focal (20.04) 5.4 / 5.15 HWE
debian-bullseye 5.10
ubuntu-jammy (22.04) 5.15 / 6.x HWE
debian-bookworm 6.1 Stable actual
ubuntu-noble (24.04) 6.8 LTS actual

Dos matices importantes. (1) Los kernels HWE de Ubuntu rompen el mapeo por versión de la distro: un bionic con HWE 5.4 pasa de “no afectado” (GA 4.15) a “afectado”. Conviene mirar siempre el uname -r real. (2) El fix también se retroporta manteniendo el número de versión, así que un kernel ya parcheado dentro de la ventana [4.19, 7.1) no se distingue por uname de uno vulnerable. Por eso el checker no se basa en una lista de versiones, sino en una detección dinámica de la accesibilidad real del módulo, y ofrece una opción de exclusión para hosts que el operador sabe ya parcheados.

Cómo lo mitiga Core-Admin

Core-Admin incluye el checker security_and_mitigations, diseñado como un 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, Dirty Frag y ssh-keysign-pwn (CVE-2026-46333), y ahora también pedit COW.

Estrategia de mitigación

De las dos condiciones necesarias, Core-Admin elige bloquear el módulo act_pedit en lugar de deshabilitar los user namespaces sin privilegio. La razón es el coste colateral: deshabilitar user namespaces es muy invasivo (rompe runtimes de contenedores, sandboxes de navegador, etc.), mientras que la acción tc pedit está esencialmente sin uso en servidores de hosting, correo, web o base de datos.

La acción se compone de dos pasos:

  1. Despliegue de /etc/modprobe.d/disable-pedit-cow.conf con el siguiente contenido:

    install act_pedit /bin/true
    
    blacklist act_pedit
    

    La directiva blacklist por sí sola no es suficiente: solo bloquea la carga en arranque. La autoload que dispara request_module() cuando un proceso instala una acción pedit seguiría funcionando. La regla install ... /bin/true sustituye la carga del módulo por un no-op, cerrando ese camino.

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

Coste funcional de la mitigación

Software / rol ¿Afectado por el bloqueo? Notas
Apache, Nginx, Postfix, Dovecot, MySQL/MariaDB No No usan tc pedit
OpenSSH, rsync, OpenVPN, WireGuard No
iptables / nftables (NAT, firewall) No El mangle/NAT no usa la acción pedit de tc
Qdiscs normales (htb, fq_codel, cake, tbf…) No El traffic shaping habitual no usa pedit
Contenedores LXC / Docker (red bridge/veth) No Confirmado en producción
Ceph (mon / mgr / osd) No TCP plano
OVS con hardware offload (tc-flower) SmartNIC Mellanox hw-offload=true → ver skip
Neutron QoS por linuxbridge-agent (tc) Posible Marcado/edición de cabecera vía tc

A efectos prácticos, en un servidor típico de hosting / correo / web / virtualización, bloquear act_pedit es una operación de riesgo cero. La única excepción real son los nodos de red que usan OVS con descarga por hardware (tc-flower offload sobre SmartNIC), donde act_pedit traduce las reescrituras de cabecera de los flujos descargados.

Nota de campo. En el despliegue sobre nuestras nubes OpenStack, ni los nodos controladores ni los de cómputo tenían act_pedit en uso: usan el datapath estándar de OVS (kernel/OpenFlow), no tc-flower con offload. El bloqueo fue inocuo también ahí. El caso que pide la opción de exclusión es estrictamente el de SmartNIC con offload por tc.

Lógica de detección

detect_pedit_cow() actúa en dos fases:

  1. Filtro de versión: si el kernel está fuera de la ventana [4.19, 7.1), el sistema se declara no vulnerable sin más comprobaciones (por debajo de 4.19 el commit no se retroportó; desde 7.1 el fix ya está en mainline).
  2. Accesibilidad del módulo (dentro de la ventana): clasifica act_pedit en uno de cuatro estados:
    • loaded: cargado en memoria (vulnerable).
    • autoloadable: no cargado, pero modprobe -n -v lo cargaría a demanda (vulnerable).
    • blocked: existe una regla install ... que bloquea la autoload (no vulnerable).
    • absent: el módulo no está disponible en este kernel (no 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_pedit_cow() confirma tres invariantes independientes tras aplicar la mitigación:

  1. /etc/modprobe.d/disable-pedit-cow.conf existe y contiene la regla install act_pedit /bin/true.
  2. act_pedit no aparece en lsmod.
  3. modprobe -n -v act_pedit está bloqueado (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 (p. ej. nodo OpenStack en focal):

[OK] pedit COW (CVE-2026-46331): vulnerable, mitigation applied and verified
     (modprobe.d rule; act_pedit unloaded; dry-run blocked)

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

[OK] pedit COW (CVE-2026-46331): system not vulnerable
     (kernel 6.8.0, act_pedit blocked)

En una máquina fuera de la ventana por kernel demasiado antiguo (p. ej. bionic en GA 4.15 o un baremetal jessie):

[OK] pedit COW (CVE-2026-46331): system not vulnerable
     (kernel 4.15.0 predates the pedit COW window
      (offending hint optimisation backported no earlier than 4.19))

Opción de exclusión: skip_pedit_cow_mitigation

Para los pocos hosts donde bloquear act_pedit tiene coste (nodos OVS con descarga por hardware, o servidores que de verdad usan la acción tc pedit para traffic shaping avanzado) Core-Admin expone la opción skip_pedit_cow_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 “pedit COW (CVE-2026-46331) detected, system vulnerable but mitigation skipped by operator option” y deja un aviso en el log del agente.

Esta opción también es útil en hosts que el operador sabe ya parcheados dentro de la ventana [4.19, 7.1), donde uname no permite distinguir el kernel corregido del vulnerable.

Verificación manual

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

# 1. Versión del kernel (¿está en la ventana 4.19 .. 7.1?)
uname -r

# 2. ¿Está act_pedit cargado, y lo está usando algo? (mira la columna "Used by")
lsmod | awk '$1=="act_pedit"{print "act_pedit cargado, refcount="$3" usado_por="$4}'

# 3. ¿Hay filtros tc que usen pedit? (señal de nodo OVS/QoS real)
tc filter show dev any 2>/dev/null | grep -i pedit || echo "sin filtros pedit"

# 4. ¿La autoload está bloqueada?
modprobe -n -v act_pedit 2>&1 | grep -q '/bin/true\|/bin/false\|/bin/:' \
  && echo "act_pedit: blocked" \
  || echo "act_pedit: AUTOLOAD ABIERTA - VULNERABLE"

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

Si el paso 2 muestra refcount=0 (o nada) y el paso 3 no devuelve filtros, el bloqueo es seguro. Si hay refcount > 0 o filtros pedit, es un nodo OVS/QoS real: activa skip_pedit_cow_mitigation.

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-pedit-cow.conf <<'EOF'
install act_pedit /bin/true

blacklist act_pedit
EOF
chmod 0644 /etc/modprobe.d/disable-pedit-cow.conf

# 2. Descargar el módulo si está cargado (no fallará si nada lo usa)
lsmod | awk '{print $1}' | grep -qx act_pedit && rmmod act_pedit

# 3. Verificar
modprobe -n -v act_pedit 2>&1 | grep -q '/bin/true' && echo "act_pedit blocked"
lsmod | grep -qx act_pedit && echo "AÚN CARGADO - requiere reinicio" || echo "act_pedit descargado"

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, conviene retirar la mitigación para que act_pedit vuelva a estar disponible:

rm /etc/modprobe.d/disable-pedit-cow.conf
# La siguiente operación de tc que necesite el módulo lo cargará normalmente.

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

Preguntas frecuentes

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

No. La mitigación es efectiva inmediatamente: el fichero modprobe.d bloquea cualquier carga futura, y rmmod descarga el módulo en caliente. Solo se requiere reinicio si act_pedit está ocupado por un qdisc activo en el momento de la mitigación (caso muy poco habitual fuera de nodos OVS con offload), 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 y la red bridge/veth de LXC y Docker no usan la acción tc pedit. 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 es el 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 nodos de red que usan OVS con descarga por hardware (tc-flower offload sobre SmartNIC), o en hosts que usan la acción tc pedit para reescritura de cabeceras / traffic shaping avanzado. En esos casos activa skip_pedit_cow_mitigation. También en hosts que sabes ya corren un kernel con el fix retroportado.

P: ¿Y los nodos OpenStack normales?

Sin descarga por hardware, OpenStack usa el datapath estándar de OVS (módulo openvswitch + flujos OpenFlow), que no carga act_pedit. Controladores y nodos de cómputo se mitigan sin coste. Solo los despliegues con SmartNIC y hw-offload=true necesitan la opción de exclusión.

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, 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: el ataque no modifica el fichero en disco (solo su copia en la page cache) así que debsums / rpm -V pasarán limpios. La verificación de integridad clásica no sirve aquí. Los indicios más fiables son anómalos a nivel de sesión: procesos root con padres inesperados, accesos shell no justificados, o su/sudo ejecutados desde contextos extraños en los logs de autenticación. Reiniciar el servidor descarta la page cache envenenada (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 (estado system not vulnerable (act_pedit blocked)).

Referencias