En mayo de 2025 se hizo pública una vulnerabilidad de seguridad crítica, CVE-2025-1974, conocida como «IngressNightmare», que afecta al controlador ingress-nginx de Kubernetes, ampliamente implantado en infraestructuras nativas de la nube. Esta vulnerabilidad permite a atacantes no autenticados inyectar configuraciones arbitrarias en NGINX, lo que podría dar lugar a la ejecución remota de código (RCE) no autorizada y al compromiso total del clúster.
En el marco del programa OPSWAT , nuestros becarios llevaron a cabo un análisis técnico exhaustivo para comprender mejor la causa raíz, la vía de explotación y las estrategias de mitigación relacionadas con este problema de alta gravedad.

Resumen de CVE-2025-1974
CVE-2025-1974 es una vulnerabilidad crítica de inyección de plantillas detectada en las versiones de ingress-nginx hasta la 1.11.4 y, en concreto, en la 1.12.0. Los atacantes con acceso a nivel de nodo a un clúster de Kubernetes pueden aprovechar esta vulnerabilidad para ejecutar código arbitrario mediante RCE a través del controlador ingress-nginx, el cual, por defecto, cuenta con amplios privilegios, incluido el acceso a secretos críticos dentro del clúster.
El Comité de Respuesta de Seguridad de Kubernetes ha asignado a esta vulnerabilidad una puntuación CVSS v3.1 de 9,8 (gravedad crítica):

Elementos clave de este análisis
Descripción general de Kubernetes
Kubernetes (K8s) es una plataforma de código abierto diseñada para automatizar la implementación, el escalado y la gestión operativa de aplicaciones en contenedores. Los clústeres de Kubernetes suelen estar formados por varias máquinas, que pueden ser tanto hardware físico como máquinas virtuales, y que funcionan de forma conjunta para proporcionar entornos de aplicaciones de alta disponibilidad, escalables y fáciles de gestionar.
Controlador de Ingress de NGINX
El controlador de Ingress de NGINX (ingress-nginx) es un controlador de Ingress de código abierto basado en el servidor web NGINX. Opera dentro de un clúster de Kubernetes, donde actúa principalmente como proxy inverso y equilibrador de carga. Este controlador interpreta los recursos de Ingress definidos por los usuarios y los traduce en configuraciones de NGINX ejecutables para dirigir el flujo de tráfico hacia el clúster y dentro de él.
El proceso de admisión y su función
Ingress-nginx se integra con Kubernetes mediante un servicio de webhooks denominado AdmissionReview. Este servicio es crucial para procesar objetos Ingress nativos de Kubernetes y traducirlos a configuraciones de NGINX validadas y sintácticamente correctas. Aunque AdmissionReview garantiza la precisión de la configuración, opera de forma independiente del controlador ingress-nginx y, por lo general, carece de controles de autenticación rigurosos. Esta falta de autenticación estricta es un factor clave que contribuyó a la vulnerabilidad de CVE-2025-1974.

Aprovechamiento de vulnerabilidades y análisis técnico
Mecanismo de explotación
En esencia, la explotación de CVE-2025-1974 comienza con una solicitud maliciosa. Los atacantes crean una solicitud maliciosa dirigida al webhook AdmissionReview, lo que obliga a NGINX a cargar dinámicamente una biblioteca compartida en tiempo de ejecución. Partiendo de este mecanismo, nuestros compañeros analizaron tanto el webhook AdmissionReview como el flujo de trabajo de NGINX para comprender esta vía de explotación.

Vulnerabilidad de inyección de plantillas
En el webhook de AdmissionReview, al procesar las solicitudes entrantes, la función CheckIngress transforma los objetos Ingress de Kubernetes en archivos de configuración válidos de NGINX. El proceso se desarrolla de la siguiente manera:

- Cada configuración se analiza y se pasa a `generateTemplate ` para que se formatee según las plantillas predefinidas de NGINX.
- A continuación, testTemplate comprueba que la configuración generada sea compatible con el binario de NGINX subyacente.
Todas las configuraciones de NGINX se basan en plantillas predefinidas que se encuentran en el archivo nginx.tmpl, dentro del código fuente de ingress-nginx:

Dentro de la función `generateTemplate `, la configuración se procesa tal y como se muestra en el siguiente fragmento de código:

Sin embargo, la validación y la sanitización de las entradas son insuficientes. Concretamente, el campo «uid» de un objeto Ingress se inserta directamente en la plantilla de configuración de NGINX, lo que crea un punto de inyección. Un atacante puede aprovechar esto enviando entradas manipuladas, como «uid="1234#;\n\n}\n}\n}\n injection_value"».
Esta entrada maliciosa permite la inyección de variables de ámbito global en la plantilla de NGINX, lo que permite a los atacantes activar directivas arbitrarias de NGINX y, potencialmente, lograr la ejecución remota de código (RCE).

De la inyección de plantillas a la ejecución remota de código
Explicación de la función testTemplate()
Una vez que la función `generateTemplate` ha generado la configuración de NGINX, la función `testTemplate` crea un archivo de configuración temporal y ejecuta el binario de NGINX con el comando `nginx -c {config_file} -t`. Esto obliga al binario de NGINX a analizar y validar la configuración.

Para aprovechar la vulnerabilidad, un atacante debe identificar una directiva capaz de ejecutar código malicioso. En un primer momento, nuestros compañeros identificaron la directiva `load_module` como potencialmente útil, ya que esta directiva permite a NGINX cargar complementos externos. Sin embargo, esta directiva solo está permitida en la fase inicial del análisis de la configuración, lo que no se ajusta a nuestro punto de inyección.


Para resolver este problema, continuamos investigando, lo que nos llevó a la directiva ssl_engine, descrita como «OpenSSL puede cargar el módulo de forma dinámica durante las pruebas de configuración». Esto despertó nuestra curiosidad debido a su capacidad para cargar módulos de forma dinámica, lo que requería un análisis más detallado.

Explicación de la directiva ssl_engine
Para comprender mejor cómo gestiona NGINX la directiva `ssl_engine`, así como para determinar en qué condiciones NGINX permite la carga dinámica de módulos adicionales a través de esta directiva, hemos analizado el código fuente de NGINX.

Al iniciarse, NGINX carga su estado inicial y, a continuación, analiza los archivos de configuración línea por línea. Cada directiva se gestiona mediante la estructura `nginx_command_t`, y la directiva `ssl_engine` invoca directamente a `ngx_openssl_commands`.


Al analizar la función ngx_openssl_commands, nuestros compañeros descubrieron que depende de la compatibilidad con OpenSSL, concretamente de la función ENGINE_by_id, que se utiliza para los módulos SSL con aceleración por hardware.

Al analizar la función ENGINE_by_id, determinamos que permite la carga dinámica de bibliotecas compartidas. Además, si la biblioteca se compila con la extensión __attribute__((constructor)), la función asociada puede ejecutarse inmediatamente tras la carga. Esto indica que, al aprovechar la directiva ssl_engine, un atacante podría cargar bibliotecas compartidas arbitrarias en el host, lo que podría dar lugar a una ejecución remota de código (RCE).
Librerías compartidas como objetivo y estrategia de ataque
Para facilitar la ejecución de código de forma fiable, el siguiente paso consiste en identificar una biblioteca compartida. En lugar de recurrir a bibliotecas externas, el propio comportamiento de NGINX ofrece un enfoque más viable y controlado: el mecanismo de búfer del cuerpo de la solicitud del cliente. Esta característica permite a NGINX descargar las solicitudes entrantes de gran tamaño en archivos temporales, lo que abre la puerta a posibles vulnerabilidades basadas en un comportamiento predecible en el manejo de archivos.

De forma predeterminada, cuando una solicitud entrante supera los 8 KB, NGINX escribe el cuerpo de la solicitud en un archivo temporal ubicado en /tmp/nginx/client-body, utilizando un nombre de archivo con el formato cfg-{valor_aleatorio}. Estos archivos temporales se conservan durante un máximo de 60 segundos entre los fragmentos recibidos correctamente de un mensaje.

Tras escribir una parte del cuerpo de la solicitud en un archivo temporal, NGINX pospone su eliminación hasta que se haya recibido el cuerpo completo. Si la solicitud sigue sin completarse y no se reciben datos durante un máximo de 60 segundos, el archivo acaba siendo eliminado. Sin embargo, al retener intencionadamente el fragmento final de datos, un atacante puede mantener el archivo temporal en uso, lo que lo hace vulnerable a un ataque.

Aunque es posible controlar el contenido del archivo subido, resulta difícil localizarlo en el sistema de archivos debido a que el nombre del archivo es aleatorio. La ruta de almacenamiento se puede configurar mediante client_body_temp_path, pero el nombre del archivo se genera aleatoriamente en tiempo de ejecución, lo que lo hace impredecible. Esta aleatoriedad dificulta considerablemente el acceso selectivo, incluso mediante ataques de fuerza bruta. Para superar esto, el equipo aprovechó comportamientos inherentes al sistema operativo Linux. Consideremos el siguiente ejemplo:

This code opens a file and keeps it in an active state, closely mimicking the behavior of NGINX's client body buffer mechanism. Using /proc/{pid}/fd directory, attackers can find symbolic links created by the Linux kernel that map open file descriptors to their corresponding file paths. This route allows attackers to reduce the brute-force space to only two variables: the process ID (pid) and the file descriptor (fd).

Simulación de la explotación
A partir del análisis anterior, un enfoque práctico para la explotación de RCE dentro del pod de Ingress-NGINX es:
- Sube una biblioteca compartida maliciosa utilizando el mecanismo de búfer del cuerpo de la solicitud del cliente de NGINX para almacenarla temporalmente en el sistema de archivos.
- Utiliza la inyección de plantillas para lanzar un ataque de fuerza bruta que obligue a NGINX a cargar la biblioteca compartida subida previamente a través de directivas vulnerables.

Creación de la carga útil que contiene la biblioteca compartida
Para garantizar la ejecución del código al cargarse, se define una función de punto de entrada con extensión de constructor dentro de la biblioteca compartida maliciosa. Esta función se ejecuta cuando NGINX la carga y está diseñada para establecer una conexión de shell inverso con un host remoto.

Tras la compilación, el tamaño de la biblioteca compartida resultante superó holgadamente los 8 KB, lo que permitió que NGINX la almacenara en el búfer sin necesidad de rellenarla con datos adicionales.

A continuación, nuestros compañeros elaboraron una solicitud con un valor de «Content-Length» inflado (por ejemplo, 1 MB) para provocar una discrepancia de tamaño. Esto hizo que NGINX almacenara en el búfer todo el cuerpo del mensaje en lugar de procesarlo inmediatamente, lo que garantizaba que el objeto compartido se escribiera en una ubicación predecible.

Activación de la biblioteca compartida mediante inyección
Una vez instalada la biblioteca compartida, a continuación inyectamos una directiva maliciosa en la configuración de NGINX aprovechando el campo «uid» vulnerable. Esta directiva incluía «ssl_engine» apuntando a la ruta del archivo almacenado en el búfer:

Para que el RCE tenga éxito, es necesario que la directiva `ssl_engine` haga referencia a la ruta correcta del archivo almacenado en el búfer. Esto se puede lograr mediante un script automatizado de fuerza bruta que recorra sistemáticamente las posibles combinaciones de ID de proceso y descriptores de archivo para identificar el enlace simbólico válido que apunta al objeto compartido almacenado en el búfer.

A continuación se muestra un ejemplo de la plantilla de NGINX generada que activó el exploit.

Si la explotación se llevaba a cabo con éxito, el atacante podía obtener acceso a la shell en el contexto del pod «ingress-nginx», que, por defecto, tenía acceso a secretos confidenciales del clúster de Kubernetes.

Mitigación y remediación
Para mitigar de forma eficaz los riesgos asociados al CVE-2025-1974, las organizaciones necesitan una solución que les proporcione visibilidad y control sobre sus componentes de código abierto.
OPSWAT , una tecnología fundamental de la plataforma MetaDefender®, responde a esta necesidad al ofrecer un inventario de todos los componentes de software, bibliotecas, contenedores Docker y dependencias que se utilizan. Permite a las organizaciones realizar un seguimiento, proteger y actualizar sus componentes de forma proactiva.

En el ejemplo anterior, la tecnología SBOM de MetaDefender analizó el paquete nginx-ingress-controller, que contenía la vulnerabilidad CVE-2025-1974. El sistema clasificó automáticamente el problema como «crítico» y proporcionó información sobre las versiones corregidas disponibles, lo que permitió a los equipos priorizar y corregir rápidamente la vulnerabilidad antes de que pudiera ser explotada.
OPSWAT está disponible en MetaDefender Core MetaDefender Software Chain™, lo que permite a los equipos de seguridad identificar las vulnerabilidades y actuar con mayor rapidez. Con OPSWAT , los equipos de seguridad pueden:
- Localice rápidamente los componentes vulnerables: identifique de inmediato los componentes de código abierto afectados por ataques de deserialización. Esto garantiza una actuación rápida, ya sea mediante la aplicación de parches o la sustitución de las bibliotecas vulnerables.
- Garantice la aplicación proactiva de parches y actualizaciones: supervise continuamente los componentes de código abierto mediante OPSWAT para adelantarse a las vulnerabilidades de deserialización. OPSWAT puede detectar componentes obsoletos o inseguros, lo que permite realizar actualizaciones oportunas y reducir la exposición a los ataques.
- Garantizar el cumplimiento normativo y la presentación de informes: OPSWAT ayuda a las organizaciones a cumplir los requisitos normativos, en un contexto en el que los marcos reguladores exigen cada vez más transparencia en las cadenas de suministro de software.
