Pug (antes conocido como Jade) es un motor de plantillas de alto rendimiento ampliamente usado en la comunidad Node.js por su renderizado de HTML conciso y funcionalidades avanzadas como condicionales, iteraciones y herencia de plantilla. Aunque ofrece herramientas poderosas para desarrolladores, su habilidad para ejecutar código JavaScript directamente en las plantillas puede suponer un riesgo importante.
Las vulnerabilidades de seguridad de Pug principalmente salen de su capacidad de interpolar código JavaScript en variables de plantilla. Esta funcionalidad, diseñada para generar contenido dinámicamente puede ser explotada de forma maliciosa si el input del usuariose incrusta en la plantilla sin sanitizar correctamente.
Puntos Clave de Vulnerabilidades
- Interpolación JavaScript: Pug permite incrustar JS directamente en las plantillas usando
#{}. Si el input del usuario se interpola sin sanitización adecuada puede llevar a ejecución de código arbitrario. - Escapado por Defecto: Pug ofrece un escapado automático para ciertos inputs, convirtiendo los caracteres como
<,>y&a sus entidades HTML equivalentes para evitar ataques XSS. Sin embargo este comportamiento por defecto no cubre todos los problemas de seguridad, en particular al lidiar con interpolación no escapada!{}o escenarios de input complejos.
Explotación
Antes de construir el payload es importante confirmar que la aplicación usa Pug. Ve a http://ssti.thm:8001/jade/.
Inyecta una sintaxis básica de Pug para comprobar el procesado de la plantilla, como #{7*7}. Si la aplicación responde con 49, confirma que Pug está procesando la plantilla.

Ya que Pug permite la interpolación de JavaScrip, podemos usar el payload #{root.process.mainModule.require('child_process').spawnSync(ls).stdout}.
Este, lo que hará será usar los módulos de Node.js para ejecutar comandos de sistema. Debajo está el análisis del payload.
root.process: Accede al objeto globalprocessde Node.js en la plantilla Pug.mainModule.require('child_process'): Requiere el módulochild_processde manera dinámica, bypasseando restricciones potenciales.spawnSync('ls'): Ejecuta el comandolsde forma síncrona..stdout: Captura el output del comando.

Por qué spawnSync('ls -lah') Puede No Funcionar
Cuando intentamos usar spawnSync('ls -lah'), estamos intentando pasar el comando entero y sus argumentos como una sola string. Esto no funciona porque spawnSync no separa la string en un comando y sus argumentos. En su lugar, lo trata todo como un comando a ejecutar.
Este comportamiento es crítico para pervenir ciertos tipos de vulnerabilidades de seguridad. como la inyección de comandos.
Entendiendo el Uso de spawnSync
La función spawnSync está diseñada para ejecutar un comando en la shell y dar control detallado sobre el input y output del comando. Es parte del módulo child_process de Node.js, lo que permite a Node.js ejecutar otros procesos en el sistema donde corre.
La firma de la función para spawnSync es:
spawnSync(command, [args], [options])
- command: Esto es una string que especifica el comando a ejecutar.
- args: Es un array con los argumentos que pasarle al comando.
- options: Es un parámetro opcionar para especificar opciones como el directorio de trabajo, variables de entorno, timeout, etc.
Uso Correcto de spawnSync
Para usar spawnSync correctamente para ejecutar el comando ls con el argumento .lah, deberías separar el comando y sus argumentos en partes diferentes.
const { spawnSync } = require('child_process'); const result = spawnSync('ls', ['-lah']); console.log(result.stdout.toString());
De esta forma corregida:
- 'ls': Es el comando.
- ['-lah']: Es un array que contiene todos los argumentos pasados al comando.
Esta estructura asegura que el comando ls se llame con el argumento -lah, permitiendo al comando funcionar como se espera. Por lo que el payload final es:
#{root.process.mainModule.require('child_process').spawnSync('ls', ['-lah']).stdout}

