Rating: 5.0

After mildly overengeneering the previous challenge, Pyjail ATricks, I had already created some extremely shoddy code that let me encode arbitrary text to the requirements of the filter.

As a first step, it substituted a hardcoded list of numbers and a database of `__doc__` strings from the interpreter:

```
STRING_SOURCES = (
("().__doc__", "tuple() -> empty tuple\ntuple(iterable) -> tuple initialized from iterable's items\n\nIf the argument is a tuple, the return value is the same object."),
("''.__doc__", "str(object='') -> str\nstr(bytes_or_buffer[, encoding[, errors]]) -> str\n\nCreate a new string object from the given object. If encoding or\nerrors is specified, then the object must expose a data buffer\nthat will be decoded using the given encoding and error handler.\nOtherwise, returns the result of object.__str__() (if defined)\nor repr(object).\nencoding defaults to sys.getdefaultencoding().\nerrors defaults to 'strict'."),
)
[...]
```

These also revealed the python version as python 3.6, which will be relevant later on.

As a fallback, for this challenge I added `eval` of the hex escape in a string:

```
hex_escape = f"'\\x{ord(letter):x}'"
escaped = f"eval({escape_text(hex_escape)})"
```

Armed with this, I could iterate quickly. I first implemented a quick loop that
would let me type lines directly into the challenge instead of needing clumsy
copy pasting. My first experiments were around trying to get importing to work, which were
unsuccessful because it appeared `__builtins__`, which `__import__` is part of,
was horribly broken.

Earlier, lacking a `dir()` or `globals()` function, I had simply tried all global
variables available on my local python. This left me with the following list:

```
repr
print
str
__loader__
__spec__
all
bin
eval
```

Of special interest to me was `__loader__`, which is part of python's import
machinery. I quickly discovered the `SourceFileLoader.get_data(fname)` method,
which is a wrapper around file reading, as well as it's sibling
`set_data(fname, data)`. I used the former to explore and download the source
code of the PyJail, which revealed that the `__builtin__` object was actually
being globally overwritten, instead of just not being passed to `exec` as
python sandbox challenges would usually do:

```
def make_secure():
original = __builtins__.__dict__.copy()
__builtins__.__dict__.clear()
safe_builtins = ["dir", "repr", "str", "print", "eval", "input", "any", "exec", "all", "Exception"]
for func in safe_builtins:
__builtins__.__dict__[func] = original[func]
```

Obtaining this also let me iterate even more quickly by running the program
locally without any of the checks (and also working repl history!).

I eventually figured out that to progress, I'd probably need either `listdir()`
or `system()`, for which I'd need the `os` module. Alternatively, `sys` would
likely allow me to rebuild `__builtins__` or importing.

Checking the source code for `__loader__`, which surely must use `os` or `sys`
somehow, I discovered special variables named `_os` and `_sys` that `importlib` uses to boostrap itself, which I could
access though the `SourceFileLoader.get_data.__globals__` map. One quick `_os.system("sh")` `ls`
and `cat [RANDOM NAME].txt` later the flag was mine.