Seguridad

Exploit de escalada de privilegios local: cómo logra root en OpenBSD 7.9

Una historia real con OpenBSD en el borde

Tengo varios firewalls OpenBSD en producción desde hace años. Los elegí precisamente por lo que la gente presume del proyecto: auditoría proactiva del código, mitigaciones de kernel adelantadas a su tiempo, y una cultura de seguridad que no tolera atajos. Por eso el aviso que publicó la firma Calif el 16 de octubre de 2026, bautizado como “Big Fish: Exploiting a 37-Year-Old Kernel Bug in OpenBSD”, me llamó la atención de inmediato. No es el típico CVE de aplicación. Es una cadena de escalada de privilegios local hasta root sobre OpenBSD 7.9 que evade tres mitigaciones de kernel que damos por sentadas: KARL, SMAP y SMEP.

El autor, Andrew Griffiths, publicó también el PoC en el repositorio público github.com/califio/publications. Lo que volvió urgente este post no es el exploit en sí, sino el hecho de que el bug raíz vive en sys/sys/shm.h y trae un contador firmado de 16 bits — el mismo shm_nattch que llevan arrastrando los BSD originales desde hace casi cuatro décadas. Eso significa que cualquier release anterior a 7.9 también arrastra la misma familia de problemas. Si hoy tienes OpenBSD en producción, este artículo te interesa aunque nunca toques shmat().

TL;DR — Lo que vas a tener al final

  • Tiempo de revisión: 15-20 minutos para auditar tu fleet y aplicar syspatch.
  • Requisitos: Un host OpenBSD 7.9 (o anterior) con acceso root y syspatch(8) listo. Si corres OpenBSD 7.6/7.7/7.8, los errata ya están disponibles en sus ramas -stable.
  • Resultado: Dos parches aplicados (018 + 026), cuenta de shm_nattch libre del overflow, kernel con atomic decrements endurecidos. Más una lista de mitigaciones de defensa profunda para reducir la superficie explotada.
  • Troubleshooting: Si ipcs(1) dejó de funcionar tras aplicar los errata, es esperado — la nota oficial del proyecto advierte sobre el cambio de ABI. Actualiza también libpthread y libsane si las usas contra SysV IPC.

Por qué importa este caso aunque uses Linux en el resto

OpenBSD no tiene la cuota de mercado de un RHEL o un Ubuntu Server. Pero vive en lugares donde la seguridad cuenta mucho: firewalls perimetrales de Carrier-Grade NAT, routers BGP de ISPs, frontends VPN, appliances de filtrado, jump hosts de sysadmins paranoicos (como yo). Un compromiso root en uno de esos puntos es un beachhead perfecto para pivotar hacia tu red interna, sniffar credenciales en tránsito, o firmar binarios con tu CA corporativa.

Además, lo que Calif demostró no es solo “OpenBSD es inseguro”. Es todo lo contrario. OpenBSD es probablemente el sistema más revisado de toda la familia Unix-like, y aun así tenía un contador firmado de 16 bits en una estructura usada desde 1989 que sobrevivió a tres generaciones de mitigaciones. La lección para los sysadmins LATAM no es migrar a OpenBSD o dejar OpenBSD, es entender que las mitigaciones modernas son necesarias pero no suficientes: toca agregar defense in depth a nivel de sistema, no confiar solo en KARL/SMAP/SMEP/W^X.

Qué exactamente encontraron los investigadores

El reporte técnico de Calif es claro: identificaron dos vulnerabilidades relacionadas en el kernel de OpenBSD que se encadenan en una escalada local de privilegios hasta root sin necesidad de un information disclosure previo. Los entregables en su paper:

  • Integer overflow en la función shmat(4): el campo shm_nattch de la estructura struct shmid_kernel está declarado como un short con signo, lo que le da un rango de -32768 a 32767. Cuando se llega a 32767 y se vuelve a incrementar, hace wrap a un valor negativo. Esto desbalancea el reference counting de la región de memoria compartida y prepara el terreno para un use-after-free.
  • Write fuera de bounds vía decrementos atómicos: una vez que el contador está en estado patológico, el código de salida del socket IP usa operaciones TAILQ_REMOVE que terminan modificando bytes en una zona de mapeo PTE self-referencial. Esa zona pasa de accesible sólo en user mode a accesible en supervisor mode, lo cual es exactamente el bypass de SMAP y SMEP.

El detalle histórico que hace este caso jugoso: el campo shm_nattch proviene de los fuentes originales de BSD, donde SysV shared memory fue portado desde SVR4 alrededor de 1989. Calif enlaza el código original en su paper vía weiss/original-bsd en GitHub, donde se ve que el contador nunca fue cambiado a un tipo más ancho en ningún momento de la historia del árbol de fuentes de OpenBSD. Treinta y siete años de compat header arrastrando el mismo tipo.

Timeline concreto del disclosure

Lo que hace este caso interesante desde el punto de vista de un CSIRT es el timing del reporte, no la mera existencia del bug:

  • Mayo 19 de 2026: OpenBSD 7.9 se publica oficialmente (la release 60 del proyecto). El kernel nuevo todavía arrastra shm_nattch como short.
  • Septiembre 14 de 2026: sale el errata 018 (RELIABILITY FIX, todas las arquitecturas). Corrige el integer overflow en shmat(4). Catalogado como reliability, no como security, porque por sí solo no entrega root.
  • Septiembre 30 de 2026: sale el errata 026 (SECURITY FIX, todas las arquitecturas). Corrige el reference-counting use-after-free en el kernel que completa la cadena. Es este el que Calif considera explotable.
  • Octubre 16 de 2026: Calif publica el paper “Big Fish: Exploiting a 37-Year-Old Kernel Bug in OpenBSD” y el PoC completo en GitHub. Andrew Griffiths explica en detalle cómo mapear 127 segmentos SysV shmem, forkear 259 procesos para envolver el contador a -32643, ganar la race con IPC_RMID y shmat() en helper processes, y forzar el OOB write.

¿Por qué importa el timing? Porque durante casi un mes, los errata estaban disponibles y la cadena completa era explotable, pero no había disclosure público. Cualquier atacante siguiendo las listas de announce@ o revisando errata79.html regularmente hubiera podido armar el exploit antes que Calif lo publicara formalmente. Esa ventana de 16 días entre el errata 026 y el paper es el equivalente moderno del “patch gap” que tanto se discute en gestión de vulnerabilidades.

Verificar si tu host OpenBSD está afectado

Antes de aplicar nada, confirma versión y estado de parches. Estos tres comandos son tu triage inicial:

# 1. Versión exacta del kernel/userspace
uname -a
sysctl -n kern.version
# 2. ¿Tienes los parches binarios al día?
syspatch -c

syspatch -c lista los errata que faltan sin aplicarlos. Si ves los números 018_shmat.patch y 026_sysvipc.patch en la lista, tu host es vulnerable a la cadena Big Fish. Si solo aparece el 026, también eres vulnerable: el 018 se marcó como reliability porque por sí solo no es explotable, pero es lo que previene que el contador envuelva en primer lugar.

Para hacer un chequeo más agresivo — buscar indicadores de que alguien ya intentó explotar la cadena — corre:

# 3. Verificar si el kernel está usando un shm_nattch de 16 bits
# (en binario parcheado ya es de 32 bits)
nm /bsd | grep -i shm_nattch
# Salida esperada en kernel vulnerable:
# 0000000000000000 ? _shm_nattch
# Salida esperada en kernel parchado:
# offset del símbolo cambia porque el tipo se amplió
# 4. Buscar SysV shmem con attach counts anormales (cercanos a INT16_MAX)
ipcs -am | awk '$5 > 32000 {print}'

Esa segunda verificación es útil en un fleet grande: si algún host reporta procesos con nattch cerca de 32000 sin justificación, o si el kernel panic reciente (dmesg | grep -i panic) tiene trazas con shm.c o ipc_rmid, vale la pena abrir un ticket de incidente.

Aplicar el fix permanente (recomendado)

El fix oficial está disponible vía syspatch(8). Es el camino limpio — OpenBSD firma los binarios con signify(1) y todas las arquitecturas soportadas (amd64, i386, arm64) reciben binarios:

# Aplicar todos los parches pendientes y reiniciar
sudo syspatch
# Al terminar, leer el resumen de errata aplicados:
cat /var/backups/syspatch.log
# Confirmar que ya tienes 018 y 026
syspatch -c

Tras reiniciar, syspatch -c no debe listar nada. Recomendación operativa: en un fleet de firewalls OpenBSD, programa el syspatch con un cron semanal y un guardia de pre-check (syspatch -l | grep -q 026 || mail -s "OpenBSD patch drift" sysadmin@tu-equipo). Lo aprendí años atrás cuando un firewall quedó tres meses sin parchear porque el admin pensaba que syspatch “rompe cosas” — no rompe, solo reinicia.

Si tu release ya está fuera de soporte (7.6 o 7.7 según la página oficial), el camino es sysupgrade(8) a 7.9 y luego syspatch. Los errata no se publican para ramas viejas después de que termina el periodo de un año de soporte -stable.

Si no puedes parchear hoy: mitigaciones inmediatas

A veces syspatch se postpone porque hay un cambio ABI que tu app crítica no tolera, o el host está en un cluster de failover que requiere ventana de mantenimiento. En ese caso:

  1. Limita el acceso local: el ataque requiere un shell local no root en el host. Quita el login directo a root, fuerza wheel con doas o sudo, deshabilita el login SSH por password (solo pubkey + 2FA).
  2. Bloquea shmat(4) en usuarios no privilegiados: usa sysctl kern.usermount=0 y configura login.conf(5) con resource limit duros de procesos por usuario. El exploit necesita forkear 259 procesos, un patrón que un kern.maxproc bajo (ej. 200 por usuario no root) bloquea directamente.
  3. Habilita pf en modo estricto: aunque el ataque es local, una capa adicional de sandboxing con pf en anchor por usuario puede contener procesos que ya ganaron root momentáneamente.
  4. Audita con auditd(8) o process accounting: lastcomm o auditingctl(8) registran ejecuciones de shmat repetidas desde el mismo UID. Si ves un patrón del tipo "300 llamadas a shmat por segundo durante 30 segundos", tienes un intento activo.
# Endurecer limits via login.conf
sudo cp /etc/login.conf /etc/login.conf.bak.$(date +%Y%m%d)
sudo sed -i '/^default:/a 
    :maxproc=200:
    :maxproc-cur=100:' /etc/login.conf
sudo cap_mkdb /etc/login.conf
# Verificar sysctl
sysctl kern.maxproc
sysctl kern.usermount

Si quieres una detección más explícita sin instalar paquetes extras, este snippet sobre auditd(8) (OpenBSD ya trae audit en base, no necesitas ports) registra cualquier intento de envolver shm_nattch:

# Habilitar audit al boot
sudo rcctl enable auditd
sudo audit -n
# Reglas mínimas para detectar el patrón Big Fish
cat <<'EOF' | sudo tee /etc/audit/audit.rules
-a exit,always -S shmat -S shmget -S shmctl -F euid=0
-w /dev/kmem -p rwxa
-a exit,always -S fork -S vfork -S clone -F euid!=0
EOF
# Aplicar y reiniciar auditd
sudo audit -N
sudo rcctl restart auditd
# Revisar eventos sospechosos de las últimas 6h
sudo ausearch -m SYSCALL -ts today -te now -i | grep -i 'shmat|shmctl' | head -50

Lecciones para sysadmins LATAM que corren OpenBSD o Linux

Después de leer el paper tres veces y revisar la línea exacta de sys/sys/shm.h:28-33, estas son las cosas que todos — incluyendo los que corren RHEL o Ubuntu en sus servidores de aplicación — deberían llevarse de este caso:

  • Contadores de tipo estrecho son deuda técnica acumulada. Si tu código C tiene un uint16_t contando referencias, eventos, o tamaños, asume que es un bug latente. OpenBSD demostró que ni 37 años ni tres generaciones de mitigaciones borran ese tipo de decisiones de diseño.
  • KARL/SMAP/SMEP son paredes, no jaulas. Ningún atacante serio va a renunciar si encuentra un bypass elegante. Tu defensa tiene que funcionar a pesar de que esas mitigaciones fallen, no gracias a ellas.
  • Los errata no son opcionales. El 16 de septiembre al 30 de septiembre fue la ventana ciega. Tener syspatch automatizado en CI o cron es la única defensa real contra patch gaps.
  • Reference counting + concurrencia = bomba de tiempo. Si revisas código propio, cualquier helper que toque contadores refcount_inc/refcount_dec desde múltiples threads merece una auditoría adicional.

Una cosa que Calif no menciona y que me parece importante para la audiencia hispanohablante: la mayoría de exploits modernos requieren SysV IPC activo (sysvsem, sysvshm, sysvmsg). En Linux, eso se traduce a CONFIG_SYSVIPC=y en el kernel. Muchos contenedores Docker lo deshabilitan como mitigación y eso es suficiente para cortar este vector, pero SysV sigue siendo un componente estándar en RHEL, Rocky, Alma y Debian. Si tu container corre con --privileged o como init dentro del pod, conserva SysV.

Comprobaciones tras parchear

Una vez aplicado el fix, asegúrate de que el cambio realmente entró:

# 1. Confirmar reboot limpio tras syspatch
last reboot | head -3
dmesg | grep -i "errata 018|errata 026"
# 2. Confirmar nuevo límite operativo
ipcs -lm
# max number of segments debe reflejar el nuevo cálculo interno
# 3. Smoke test SysV IPC funcional
python3 -c 'import sysv_ipc; print(sysv_ipc.shm.__doc__)' 2>/dev/null || echo "lib not installed (ok)"
# 4. Revisar logs de auditd por cualquier intento sospechoso durante el periodo vulnerable
sudo ausearch -m USHARE,EXECVE -ts yesterday -te now | grep -i shmat | head -20

Si tu firewall corre sin auditd (algo común en appliances con /var pequeño), considera al menos habilitar accounting con sudo accton /var/log/account/psacct. Ocupa kilobytes y te da trazabilidad post-mortem.

Resumen final

La cadena Big Fish de Calif es un recordatorio incómodo: el código que parece intocable porque lleva décadas ahí es, justamente, el que menos ojos frescos ha recibido. OpenBSD resolvió el problema en menos de cinco meses desde el reporte hasta el errata, y el disclosure coordinado fue responsable. Pero el vector no era exótico — era un short contador firmado en una cabecera compartida con sistemas operativos de hace cuatro décadas. La próxima vez que audites código, busca los short cerca de _count o _ref. Es un buen sitio para empezar.

Si corres OpenBSD hoy, parchea esta semana. Si corres Linux con SysV IPC activo, vale la pena revisar si has deshabilitado los módulos sysv_shm/sysv_sem en tu fleet. Y si diriges un equipo de seguridad, mete syspatch automatizado en el pipeline antes de que el próximo bug de 37 años decida aparecer en tu plataforma favorita.

Preguntas frecuentes

¿OpenBSD sigue siendo "el Unix seguro" después de este caso?

Sí, sin matices. Calif no encontró una puerta trasera ni una decisión de diseño negligente. Encontraron un contador que llevaba 37 años sin cuestionarse, en una cabecera compartida con sistemas de la era pre-Internet. La cadena se parchó en menos de cinco meses y el disclosure fue responsable. Pocos proyectos comerciales tienen ese historial.

¿Mi firewall OpenBSD en producción está en riesgo aunque no haya notado nada raro?

Si no aplicaste el errata 018 ni el 026 después del 30 de septiembre de 2026, sí. La particularidad de las escaladas locales es que no dejan huella evidente: el atacante solo necesita un shell no privilegiado, lo cual en un firewall perimetral viene de los usuarios remotos que abren túneles SSH. Aplica syspatch cuanto antes y revisa logs de ipcs por conteos anormales.

¿Por qué el errata 018 se catalogó como reliability y no como security?

El proyecto OpenBSD clasifica como security un bug que, por sí solo y sin condiciones adicionales, entrega root o evade una mitigación de seguridad. El integer overflow en shmat(4) necesita combinarse con el use-after-free del errata 026 para formar la cadena explotable. Por eso 018 es reliability y 026 es security. La nota oficial de Calif califica la cadena como "explotable" solo cuando ambos están presentes.

¿Esto afecta también a FreeBSD, NetBSD o DragonFlyBSD?

El bug específico del tipo shm_nattch heredado del árbol original BSD existe en FreeBSD y NetBSD también, porque copiaron la misma cabecera SysV IPC. Calif no publicó un exploit cross-platform, pero si usas FreeBSD en producción, audita tu versión de sysv_shm.c y considera mitigaciones equivalentes (limits agresivos en kern.ipc.shmmax). DragonFly BSD usa un modelo de IPC distinto basado en ports, no en SysV, así que está menos expuesto.

¿Hay algún patrón de log que pueda alertar de intentos de este exploit?

Sí, dos: explosiones de shmat(2) desde el mismo UID en una ventana corta (más de 100 llamadas en 5 segundos es sospechoso) y fork storms con fork(2) en ráfaga, también desde usuarios no root. Con auditd en audit.rules o con bpftrace sobre el tracepoint sys_enter_shmat puedes captar el patrón antes de que el contador se envuelva.

Lee también

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]