Celestial
Executive summary — Celestial exposes a single Node.js/Express application on port 3000 that stores user state in a base64-encoded
profilecookie. That cookie is a JSON object the app deserializes server-side with node-serialize, vulnerable to CVE-2017-5941: embedding a function behind a special marker is enough to get it executed as arbitrary code during deserialization itself. That hands over a shell as thesunuser (user.txt); from there, a root cron running a Python script owned by —and therefore writable by—sunallows swapping it for a payload that, once the cron fires, drops a SUID shell and delivers root.txt.
| Platform | Hack The Box |
| Operating system | Linux (Ubuntu) |
| Difficulty | Medium |
| Status | Retired |
| Target IP | 10.129.228.94 |
| Exposed service | 3000/tcp — Node.js Express |
Attack map
[3000/tcp] Node.js Express -> `profile` cookie (base64 JSON, reflected back)
│ CVE-2017-5941 (node-serialize): _$$ND_FUNC$$_...() payload in "username"
▼
IIFE executed DURING unserialize() -> reverse shell
│
▼
[USER] sun (uid=1000) <- user.txt
│ root cron runs /home/sun/Documents/script.py (owned by sun, writable)
▼
script.py replaced with a Python payload -> SUID /tmp/rootbash
▼
[ROOT] /tmp/rootbash -p <- root.txt
1. Reconnaissance
sudo nmap -p- --min-rate 2000 -T4 -Pn 10.129.228.94 -oN nmap/allports.txt
sudo nmap -p 3000 -sCV -Pn 10.129.228.94 -oN nmap/svc.txt
3000/tcp open http Node.js Express framework
A single open port across the whole range: the attack surface is, necessarily, that Express app. The HTTP response carries a cookie worth a second look:
Set-Cookie: profile=eyJ1c2VybmFtZSI6IkR1bW15Ii... (HttpOnly)
X-Powered-By: Express
echo 'eyJ1c2VybmFtZSI6IkR1bW15Ii...' | base64 -d
# {"username":"Dummy","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}
Sending that same cookie back, the app reflects the fields in the response:
curl -s http://10.129.228.94:3000/ -b 'profile=<base64>'
# Hey Dummy 2 + 2 is 22
A base64 cookie that decodes to JSON and that the server processes server-side — not just reads, but reconstructs an object from — is the classic signature of insecure deserialization in Node.js. The library most associated with this pattern is
node-serialize, which doesn't just reconstruct data: it also knows how to reconstruct serialized functions, which is the root of the issue.
Resumen ejecutivo — Celestial expone una única aplicación Node.js/Express en el puerto 3000 que guarda el estado del usuario en una cookie
profilecodificada en base64. Esa cookie es un objeto JSON que la app deserializa del lado del servidor con node-serialize, vulnerable a CVE-2017-5941: basta con incrustar una función con un marcador especial para que se ejecute como código arbitrario durante la propia deserialización. Eso entrega una shell como el usuariosun(user.txt); desde ahí, un cron de root que ejecuta un script Python propiedad desun—y por tanto escribible— permite sustituirlo por un payload que, al dispararse el cron, deja una shell SUID y entrega root.txt.
| Plataforma | Hack The Box |
| Sistema operativo | Linux (Ubuntu) |
| Dificultad | Medium |
| Estado | Retired |
| IP objetivo | 10.129.228.94 |
| Servicio expuesto | 3000/tcp — Node.js Express |
Mapa del ataque
[3000/tcp] Node.js Express -> cookie `profile` (JSON en base64, reflejada)
│ CVE-2017-5941 (node-serialize): payload _$$ND_FUNC$$_...() en "username"
▼
IIFE ejecutado DURANTE unserialize() -> reverse shell
│
▼
[USER] sun (uid=1000) <- user.txt
│ cron de root ejecuta /home/sun/Documents/script.py (propiedad de sun, escribible)
▼
script.py sustituido por payload Python -> /tmp/rootbash con bit SUID
▼
[ROOT] /tmp/rootbash -p <- root.txt
1. Enumeración
sudo nmap -p- --min-rate 2000 -T4 -Pn 10.129.228.94 -oN nmap/allports.txt
sudo nmap -p 3000 -sCV -Pn 10.129.228.94 -oN nmap/svc.txt
3000/tcp open http Node.js Express framework
Un único puerto abierto en todo el rango: la superficie de ataque es, por fuerza, esa aplicación Express. La respuesta HTTP incluye una cookie que llama la atención:
Set-Cookie: profile=eyJ1c2VybmFtZSI6IkR1bW15Ii... (HttpOnly)
X-Powered-By: Express
echo 'eyJ1c2VybmFtZSI6IkR1bW15Ii...' | base64 -d
# {"username":"Dummy","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}
Enviando esa misma cookie de vuelta, la app refleja los campos en la respuesta:
curl -s http://10.129.228.94:3000/ -b 'profile=<base64>'
# Hey Dummy 2 + 2 is 22
Una cookie en base64 que contiene JSON y que el servidor procesa del lado servidor —no solo lee, sino que reconstruye un objeto a partir de ella— es el patrón clásico de deserialización insegura en Node.js. La librería más asociada a este patrón es
node-serialize, que no solo reconstruye datos: también sabe reconstruir funciones serializadas, lo cual es la raíz del problema.
Root Access unlocks the content you can't see — everything else on the site is already open.
See Root Access plansNo account? Create one free then upgrade from your console.
Root Access desbloquea el contenido que no puedes ver — todo lo demás de la web ya está abierto.
Ver planes de Root Access¿Sin cuenta? Crea una gratis y luego mejora desde tu consola.