LinkVortex
Executive summary — LinkVortex starts from an exposed .git repository on a development subdomain, which leaks a hardcoded admin password sitting inside a test file. That password unlocks a Ghost CMS admin session, which is used to exploit CVE-2023-40028 — a symlink smuggled inside an imported ZIP that the decompression routine never filters — to read arbitrary files off the box and recover plaintext SMTP credentials. Those credentials, reused as the SSH password for a system account, hand over user.txt; for root, a
sudoscript that "quarantines" symlinks falls to a double symlink because it only validates the first hop of the chain.
| Platform | Hack The Box |
| Operating system | Linux (Ubuntu 22.04) |
| Category | Web / Ghost CMS |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.231.194 |
Attack map
[80] linkvortex.htb — Ghost CMS 5.58 blog (the redirect reveals the vhost)
│ vhost dev.linkvortex.htb — "Launching Soon", with .git/ exposed and served
▼
[.git dump] credential leak — test password + config.production.json path
│ admin@linkvortex.htb / OctopiFociPilfer45 -> valid Ghost admin session
▼
[Ghost Admin] CVE-2023-40028 — ZIP import with a symlink, extract-zip doesn't filter it
│ arbitrary file read with the privileges of the Ghost process (node)
▼
[LFI] config.production.json -> SMTP credentials: bob@linkvortex.htb / fibber-talented-worth
│ same password reused as the SSH password for the system account bob
▼
[SSH] bob (user.txt)
│ sudo NOPASSWD /opt/ghost/clean_symlink.sh *.png — single-hop readlink filter
▼
[ROOT] double symlink (evil.png -> stage -> /root/root.txt) evades the filter,
the final cat resolves the whole chain
1. Reconnaissance
nmap -sV -sC --top-ports 50 -Pn -oN recon/nmap-top.txt 10.129.231.194
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd
|_http-title: Did not follow redirect to http://linkvortex.htb/
OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 places the target on Ubuntu 22.04 LTS. The unfollowed redirect hands over the main vhost, linkvortex.htb, for free — no fuzzing needed.
echo "10.129.231.194 linkvortex.htb" | sudo tee -a /etc/hosts
A full nmap -p- confirms only two real open ports: the whole attack surface lives on port 80.
curl -s http://linkvortex.htb/ | grep -iE 'generator|X-Powered'
<meta name="generator" content="Ghost 5.58">
X-Powered-By: Express
A blog built on Ghost CMS 5.58 ("BitByBit Hardware") running on Node/Express behind Apache, with the admin panel reachable at /ghost (200). The version is the clue that matters: Ghost ≤ 5.59 carries CVE-2023-40028.
The server returns a catch-all response (200, same size) for any unrecognized Host, so finding real vhosts means comparing status code and size against that baseline, not trusting a bare 200:
for sub in dev devops git staging test admin blog api internal; do
r=$(curl -s -o /dev/null -w "%{http_code}:%{size_download}" -H "Host:$sub.linkvortex.htb" http://linkvortex.htb/)
echo "$sub.linkvortex.htb ->$r"
done
dev.linkvortex.htb -> 200:2538 <- different: real vhost
devops.linkvortex.htb -> 301:230
git.linkvortex.htb -> 301:230 (everything else redirects the same way)
Only
dev.linkvortex.htbanswers differently from the rest — a real vhost, not a catch-all false positive. Its landing page is a static "Launching Soon" screen with no apparent content. When a server answers identically to anyHost, running an uncalibratedffufgenerates hundreds of false positives; always diff against the baseline response before trusting the list.
Resumen ejecutivo — LinkVortex parte de un repositorio .git expuesto en un subdominio de desarrollo, de donde se filtra una contraseña de administrador hardcodeada en un fichero de test. Con esa sesión de administrador en Ghost CMS se explota CVE-2023-40028 — un symlink dentro de un ZIP importado que la función de descompresión no filtra — para leer ficheros arbitrarios del sistema y recuperar credenciales SMTP en claro. Esas credenciales, reutilizadas como contraseña SSH de la cuenta del sistema, dan el user.txt; para root, un script de
sudoque "pone en cuarentena" symlinks cae ante un doble enlace simbólico porque solo valida el primer salto de la cadena.
| Plataforma | Hack The Box |
| Sistema operativo | Linux (Ubuntu 22.04) |
| Categoría | Web / Ghost CMS |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.231.194 |
Mapa del ataque
[80] linkvortex.htb — blog Ghost CMS 5.58 (la redirección revela el vhost)
│ vhost dev.linkvortex.htb — "Launching Soon", con .git/ expuesto y servido
▼
[.git dump] fuga de credencial — password de test + ruta de config.production.json
│ admin@linkvortex.htb / OctopiFociPilfer45 -> sesión de administrador Ghost
▼
[Ghost Admin] CVE-2023-40028 — import de ZIP con symlink, extract-zip no lo filtra
│ lectura arbitraria de ficheros con los privilegios del proceso Ghost (node)
▼
[LFI] config.production.json -> credenciales SMTP: bob@linkvortex.htb / fibber-talented-worth
│ misma contraseña reutilizada como password SSH de la cuenta de sistema bob
▼
[SSH] bob (user.txt)
│ sudo NOPASSWD /opt/ghost/clean_symlink.sh *.png — filtro readlink de un solo salto
▼
[ROOT] doble symlink (evil.png -> stage -> /root/root.txt) evade el filtro,
el cat final resuelve la cadena completa
1. Reconocimiento
nmap -sV -sC --top-ports 50 -Pn -oN recon/nmap-top.txt 10.129.231.194
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd
|_http-title: Did not follow redirect to http://linkvortex.htb/
OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 sitúa el objetivo en Ubuntu 22.04 LTS. La propia redirección no seguida regala el vhost principal, linkvortex.htb, sin necesidad de fuzzing.
echo "10.129.231.194 linkvortex.htb" | sudo tee -a /etc/hosts
Un nmap -p- completo confirma que solo hay dos puertos reales abiertos: todo el vector de ataque está en el 80.
curl -s http://linkvortex.htb/ | grep -iE 'generator|X-Powered'
<meta name="generator" content="Ghost 5.58">
X-Powered-By: Express
Blog montado sobre Ghost CMS 5.58 ("BitByBit Hardware") corriendo sobre Node/Express detrás de Apache, con el panel de administración accesible en /ghost (200). La versión es la pista que importa: Ghost ≤ 5.59 arrastra CVE-2023-40028.
El servidor devuelve una respuesta comodín (200, mismo tamaño) para cualquier Host que no reconoce, así que localizar vhosts reales exige comparar código y tamaño contra ese comodín, no fiarse de un 200 a secas:
for sub in dev devops git staging test admin blog api internal; do
r=$(curl -s -o /dev/null -w "%{http_code}:%{size_download}" -H "Host:$sub.linkvortex.htb" http://linkvortex.htb/)
echo "$sub.linkvortex.htb ->$r"
done
dev.linkvortex.htb -> 200:2538 <- distinto: vhost real
devops.linkvortex.htb -> 301:230
git.linkvortex.htb -> 301:230 (todos los demás redirigen igual)
Solo
dev.linkvortex.htbresponde de forma distinta al resto — es un vhost real, no un falso positivo del comodín. Su portada es una "Launching Soon" estática, sin contenido aparente. Cuando el servidor responde igual a cualquierHost, lanzar unffufsin calibrar el filtro de tamaño produce cientos de falsos positivos; comparar siempre contra la respuesta base antes de confiar en la lista.
This content is Root Access only. Everything else on the site — the free tier, the whole public library — stays open.
See Root Access plansNo account? Create one free then upgrade from your console.
Este contenido es solo para Root Access. Todo lo demás del sitio — el nivel gratuito, toda la biblioteca pública — sigue abierto.
Ver planes de Root Access¿Sin cuenta? Crea una gratis y luego mejora desde tu consola.