pyjail wiki

TSG CTF 2025 - SafePickle

Challenge

特定の pickle オペコードが禁止された環境。

import pickle
import io
BANNED_OPCODES = {
pickle.REDUCE, # R
pickle.GLOBAL, # c
pickle.INST, # i
pickle.OBJ, # o
pickle.BUILD, # b
}
data = input()
pickled = bytes.fromhex(data)
# バイトコードチェック
for byte in pickled:
if byte in BANNED_OPCODES:
print("Banned opcode!")
exit()
result = pickle.loads(pickled)

Solution

使用可能なオペコード

禁止されていないオペコードを確認:

オペコード記号機能
PROTO\x80プロトコルバージョン
FRAME\x95フレーム
MARK(スタックマーク
STOP.終了
PUTpメモに保存
GETgメモから取得
STRINGS文字列
TUPLEtタプル
DICTd辞書

BUILD なしでの属性設定

BUILD (b) が禁止されているが、SETITEM や他の方法で属性を設定:

# SETITEM を使用
pickle.SETITEM # s

代替のコード実行方法

# __reduce__ の代わりに
# STACK_GLOBAL (Python 3.7+) を使用
pickle.STACK_GLOBAL # \x93

Technical Details

pickle プロトコル

# プロトコルバージョン
# 0-2: ASCII ベース
# 3-5: バイナリベース
# GLOBAL vs STACK_GLOBAL
# GLOBAL: cmodule\nname\n
# STACK_GLOBAL: (文字列をスタックから取得) \x93

セキュリティバイパス

# REDUCE なしでのコード実行
# - STACK_GLOBAL + TUPLE + NEWOBJ
# - INST (禁止されていない場合)

攻撃ペイロードの構築

import pickletools
payload = b'\x80\x04\x95...' # 手動構築
pickletools.dis(payload) # デバッグ

Alternative Solutions

別解1: NEWOBJ_EX

# Python 3.6+ の新しいオペコード
pickle.NEWOBJ_EX # \x92

別解2: プロトコル 0/1 のオペコード

古いプロトコルのオペコードは禁止リストにない場合がある。

Flag

TSGCTF{...}

References