## ENOJAIL> We launched a new service today. It's called ENOJAIL™ and gives the user access to an unlimited IPython shell. Sadly, management decided we need to leave our valuable `flag.txt` on the server, so naturally, engineering had to lock it back down a bit. But we are sure you will still enjoy all of the possibilities, such as.. Calculating 5/5 fully interactively, evaluating '1', and so much more!
### What's going on?It's all about a broken iPython Shell. We observed that some characters were not allowed.So we tried to print'em all, and got the whitelisted characters:```0123456789abcdefghijklmABCDEFGHIJKLMNOP&'-./:;<=@_`````
We also tried some bash commands, and we saw some of them worked (`cd`, `ll`).`ll` showed us 2 files, `flag.txt` and `enojail.py`.However, commands with blacklisted characters couldn't be valid.
### The solutionFor this reason, we thought about commands with only allowed characters and we found out `head`, `diff` were perfect for printing the flag. Nonetheless, commands like `head` and `diff` didn't work alone. After a few attempts, we observed `ll;` was perfect for command injection:
```bash`ll; head `diff ./ ../` -c 100000000000````- `ll` is important, we can use it for command injection e.g. `head`- `head -c 100000000000` shows the first 100000000000 bytes- `diff` shows the differences between the current directory and the parent dir.-  ```head `diff ./ ../` -c 100000000000``` shows the first 100000000000 byte difference between the parent dir and the current dir, exposing `flag.txt` from the current dir.
Voilà, the flag.
### An interesting script
*We love this pyfuck variant*, so here's the `enojail.py` source code leaked from the challenge.
```python#!/usr/bin/env python3"""ENOJAIL"""import re, sys, signal, datetimefrom subprocess import Popen, PIPEfrom functools import partialfrom socketserver import ForkingTCPServer, BaseRequestHandler
PORT = 5656REGEX = r"p*[^ &\--=@-P'_-m]*,*"
class RequestHandler(BaseRequestHandler):    def handle(self):        print(            "{}: session for {} started".format(                datetime.datetime.now(), self.client_address[0]            )        )        fd = self.request.makefile("rwb", buffering=0)        main(fd, fd, bytes=True)
def main(f_in=sys.stdin, f_out=sys.stdout, bytes=False):    def enc(str):        if bytes:            return str.encode()        return str
    def decode(b):        if bytes:            return b.decode()        return b
    def alarm_handler(signum, frame):        f_out.write(enc("\nThank you for your visit.\nPlease come back soon. :)\n"))        print("{}: Another timeout reached.".format(datetime.datetime.now()))        sys.exit(15)
    if "debug" not in sys.argv:        signal.signal(signal.SIGALRM, alarm_handler)        signal.alarm(15)
    f_out_no = f_out.fileno()
    r = REGEX    cat_food = partial(re.compile(r).sub, "")    proc = Popen(        ["python3", "-u", "-m", "IPython", "--HistoryManager.enabled=False"],        stdin=PIPE,        stdout=f_out_no,        stderr=f_out_no,    )    si = proc.stdin
    f_out.write(        f"""Welcome to the ENOJAIL™ interactive IPython experience!You can IPython all you want.Just please don't look at ./flag.txt, thanks!
Oh, actually, we will filter out some characters before we eval them, just to be on the safe side.Get started by tying '1' or 1 / 1.
Enjoy your stay! :)\n\n""".encode()    )
    while True:        userinput = cat_food(decode(f_in.readline())).strip()        # forward "sanitized" input to IPython        si.write("{}\n".format(userinput).encode())        si.flush()
if __name__ == "__main__":    print("Listening on port {}".format(PORT))    ForkingTCPServer(("0.0.0.0", PORT), RequestHandler).serve_forever() ```