Verbose
Challenge Lab (Easy) - by Tyler Ramsbey
The following post by 0xb0b is licensed under CC BY 4.0
Scenario
Objective / Scope
You have been authorized to perform an external penetration test against a target organization. During the initial reconnaissance phase, you identified a web application that allows unrestricted public user registration.
Enumerate: Map the application's attack surface and functionality.
Identify: Locate exploitable vulnerabilities within the application logic or configuration.
Exploit & Escalate: Leverage identified flaws to compromise the system, with the final goal of securing root access to the host server to demonstrate maximum impact.
Summary
Summary
In Verbose we conduct an external web application hosted on port 80. In the initial enumeration phse we identified a verbose /api/users/all endpoint exposing plaintext credentials for all registered users, including the admin account. Using these credentials, we try to authenticate to the admin dashboard but are prompted for a 2FA code. Lacking rate limiting or code invalidation, we brute-force the 4-digit code via FFuF to gain access. Within the dashboard, an image upload feature allows metadata injection, leading to a server-side template injection (SSTI) vulnerability via EXIF data in uploaded PNG files. Exploiting this flaw, we achieve remote code execution, confirm root privileges, and establish a reverse shell, obtaining the final flag.
Recon
We use rustscan -b 500 -a 10.0.24.218 -- -sC -sV -Pn to enumerate all TCP ports on the DC01 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 have SSH on port 22 available and a web service running Werkzeug/3.1.5 Python/3.12.3 on port 80.

We try to enumerate the directories and pages using Feroxbuster and find the /messages endpoint and login related endpoints to be reachable.

We visit the index page and get redirected to a login.

Using Wappalyzer we can confirm that the server is running flask.

The initial enumeration revealed that login/register and reset endpoints are vulnerable to username enumeration. With our first login attempt as admin, we were able to directly confirm the username admin via the message 'Invalid password'.

Web Access as admin
During the enumeration phase, we also created a user and visited the message board. At the same time, we redirected our traffic to Burp Suite but did not intercept it. Upon further inspection, we found the /api/users/all endpoint in our HTTP history, which is 'somewhat too verbose' and leaks the credentials of all users on the platform.

We try to log in as admin with the found credentials...

... and are requested a 2FA 4 digit verification. We note that there is no rate limiting and even after a failed attempt, the 2FA codes do not become invalid because in our brute force attempts we find the 2FA code.

We note down the session...

... and inspect the POST request to craft our FFuF command to brute force the 2FA submission.

With tht tool seq we craft a wordlist of all possible 2FA codes.

Next, we try every code, but only use 3 threads. In an initial attempt the appliaction throwed to many server errors without the limitation. This may take a while. The 2FA code might be different in other instances. We get a hit with a redirect code 302.

We submit the 2FA code...

... and are able to log in as admin.

At the admin dashboard we find the first flag.

Shell as root
On the admin dashboard we are able to submit a logo.

If we click on Preview Current Logo after uploading one, we see the logo. It also fetches the meta data of the image.

Let's try to inject some metadata to our logo and see if it gets reflected. For this we are using exiftool.

After re-uploading the image we see the meta data reflected.

We try a simple SSTI payload, ...

... re-upload the image, and see it gets evaluated.

Since we know the application is running on Flask from our Wappalyzer enumeration we try a SSTI payload from one of my favourite blogs of Ingo Kleiber.
We prepare the payload to print the id of the current user running the application.
We update the metadata.

Upload the image and preview it, and we see it's running as root.

Next, we prepare a simple reverse shell payload...

upload the image again, set up a listener using Penelope and preview the logo again. This time it does not get rendered, but we get a connection back to our listener. We are root and find the final flag at /root/root.txt


Last updated
Was this helpful?