Vulnerability Analysis

CVE-2026-33017: Langflow Unauthenticated RCE — What It Is & How to Fix It

Executive Summary

CVE-2026-33017 is a critical unauthenticated remote code execution (RCE) vulnerability in Langflow, the popular open-source framework used for building AI agent and RAG pipelines. The flaw allows a remote, unauthenticated attacker to execute arbitrary Python code on any exposed Langflow instance with a single HTTP request. Active exploitation was observed within 20 hours of public disclosure, and CISA has added the CVE to its Known Exploited Vulnerabilities catalog — making immediate patching to version 1.9.0 (not 1.8.2) urgent.


1. What Is This Vulnerability?

Langflow exposes a public API endpoint designed to let unauthenticated users "build" shared flows:

POST /api/v1/build_public_tmp/{flow_id}/flow

This endpoint is intended for publicly shared flows that guests can execute without logging in. The bug lies in how optional request body data is handled: when a caller supplies a data parameter in the JSON body, Langflow substitutes the attacker-supplied flow definition for the stored one. That flow definition can contain arbitrary Python code embedded in node configurations — and Langflow passes it directly to exec() with zero sandboxing.

# Simplified vulnerable pattern (pseudocode from patched diff)
flow_data = request.json.get("data") or db.get_flow(flow_id)
# ...
exec(flow_data["nodes"][n]["data"]["code"])   # ← no sandbox, no validation

Because no authentication check guards the data override path, any internet-reachable Langflow instance is exploitable.

Attack Vector

  1. Attacker enumerates (or guesses) any valid flow_id — or uses a dummy UUID accepted by some versions.
  2. A single POST request is sent containing a flow payload whose node code is a reverse shell or credential-harvesting script.
  3. The server executes the Python payload with the privileges of the Langflow process (often root inside Docker containers).
  4. Full server-side code execution is achieved, typically within milliseconds.

No CVE PoC was publicly available at the time of the first attacks. Threat actors reverse-engineered the advisory description alone to build working exploits.

Real-World Impact

The Sysdig Threat Research Team observed exploitation from six unique source IPs within 48 hours of disclosure. The attack lifecycle unfolded in three phases:

  • Phase 1 (hours 0–20): Mass automated scanning from four IPs delivering identical payloads.
  • Phase 2 (hours 20–36): Active reconnaissance with pre-staged attacker infrastructure, likely targeting high-value AI/ML deployments.
  • Phase 3 (hours 36–48): Data exfiltration — environment variables, API keys, database credentials, and LLM provider tokens were siphoned to attacker-controlled hosts.

Compromised Langflow instances expose not only the host server but every downstream service connected to the AI pipeline: vector databases, LLM API accounts, cloud credentials, and potentially software supply chains.


2. Who Is Affected?

Component Affected Versions
Langflow (open-source) ≤ 1.8.1 and 1.8.2 (incomplete patch)
Langflow Cloud (SaaS) Patched by vendor — no user action required
Self-hosted Docker deployments Affected if image tag is not 1.9.0+
Kubernetes deployments Affected if Helm chart image is not 1.9.0+

⚠️ Version 1.8.2 is still vulnerable. The patch in 1.8.2 was incomplete — JFrog Security Research confirmed the exploit path remains open. Only version 1.9.0 or later fully closes the vulnerability.

Instances are particularly at risk if:

  • The Langflow UI/API port (default 7860) is internet-facing without authentication.
  • A reverse proxy or API gateway does not enforce authentication on the /api/v1/build_public_tmp/ path.
  • The process runs with elevated privileges (e.g., as root in Docker).

3. How to Detect It (Testing)

Manual Testing Steps

  1. Identify exposure: Confirm whether /api/v1/build_public_tmp/ is reachable from untrusted networks.

    curl -I https://<your-langflow-host>/api/v1/build_public_tmp/test-id/flow
    

    A 404 or 422 (not 401/403) without credentials confirms the endpoint is unauthenticated.

  2. Attempt a benign code injection: Send a controlled payload that writes a marker file (in a non-production environment only):

    curl -X POST https://<host>/api/v1/build_public_tmp/test/flow \
      -H "Content-Type: application/json" \
      -d '{"data":{"nodes":[{"data":{"code":"import os; open('/tmp/pwned.txt','w').write('vuln')"}}]}}'
    

    Check for /tmp/pwned.txt on the server. Its presence confirms vulnerability.

  3. Check the running version:

    curl https://<host>/api/v1/version
    # Look for "version" field — anything below 1.9.0 is vulnerable
    

Automated Scanning

  • Tool: Nuclei (ProjectDiscovery)

  • Template: Search for community template tagged CVE-2026-33017 in the nuclei-templates repository.

    nuclei -u https://<host> -t cves/2026/CVE-2026-33017.yaml
    
  • Expected output: [critical] [CVE-2026-33017] [http] https://<host>/api/v1/build_public_tmp/... confirms vulnerable instance.

  • Tool: Grype or Trivy (for image scanning)

    trivy image langflowai/langflow:1.8.1
    # Reports CVE-2026-33017 under HIGH/CRITICAL findings
    

Code Review Checklist

  • Verify the build_public_tmp endpoint enforces strict input validation and does not pass request.data to any exec() or eval() call.
  • Confirm flow node code fields are validated against an allowlist before execution.
  • Check that public endpoints require the flow_id to match a database-stored record (no arbitrary UUIDs accepted).
  • Ensure sandboxing (e.g., RestrictedPython or subprocess isolation) is applied to all user-defined code paths.

4. How to Fix It (Mitigation)

Step-by-Step Remediation

  1. Update Langflow to version 1.9.0 or later — the only version with a complete fix.

    pip install langflow==1.9.0
    # or for Docker:
    docker pull langflowai/langflow:1.9.0
    
  2. Do NOT stop at 1.8.2 — confirm your installed version is exactly 1.9.0+:

    python -c "import langflow; print(langflow.__version__)"
    
  3. Rotate all secrets immediately if the instance was internet-accessible at any time on a vulnerable version:

    • LLM API keys (OpenAI, Anthropic, etc.)
    • Vector database credentials
    • Cloud provider credentials (AWS, GCP, Azure)
    • Any database connection strings stored in environment variables
  4. Audit logs for signs of exploitation:

    • Unexpected outbound connections from the Langflow process
    • POST requests to /api/v1/build_public_tmp/ from external IPs
    • Unusual files written to /tmp or the application working directory
  5. Deploy the instance behind authentication — even after patching, the public build endpoint should not be world-accessible unless your use case explicitly requires it.

Network-Level Mitigation (Immediate, Pre-Patch)

If you cannot patch immediately, block or authenticate the vulnerable path at the reverse proxy level:

# nginx example: require basic auth for the vulnerable path
location /api/v1/build_public_tmp/ {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://langflow:7860;
}

Or block it entirely if public flows are not required:

location /api/v1/build_public_tmp/ {
    return 403;
}

Code Fix Example

The fix in version 1.9.0 removes the ability to supply arbitrary data overrides from unauthenticated requests and adds input validation before any code execution:

# BEFORE (vulnerable — 1.8.1 and 1.8.2)
flow_data = request.json.get("data") or await get_flow_from_db(flow_id)
nodes = flow_data["nodes"]
for node in nodes:
    exec(node["data"]["code"])   # arbitrary code execution

# AFTER (patched — 1.9.0)
# Unauthenticated endpoint no longer accepts caller-supplied flow data
flow_data = await get_flow_from_db(flow_id)   # DB record only, no override
if flow_data is None or not flow_data.is_public:
    raise HTTPException(status_code=403)
# Code fields validated against allowlist before execution
validated_code = validate_node_code(node["data"]["code"])
sandbox_exec(validated_code)

Configuration Hardening

  • Set LANGFLOW_AUTO_LOGIN=false and configure proper user authentication.
  • Run Langflow as a non-root user inside containers (USER langflow in Dockerfile).
  • Mount the application directory read-only where possible.
  • Use network policies (Kubernetes) to restrict Langflow's egress to known endpoints only.

5. How to Test the Fix (Validation)

Regression Test Scenarios

  • Scenario A: Verify the patch is applied — GET /api/v1/version returns 1.9.0 or higher.
  • Scenario B: Replay the exploit payload against the patched instance and confirm a 403 Forbidden or 422 Unprocessable Entity response — no code execution occurs.
  • Scenario C: Confirm legitimate public flow execution still works for flows explicitly marked public in the database.

Security Test Cases

Test Case 1: Verify RCE no longer possible via data override

  • Precondition: Langflow 1.9.0 installed; a valid public flow_id exists in the DB.
  • Steps: Send POST /api/v1/build_public_tmp/{flow_id}/flow with a data body containing malicious Python code.
  • Expected Result: Server returns 403 or ignores the data field; no code is executed; no marker file created.

Test Case 2: Confirm unauthenticated endpoint still serves legitimate public flows

  • Precondition: A flow is explicitly set as public in the database.
  • Steps: Send POST /api/v1/build_public_tmp/{flow_id}/flow with no data body.
  • Expected Result: Flow executes using only the DB-stored definition; response 200 OK.

Test Case 3: Confirm the endpoint rejects unknown flow IDs

  • Precondition: Use a random UUID as flow_id.
  • Steps: Send the POST request.
  • Expected Result: 404 Not Found — no code path reached.

Automated Test

import requests, pytest

BASE_URL = "https://your-langflow-host"
VALID_PUBLIC_FLOW_ID = "your-known-public-flow-id"

MALICIOUS_PAYLOAD = {
    "data": {
        "nodes": [{
            "data": {
                "code": "import os; os.system('touch /tmp/cve_2026_33017_pwned')"
            }
        }]
    }
}

def test_rce_not_exploitable():
    r = requests.post(
        f"{BASE_URL}/api/v1/build_public_tmp/{VALID_PUBLIC_FLOW_ID}/flow",
        json=MALICIOUS_PAYLOAD,
        timeout=10
    )
    assert r.status_code in (403, 422), f"Unexpected status: {r.status_code}"
    # Verify no marker file was created (requires server-side check or OOB callback)

def test_version_is_patched():
    r = requests.get(f"{BASE_URL}/api/v1/version", timeout=10)
    version = r.json().get("version", "0.0.0")
    major, minor, patch = map(int, version.split(".")[:3])
    assert (major, minor, patch) >= (1, 9, 0), f"Unpatched version: {version}"

6. Prevention & Hardening

Best Practices

  • Never expose AI orchestration frameworks directly to the internet. Place Langflow, Flowise, and similar tools behind an authenticated reverse proxy or VPN.
  • Audit public endpoints after every upgrade. Any endpoint that accepts unauthenticated input and processes code (even indirectly) is a high-risk target.
  • Implement secrets management. Store LLM API keys and DB credentials in a vault (HashiCorp Vault, AWS Secrets Manager) rather than environment variables — this limits blast radius if an RCE occurs.
  • Apply the principle of least privilege to the Langflow process: restrict filesystem writes, outbound network access, and system calls.
  • Pin dependency versions in Docker images and CI/CD pipelines; subscribe to Langflow's GitHub security advisories to be notified of future patches.

Monitoring & Detection

  • SIEM rule: Alert on unexpected POST requests to /api/v1/build_public_tmp/ from external IPs, especially with a Content-Type: application/json body larger than your typical flow payloads.
  • Egress monitoring: Alert on outbound connections from the Langflow process to IPs not in your approved LLM/cloud provider list.
  • File Integrity Monitoring (FIM): Monitor /tmp and the application directory for unexpected file creation — a common attacker beachhead indicator.
  • Container runtime security: Tools like Falco or Sysdig can detect anomalous exec() calls spawning shells from a Python process.
# Falco rule example
- rule: Langflow RCE Indicator
  desc: Detect shell spawned from Langflow Python process
  condition: >
    spawned_process and
    proc.pname = "python3" and
    proc.name in (shell_binaries) and
    container.image.repository contains "langflow"
  output: >
    Shell spawned inside Langflow container (cmd=%proc.cmdline container=%container.id)
  priority: CRITICAL

References

Latest from the blog

See all →