Skip to content

CVE-2026-93674: Verified Reproduction

CVE-2026-93674: IBM Langflow OSS 1.0.0 through 1.12.2 is vulnerable to remote unauthenticated code execution via OS command injection.

CVE-2026-93674 is verified against langflow-ai/langflow · PyPI / Python. Affected versions: 1.0.0 through 1.12.2. Fixed in 1.12.3. Vulnerability class: Command Injection. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00381.

REPRO-2026-00381 langflow-ai/langflow · PyPI / Python Command Injection Oct 9, 2026 CVE entry .txt
Severity
CRITICAL
CVSS
9.8
Confidence
HIGH
Reproduced in
74m 34s
Tool calls
241
Spend
$6.86
01 · Overview

What Is CVE-2026-93674?

CVE-2026-93674 is a critical-severity Command Injection vulnerability affecting langflow-ai/langflow 1.0.0 through 1.12.2. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00381).

02 · Severity & CVSS

CVE-2026-93674 Severity & CVSS Score

CVE-2026-93674 is rated critical severity, with a CVSS base score of 9.8 out of 10.

CRITICAL threat level
9.8 / 10 CVSS base
Weakness CWE-94 Improper Control of Generation of Code (Code Injection) — Improper Control of Generation of Code ('Code Injection')

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected langflow-ai/langflow Versions

langflow-ai/langflow · PyPI / Python versions 1.0.0 through 1.12.2 are affected.

How to Reproduce CVE-2026-93674

$ pruva-verify REPRO-2026-00381
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00381/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-93674

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

JSON code field of POST /api/v1/validate/code ("import <attacker-planted-module>"), with the module planted remotely via the class body of POST /api/v1/custom_component; superuser JWT minted with no credentials via GET /api/v1/auto_login (LANGFLOW_AUTO_LOGIN=true package default)

Attack chain
  1. POST /api/v1/validate/code
  2. lfx.custom.validate.validate_code()
  3. importlib.import_module(attacker_module) executes module top-level code in the server process (fixed in 1.12.3 by commit 461506ac2f, find_spec-only)
Runnable proof: reproduction_steps.sh
Captured evidence: fixed attempt 1 containerfixed attempt 2 container
How the agent worked 536 events · 241 tool calls · 1h 14m
1h 14mDuration
241Tool calls
101Reasoning steps
536Events
17Dead-ends
Agent activity over 1h 14m
Policy
1
Support
12
Repro
301
Judge
32
Variant
185
Verify
1
0:0073:55

Root Cause and Exploit Chain for CVE-2026-93674

Versions: 1.0.0 through 1.12.2 (IBM bulletin / NVD CPE range; the

Langflow OSS through 1.12.2 exposes POST /api/v1/validate/code, a "validation-only" endpoint whose backend (lfx.custom.validate.validate_code) calls importlib.import_module() on every import statement found in attacker-supplied code. Importing a module executes its top-level code, so the endpoint runs arbitrary Python inside the Langflow server process during what is documented as a non-executing validation step. Combined with (a) the default single-user configuration (LANGFLOW_AUTO_LOGIN=true, which lets any network client mint a superuser token from GET /api/v1/auto_login with no credentials) and (b) a second remote code-evaluation sink (POST /api/v1/custom_component, which exec()s the attacker-supplied component class body and can plant a malicious module into the server-writable site-packages), a remote unauthenticated attacker obtains arbitrary OS command execution as the Langflow service user. The vulnerability is fixed in Langflow 1.12.3 by commit 461506ac2f38f70a994b5140572b876448c11e4c ("fix(security): close pathlib/io/codecs scanner bypass and stop validate_code from executing imports", H1-3992099 / LE-2683), which replaces import_module() with importlib.util.find_spec() — locate-only, never executing module code.

  • Package/component: langflow / langflow-base / lfx (PyPI), specifically lfx.custom.validate.validate_code behind the FastAPI route POST /api/v1/validate/code.
  • Affected versions: 1.0.0 through 1.12.2 (IBM bulletin / NVD CPE range; the vulnerable importlib.import_module() loop is present in v1.12.2 and removed in v1.12.3).
  • Risk level: Critical (CVSS 9.8, AV:N/AC:L/PR:N/UI:N). Full remote, unauthenticated OS command execution with the privileges of the Langflow service account (in the official container: uid=1000(user) gid=0(root)), i.e. complete compromise of flows, stored credentials/global variables, and any data readable by the service.

Impact Parity

  • Disclosed/claimed maximum impact: remote (unauthenticated) arbitrary code / OS command execution (code_execution).
  • Reproduced impact from this run: remote unauthenticated OS command execution through the real HTTP API of the digest-pinned official langflowai/langflow:1.12.2 image: the planted module ran id via subprocess.check_output(..., shell=True) inside the server process; the output (uid=1000(user) gid=0(root) groups=0(root)) was exfiltrated in-band in the HTTP 500 detail field of the very POST /api/v1/validate/code response, and a unique per-attempt marker file was written inside the container filesystem.
  • Parity: full.
  • Not demonstrated: nothing material — the claimed impact class was reproduced end-to-end. (A persistent shell/pivot was not attempted; it is not required for parity.)

Root Cause

src/lfx/src/lfx/custom/validate.py (v1.12.2), function validate_code(code):

# Evaluate the import statements
for node in tree.body:
    if isinstance(node, ast.Import):
        for alias in node.names:
            try:
                importlib.import_module(alias.name)   # <-- EXECUTES module top-level code
            except ModuleNotFoundError as e:
                errors["imports"]["errors"].append(str(e))

importlib.import_module() is not a lookup — it loads and executes the module. Because the endpoint is reachable by any network client under the default LANGFLOW_AUTO_LOGIN=true configuration (the auto-login route issues a superuser JWT without credentials), an attacker who can place a Python file on any sys.path entry writable by the service account gets it executed by simply sending {"code": "import <module>"}. The official container runs as uid=1000 and owns /app/.venv/lib/python3.14/site-packages, which is on sys.path, so the built-in custom-component code-evaluation feature (POST /api/v1/custom_component → build_custom_component_template() → exec() of the class body) provides the file-write primitive fully remotely:

class Planter(CustomComponent):
    _w = pathlib.Path("/app/.venv/lib/python3.14/site-packages/<mod>.py").write_text(payload)

The planted module both writes a unique marker file and raises RuntimeError("PLANTED_EXEC:<token>:" + subprocess.check_output("id", shell=True)). validate_code only catches ModuleNotFoundError, so the RuntimeError propagates to the route handler, which returns HTTP 500 with detail=str(e) — exfiltrating the command output directly in the HTTP response.

In 1.12.3 the same request path performs importlib.util.find_spec(alias.name.split(".")[0]) and never executes module code; the identical attacker procedure therefore produces HTTP 200, empty errors, and no marker file.

  • Fix commit: 461506ac2f38f70a994b5140572b876448c11e4c (fix(security): close pathlib/io/codecs scanner bypass and stop validate_code from executing imports, PR #15201, H1-3992099 / LE-2683).
  • Fixed release: Langflow OSS 1.12.3 (git tag v1.12.3 = fec71dca901949c09ed4d63315804337cd2eb13d).

Note on CVE mapping: the IBM bulletin for 1.12.3 lists 25 CVEs without per-CVE commit mapping. CVE-2026-93674 is the 9.8 PR:N CWE-94 ("code injection / OS command") entry; the validate_code import-execution sink fixed by 461506ac2f is the matching unauthenticated remote code-execution fix in the 1.12.3 security train. The public third-party PoC (rmhowe425/POC-CVE-2026-93674) targets the MCP stdio endpoint (/api/v2/mcp/servers), which corresponds to the earlier GHSA-w794-rj3p-xv45 / CVE-2026-105697 fix (1.10.3) — that allowlist is already present in 1.12.2, so the MCP path is not the 1.12.2→1.12.3 divergence; the validate/code path is.

Reproduction Steps

  1. bundle/repro/reproduction_steps.sh (self-contained; requires docker, curl, jq).
  2. The script:
    • Pulls/pins the official images by digest: vulnerable langflowai/langflow@sha256:79c02794adebe82d756b7152ce4feebe4a5426e1faf3fe5b5d0dd08f304510c4 (v1.12.2) and fixed langflowai/langflow@sha256:34055a07d446de51760e28dab6332e22624e5f48dca611567779992fc32c5ec0 (v1.12.3).
    • Starts two fresh vulnerable containers and two fresh fixed containers (LANGFLOW_AUTO_LOGIN=true, the OSS package default), waiting for /health.
    • Per attempt: (1) GET /api/v1/auto_login with no credentials → superuser JWT; (2) POST /api/v1/custom_component plants pruva_planted_<run>_<n>.py into site-packages via class-body exec(); (3) POST /api/v1/validate/code {"code":"import <module>"} triggers the vulnerable import execution; the marker file is read back out of the container.
    • Vulnerable pass criteria: HTTP 500 + PLANTED_EXEC:<token>:uid=1000(user)... in the response body + marker file containing the unique token inside the container. Fixed pass criteria: HTTP 200, empty errors, no marker.
  3. Expected evidence: 2/2 vulnerable attempts execute attacker code; 2/2 fixed attempts do not (identical procedure, plant still succeeds on fixed — proving the divergence is exactly the validate/code import execution).

Evidence

  • Driver log: bundle/logs/reproduction_steps.log
  • Image identity: bundle/logs/repro/image_identity.txt
  • Per-attempt artifacts ({vuln,fixed}_attempt_{1,2}_* under bundle/logs/repro/attempts/): auto-login token responses, plant requests/responses, trigger requests/responses, planted module content, marker files, container logs. All SHA-256-bound in bundle/repro/runtime_manifest.json.
  • Key excerpt (vulnerable, both attempts): POST /api/v1/validate/code → HTTP=500, body {"detail":"PLANTED_EXEC:PRUVA-CVE-2026-93674-<run>-VULN-<n>:uid=1000(user) gid=0(root) groups=0(root)"}, and marker.txt = PRUVA-CVE-2026-93674-<run>-VULN-<n> uid=1000(user) gid=0(root) groups=0(root).
  • Key excerpt (fixed, both attempts): identical requests → HTTP=200, body {"imports":{"errors":[]},"function":{"errors":[]}}, empty marker file.
  • Environment: Docker on Linux x86_64; images digest-pinned as above; Python 3.14 inside the container; LANGFLOW_AUTO_LOGIN=true (package-level default; the image sets it to false, which only changes the bootstrap to credential-based login — the validate/code sink itself is identical).

Recommendations / Next Steps

  • Upgrade to Langflow OSS 1.12.3 or later (IBM/vendor guidance; the fix replaces import_module() with find_spec() in validate_code).
  • Until upgraded: do not expose Langflow to untrusted networks; set LANGFLOW_AUTO_LOGIN=false and strong superuser credentials (raises the bar to authenticated, but the sink still executes for any authenticated user on ≤1.12.2); restrict file-system write access of the service account to site-packages.
  • Defense-in-depth: treat every "validation" endpoint as non-executing (audit for other import_module/exec uses on request paths), and consider read-only root filesystems / non-root containers.
  • Testing: regression test that POST /api/v1/validate/code with an import of a planted module never executes it (covered upstream by the lfx-side tests added in the fix commit).

Additional Notes

  • Idempotency: the script is fully idempotent — each attempt uses a fresh, uniquely-named container and a unique module/marker name, and a trap removes all containers on exit. Re-running produces fresh unique tokens/markers.
  • Repeatability: the exploit ran twice per side (two fresh vulnerable processes, two fresh fixed processes) with identical outcomes.
  • Why two stages: the CVE sink (validate_code import execution) requires an importable attacker module. The plant uses Langflow's built-in custom-component code-evaluation feature, which behaves identically on 1.12.2 and 1.12.3 — the vulnerable/fixed divergence is isolated entirely to the validate_code step, which is what the 1.12.3 fix changed.
  • Limitations: none affecting the verdict. The reproduction uses the official vendor images at the exact vulnerable/fixed digests; no sanitizers, mocks, or instrumentation were used.

CVE-2026-93674 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:56
0:00
session startedaccounts/fireworks/models/kimi-k3 · CVE-2026-93674 · REPRO-20
0:09
0:10
web search
0:11
0:19
0:20
1:23
1:24
extract_facts
no facts extracted
1:26
1:26
supportclaim_contract
1:38
1:39
1:39
1:39
1:43
1:43
1:43
1:55
1:56
$ls -la /pruva/project-cache/ 2>/dev/null; ls /pruva/project-cache/repo 2>/dev/null | head; cat /pruva/project-cache/.pruva/cache_manifest.json 2>/dev/null; ls /workspace/bundle/.pruva/ 2>/dev/null
0.5s✓
total 4
drwx------ 4 vscode vscode   80 Oct  8 22:36 .
drwxr-xr-x 3 root   root   4096 Oct  8 22:37 ..
drwx------ 2 vscode vscode   60 Oct  8 22:36 .pruva
drwx------ 2 vscode vscode   40 Oct  8 22:36 repo-mirrors
{
  "schema_version": 1,
  "entries": [
1:58
web search
2:07
2:09
2:10
$curl -sL https://api.github.com/repos/rmhowe425/POC-CVE-2026-93674/contents/ 2>/dev/null | head -100; echo ===; curl -sL https://raw.githubusercontent.com/rmhowe425/POC-CVE-2026-93674/main/README.md 2>/dev/null | head -150
1.1s✓
{"cwd":"/workspace","duration_ms":727,"exit_code":0,"stderr":"","stdout":"[\n  {\n    \"name\": \"README.md\",\n    \"path\": \"README.md\",\n    \"sha\": \"084c122905dea5e24d127cd2d673079375de4eb4\",\n    \"size\": 547,\n    \"url\": \"https://api.github.com/repos/rmhowe425/POC-CVE-2026-93674/contents/README.md?ref=main\",\n    \"html_url\": \"https://github.com/rmhowe425/POC-CVE-2026-93674/blob/main/README.md\",\n    \"git_url\": \"https://api.github.com/repos/rmhowe425/POC-CVE-2026-93674/git/blobs/084c122905dea5e24d127cd2d673079375de4eb4\",\n    \"download_url\": \"https://raw.githubusercontent.com/rmhowe425/POC-CVE-2026-93674/main/README.md\",\n    \"type\": \"file\",\n    \"_links\": {\n      \"self\": \"https://api.github.com/repos/rmhowe425/POC-CVE-2026-93674/contents/README.md?ref=main\",\n      \"git\": \"https://api.github.com/repos/rmhowe425/POC-CVE-2026-93674/git/blobs/084c122905dea5e24d127cd2d673079375de4eb4\",\n      \"html\": \"https://github.com/rmhowe425/POC-CVE-2026-93674/blob/main/README.md\"\n    }\n  },\n  {\n    \"name\": \"poc.py\",\n    \"path\": \"poc.py\",\n    \"sha\": \"c570cd8f8a8d5176705efd2ae3dbc47546a87cd6\",\n    \"size\": 3450,\n    \"url\": \"http… [truncated]
2:16
$curl -sL https://raw.githubusercontent.com/rmhowe425/POC-CVE-2026-93674/main/poc.py
0.9s✓
{"cwd":"/workspace","duration_ms":281,"exit_code":0,"stderr":"","stdout":"# Exploit Title:  Langflow security bypass\r\n# Exploit Author: Richard Howe <rhowe425>\r\n# Vendor Homepage: https://www.langflow.org/\r\n# Software Link: https://www.langflow.org/desktop\r\n# Version: 1.5.0 - 1.9.0\r\n# Tested on: Ubuntu 22.04\r\n# CVE : CVE-2026-93674\r\n\r\nimport secrets\r\nimport argparse\r\nfrom requests import get, post\r\n\r\ndef execute_command(base_url: str, auth:dict):\r\n    endpoint = '/api/v2/mcp/servers?action_count=true'\r\n\r\n    try:\r\n        resp = get(base_url + endpoint, headers=auth)\r\n        resp_json = resp.json()\r\n    except Exception as e:\r\n        raise RuntimeError(f\"Error querying API.\\n{e}\")\r\n\r\n    if resp.status_code != 200:\r\n        raise RuntimeError(\r\n            f\"API returned with status code: {resp.status_code}\"\r\n        )\r\n\r\n    return resp\r\n\r\n\r\ndef save_command(base_url: str, auth: dict, server_name: str, command: str, args: str):\r\n    endpoint = f\"/api/v2/mcp/servers/{server_name}\"\r\n    data = {\"command\": f\"{command}\", \"args\": [f\"{args}\"]}\r\n\r\n    print(\"[+] Triggering the vulnerability.\")\r\n\r\n   … [truncated]
2:37
2:52

Artifacts and Evidence for CVE-2026-93674

Scripts, logs, diffs, and output captured during the reproduction.

bundle/logs/repro/attempts/fixed_attempt_1_autologin_response.json0.4 KB
bundle/logs/repro/attempts/fixed_attempt_1_container.log71.6 KB
bundle/logs/repro/attempts/fixed_attempt_1_marker.txt0.0 KB
bundle/logs/repro/attempts/fixed_attempt_1_plant_request.json0.7 KB
bundle/logs/repro/attempts/fixed_attempt_1_plant_response.json1.5 KB
bundle/logs/repro/attempts/fixed_attempt_1_planted_module.py.txt0.3 KB
bundle/logs/repro/attempts/fixed_attempt_1_trigger_request.json0.1 KB
bundle/logs/repro/attempts/fixed_attempt_2_autologin_response.json0.4 KB
bundle/logs/repro/attempts/fixed_attempt_2_container.log71.6 KB
bundle/logs/repro/attempts/fixed_attempt_2_marker.txt0.0 KB
bundle/logs/repro/attempts/fixed_attempt_2_plant_request.json0.7 KB
bundle/logs/repro/attempts/fixed_attempt_2_plant_response.json1.5 KB
bundle/logs/repro/attempts/fixed_attempt_2_planted_module.py.txt0.3 KB
bundle/logs/repro/attempts/fixed_attempt_2_trigger_request.json0.1 KB
bundle/logs/repro/attempts/fixed_attempt_2_trigger_response.json0.0 KB
bundle/logs/repro/attempts/vuln_attempt_1_autologin_response.json0.4 KB
bundle/logs/repro/attempts/vuln_attempt_1_container.log71.2 KB
bundle/logs/repro/attempts/vuln_attempt_1_planted_module.py.txt0.3 KB
bundle/logs/repro/attempts/vuln_attempt_2_autologin_response.json0.4 KB
bundle/logs/repro/attempts/vuln_attempt_2_container.log71.2 KB
bundle/logs/repro/attempts/vuln_attempt_2_plant_request.json0.7 KB
bundle/logs/repro/attempts/vuln_attempt_2_plant_response.json1.5 KB
bundle/logs/repro/attempts/vuln_attempt_2_planted_module.py.txt0.3 KB
bundle/logs/reproduction_steps.log3.4 KB
bundle/repro/rca_report.md10.0 KB
bundle/repro/reproduction_steps.sh13.1 KB
bundle/repro/runtime_manifest.json7.3 KB
bundle/repro/validation_verdict.json1.6 KB
08 · How to Fix

How to Fix CVE-2026-93674

Upgrade langflow-ai/langflow · PyPI / Python to 1.12.3 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-93674 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-93674

Is CVE-2026-93674 exploitable?

Yes. Pruva independently reproduced CVE-2026-93674 in langflow-ai/langflow and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00381).

How severe is CVE-2026-93674?

CVE-2026-93674 is rated critical severity, with a CVSS score of 9.8 out of 10.

What type of vulnerability is CVE-2026-93674?

CVE-2026-93674 is classified as CWE-94 Improper Control of Generation of Code (Code Injection) (Improper Control of Generation of Code ('Code Injection')), a Command Injection vulnerability.

Which versions of langflow-ai/langflow are affected by CVE-2026-93674?

langflow-ai/langflow 1.0.0 through 1.12.2 is affected by CVE-2026-93674.

Is there a fix for CVE-2026-93674?

Yes. CVE-2026-93674 is fixed in langflow-ai/langflow 1.12.3. Upgrading to the fixed version remediates the issue.

How can I reproduce CVE-2026-93674?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the CVE-2026-93674 reproduction verified?

Yes. Pruva reproduced CVE-2026-93674 with high confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

References for CVE-2026-93674

Authoritative sources for CVE-2026-93674 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.