Chains of Love

Advanced Track


Scenario

My Dearest Hacker,

NovaDev Solutions is a software development house known for building secure enterprise platforms for clients across multiple countries and industries. Recently, NovaDev Solutions rolled out a new customer interaction feature on their website to improve communication between clients and developers.

Shortly after deployment, NovaDev began experiencing unusual traffic patterns and minor service disruptions. Internal developers suspect that something in the latest udpate may have exposed more than intended.

Summary

chevron-rightSummaryhashtag

In Chains of Love we begin by enumerating a web server hosting nova.thm, where initial inspection reveals a seemingly static site with a contact form. Further virtual host fuzzing uncovers internal.nova.thm, and directory enumeration exposes a publicly accessible .git repository. Dumping the repository reveals a recently introduced preview_feature.py, confirming a Server-Side Template Injection (SSTI) vulnerability within the contact form. Exploiting this SSTI allows us to leak sensitive configuration data, including internal service endpoints and application secrets.

A second round of enumeration exposes app.py, revealing hardcoded admin credentials and confirming that session handling relies on JWT. Using the discovered credentials, we access the /admin panel, which provides a fetch functionality vulnerable to SSRF. Leveraging this, we pivot to internal.nova.thm, where we encounter a restricted Python sandbox. Although numeric input is filtered, we bypass limitations using Python expressions to read local files directly via list(open()).

Recon

We use rustscan -b 500 -a 10.81.131.2026 -- -sC -sV -Pn to enumerate all TCP ports on the target machine, piping the discovered results into Nmap which runs default NSE scripts -sC, service and version detection -sV, and treats the host as online without ICMP echo -Pn.

A batch size of 500 trades speed for stability, the default 1500 balances both, while much larger sizes increase throughput but risk missed responses and instability.

We identify port 80 to be open and serving a website.

From our Nmap scan result we can see that we the page redirects to http://nova.thm.

The site seems to be just a static one at first glance. We have a contact form, that after submission reflects the content provided.

We test for SSTI, but without success for now.

We enumerate further VHosts and identify internal.nova.thm, which resolves to a 403 Status error code. We are not authorized.

Next, we enumerate directories using the dirb common.txt and are able to identify a .git folder. We might be able to dump it.

Git Dump

We use gitdumper.sh to dump the repository.

We inspect the log and are able to identify one commit that added the preview_feature.py. Revealing a SSTI on the contact form only allowing {{confg}}.

SSTI

We test for the SSTI...

... and are able to identify some environment variables, including a secret, to craft a session token. This would allow us to authenticate to the admin panel, but unfortunately we do not know the structure of the session token and if it is either a JWT or Flask session token.

Recon II - Access as nova_admin_

Since run another directory scan, with common extensions like .py and are able to spot the app.py, revealing the entire source of the web app.

Here we are abe to spot the Admin credentials and the structure of the cookie. Its a JWT session cookie.

We head to the /admin/login...

... and use the credentials found in the source.

SSRF

We are able to log in and have the admin panel infront of us.

We can run a fetch command. We give it a try with the internal service we identified with our VHOSTs scan.

We see we get redirected and have a Python Sandbox infornt of us with example code.

If we click run, we see we get redirected back to our initial page with an additional parameter code=1%2B1 appended.

We try to fetch the internal service with the parameter...

But get the response that digits are not allowed. Which wouldn't allow us to craft a reverse shell since we cannot define an IP or port. But there might be more hurdles then we see know, since it is a sandbox.

Python Command Execution

With list(open()), we try to read several files.

Ending up with the following to read the flag:

Last updated

Was this helpful?