Copied from the first link.
See Werkzeug “console locked” message by forcing debug error page in the app.
The console is locked and needs to be unlocked by entering the PIN.
You can find the PIN printed out on the standard output of your
shell that runs the server
Locate vulnerable Werkzeug debug console at path vulnerable-site.com/console, but is locked by secret PIN number.
You can reverse the algorithm generating the console PIN. Inspect Werkzeug’s debug __init__.py file on server e.g. python3.5/site-packages/werkzeug/debug/__init__.py. View Werkzeug source code repo, but better to leak source code through file traversal vulnerability since versions likely differ.
In this file, see relevant method outlining steps to generate console PIN:
defget_pin_and_cookie_name(app): pin = os.environ.get('WERKZEUG_DEBUG_PIN') rv =None num =None# Pin was explicitly disabledif pin =='off':returnNone,None# Pin was provided explicitlyif pin isnotNoneand pin.replace('-', '').isdigit():# If there are separators in the pin, return it directlyif'-'in pin: rv = pinelse: num = pin modname =getattr(app, '__module__',getattr(app.__class__, '__module__'))try:# `getpass.getuser()` imports the `pwd` module,# which does not exist in the Google App Engine sandbox. username = getpass.getuser()exceptImportError: username =None mod = sys.modules.get(modname)# This information only exists to make the cookie unique on the# computer, not as a security feature. probably_public_bits = [ username, modname,getattr(app, '__name__', getattr(app.__class__, '__name__')),getattr(mod, '__file__', None), ]# This information is here to make it harder for an attacker to# guess the cookie name. They are unlikely to be contained anywhere# within the unauthenticated debug page. private_bits = [str(uuid.getnode()),get_machine_id(), ] h = hashlib.md5()for bit inchain(probably_public_bits, private_bits):ifnot bit:continueifisinstance(bit, text_type): bit = bit.encode('utf-8') h.update(bit) h.update(b'cookiesalt') cookie_name ='__wzd'+ h.hexdigest()[:20]# If we need to generate a pin we salt it a bit more so that we don't# end up with the same value and generate out 9 digitsif num isNone: h.update(b'pinsalt') num = ('%09d'%int(h.hexdigest(), 16))[:9]# Format the pincode in groups of digits for easier remembering if# we don't have a result yet.if rv isNone:for group_size in5,4,3:iflen(num)% group_size ==0: rv ='-'.join(num[x:x + group_size].rjust(group_size, '0')for x inrange(0, len(num), group_size))breakelse: rv = numreturn rv, cookie_name
getattr(app, '__name__', getattr (app .__ class__, '__name__')) is Flask
getattr(mod, '__file__', None) is the absolute path of an app.py in the flask directory
uuid.getnode() is the MAC address of the current computer, str (uuid.getnode ()) is the decimal expression of the mac address
get_machine_id() read the value in /etc/machine-id or /proc/sys/kernel/random/boot_id and return directly if there is, sometimes it might be required to append a piece of information within /proc/self/cgroup that you find at the end of the first line (after the third slash)
To find server MAC address, need to know which network interface is being used to serve the app (e.g. ens3). If unknown, leak /proc/net/arp for device ID and then leak MAC address at /sys/class/net/<device id>/address.
Convert from hex address to decimal representation by running in python e.g.:
>>>print(0x5600027a23ac)94558041547692
Once all variables prepared, run exploit script to generate Werkzeug console PIN: