🐈
【pwn】Docker内で動いているプロセスにgdbserverでデバッグする
本記事はこちらの記事を参考に書きました。ぜひ元の記事も見に行ってください。
また本記事の内容は実際に以下の配信で検証しています。興味があればぜひ御覧ください。
本記事で取り扱う問題
SECCON Beginners CTF 2025 の pivot4b++ で行います。
2025/08/01現在、まだ公式リポジトリはないので、公開され次第記載します。
gdbserverで接続してもgdbの設定はローカル側を参照するので、サーバーサイドにpwndbg等を入れなくても大丈夫です。
Dockerfile、docker-compose.yamlの編集
Dockerfile と docker-compose.yaml を以下のように編集します。
-
FROM pwn.red/jail以降をコメントアウトする -
gdb,gdbserver,socatをインストールする -
CMDでsocatを実行する。 - gdbserver用にport forwardingする
Dockerfile
FROM ubuntu:22.04@sha256:08e2cd26ee66d0d46d6394df594f2877fc9b9381d9630a9ef5d86e27dfae9a95 AS base
WORKDIR /app
COPY chall run
RUN apt update && apt install -y gdb gdbserver socat # step2: install gdb, gdbserver, socat
RUN echo "ctf4b{*** REDACTED ***}" > /flag.txt
RUN mv /flag.txt /app/flag-$(md5sum /flag.txt | awk '{print $1}').txt
CMD socat TCP-LISTEN:5000,reuseaddr,fork EXEC:"./run" # step3: run socat
# FROM pwn.red/jail # step1: commentout
# COPY --from=base / /srv # step1: commentout
# RUN chmod +x /srv/app/run # step1: commentout
# ENV JAIL_TIME=60 JAIL_CPU=100 JAIL_MEM=10M # step1: commentout
docker-compose.yml
services:
pivot4b:
build: .
ports:
- 12300:5000
- "9090:9090" # step4: port for gdbserver
privileged: true
restart: unless-stopped
solve.pyの編集
- ファイル名とコンテナ名を相当のものにする
-
remote()でdocker内のバイナリをsocatで走らせ、docker execコマンドで、docker内のgdbserverを起動する。- read系命令で止まったところからデバッグが始まります
- 起動するためだけにprocessを叩いています。コマンドが実行できれば何でもいいです
-
gdb.attachで接続 (wsl環境の場合、tmuxがないと画面が出てくれません。) - attachは非同期で行うため、タイムラグがあります。
pause()やtime.sleep()などで待機し、同期を待ちます。
solve.py
from pwn import *
from icecream import ic
import time
remote_connection = "nc addr 12300".split()
CONTAINER_NAME = "pivot4b-2-pivot4b-1" # step1: conatiner_name
FILE_NAME = "./chall" # step1: filename
local_port = 12300
exe = ELF(f"{FILE_NAME}")
rop = ROP(exe)
context.binary = f"{FILE_NAME}"
localscript = f'''
file {context.binary.path}
define rerun
!docker exec -u root -i {CONTAINER_NAME} bash -c 'kill -9 $(pidof gdbserver) &'
!docker exec -u root -i {CONTAINER_NAME} bash -c 'gdbserver :9090 --attach $(pidof run) &'
end
define con
target remote :9090
end
'''
gdbscript = '''
b *vuln+65
'''
def start():
if args.REMOTE:
return remote(remote_connection[1], int(remote_connection[2]))
elif args.LOCAL:
return remote("localhost", local_port)
elif args.GDB:
return gdb.debug(exe.path, gdbscript=gdbscript)
else:
return process([exe.path])
def GDB():
if not args.LOCAL and not args.REMOTE:
gdb.attach(p, gdbscript=gdbscript)
pause()
if args.LOCAL:
gdbserver = process(f"docker exec -u root -i {CONTAINER_NAME} bash -c".split() + [f"gdbserver :9090 --attach $(pidof run) &"]) # step2: gdb server の起動
pid = gdb.attach(('0.0.0.0', 9090), exe=f'{context.binary.path}', #gdbscript=localscript+gdbscript) # step3: gdb serverへのアタッチ
pause() # step4: pause等で待機
# time.sleep(0.5)
# r = remote("pivot4b-2.challenges.beginners.seccon.jp", 12300)
r = start()
GDB()
Discussion