Vulnerability Analysis

CVE-2025-34291: Critical CORS + RCE Vulnerability in Langflow AI Workflow Platform

Executive Summary

CVE-2025-34291 is a critical-severity (CVSS 9.4) vulnerability chain in Langflow, the widely-used open-source AI agent and workflow orchestration platform. The flaw combines an overly permissive CORS policy, a cross-site-deliverable refresh token cookie, and an unprotected code-execution endpoint to allow a remote attacker to hijack any authenticated user's session and run arbitrary code — simply by tricking the victim into visiting a malicious webpage. CISA added this to its Known Exploited Vulnerabilities (KEV) catalog on May 21, 2026, with a federal remediation deadline of June 4, 2026, and active exploitation has been attributed to the Iranian state-sponsored group MuddyWater.


1. What Is This Vulnerability?

Langflow is a low-code, browser-based platform for building, deploying, and managing AI agent pipelines and LLM-powered workflows. Organisations use it to chain together model calls, database lookups, API integrations, and automation steps — meaning a compromised Langflow instance typically holds a trove of API keys, cloud credentials, and SaaS access tokens.

CVE-2025-34291 is not a single flaw — it is a three-weakness chain (CWE-346: Origin Validation Error) that collapses into full account takeover and remote code execution:

Weakness 1 — Overly Permissive CORS

Langflow's server accepted cross-origin requests with credentials (Access-Control-Allow-Credentials: true) from any origin. A browser will honour this and include session cookies in cross-origin fetch calls initiated by a malicious page.

Weakness 2 — SameSite=None Refresh Token Cookie

The refresh_token cookie was scoped as SameSite=None; Secure, meaning the browser would attach it to cross-site requests. Combined with the CORS misconfiguration, an attacker-controlled page can call /api/v1/auth/refresh cross-origin, receive a fresh access token, and store it for subsequent requests.

Weakness 3 — Unauthenticated (Post-Takeover) Code Execution Endpoint

Langflow exposes a /api/v1/run/{flow_id} endpoint (and direct Python-code execution endpoints in some UI flows) that, once authenticated, allow arbitrary code execution by design. After stealing the token in steps 1–2, the attacker calls this endpoint with attacker-controlled code, achieving full server-side RCE.

Attack Vector

The entire exploit is delivered through a single browser visit:

1. Victim authenticates to their Langflow instance (token stored in cookie).
2. Victim visits attacker's malicious webpage.
3. Attacker's page sends a cross-origin fetch to:
     POST https://langflow.internal/api/v1/auth/refresh
   Browser attaches the SameSite=None refresh_token cookie.
4. CORS policy permits the cross-origin response → attacker receives a valid access_token.
5. Attacker's page immediately calls:
     POST https://langflow.internal/api/v1/run/<flow_id>
   with the stolen token and attacker-controlled component code.
6. Langflow server executes the injected Python payload under the service account.

No user interaction beyond visiting the malicious URL is required.

Real-World Impact

Active exploitation was first observed on January 23, 2026. The Iranian state-sponsored threat group MuddyWater (also tracked as MANGO SANDSTORM) has been linked to in-the-wild exploitation of this flaw, using it for initial access to target enterprise networks. Because Langflow workspaces routinely store API keys for OpenAI, AWS, Azure, Slack, databases, and internal automation tools, a single compromise can laterally pivot into the broader cloud environment.


2. Who Is Affected?

Component Affected Versions Fixed Version
langflow-base (PyPI) ≤ 1.6.9 ≥ 1.9.3
Langflow Docker image Tags prior to 1.9.3 1.9.3 and later
Langflow Cloud (DataStax hosted) Instances not yet migrated to ≥ 1.9.3 Patched by vendor

You are at risk if:

  • You run a self-hosted Langflow instance accessible from a browser (even on an internal network).
  • You use Langflow Cloud with SSO or persistent sessions.
  • Langflow users visit external websites while authenticated (i.e., any normal user in any organisation).
  • Your Langflow instance holds API keys for cloud providers, SaaS services, or AI model providers.

Deployments behind strict network firewalls are partially protected if the attacker cannot reach the Langflow server from the victim's browser — but internal-network pivoting (e.g., via a phishing email that delivers the malicious page) still creates risk.


3. How to Detect It (Testing)

Manual Testing Steps

Step 1 — Check your Langflow version

# If running via pip
pip show langflow | grep Version

# If running via Docker
docker inspect <container_id> | grep -i version
# or
curl -s http://localhost:7860/api/v1/version

Any version ≤ 1.6.9 is vulnerable.

Step 2 — Inspect the CORS response headers

curl -s -I \
  -H "Origin: https://evil.example.com" \
  -H "Cookie: refresh_token=<any_value>" \
  http://<langflow-host>/api/v1/auth/refresh \
  | grep -i "access-control"

A vulnerable server returns:

Access-Control-Allow-Origin: https://evil.example.com
Access-Control-Allow-Credentials: true

Step 3 — Inspect the refresh_token cookie attributes With browser DevTools (Application → Cookies), check for refresh_token:

  • Vulnerable: SameSite=None, Secure flag set
  • Fixed: SameSite=Strict or SameSite=Lax

Step 4 — Verify CSRF protection on /api/v1/auth/refresh

curl -X POST http://<langflow-host>/api/v1/auth/refresh \
  -H "Origin: https://evil.example.com" \
  --cookie "refresh_token=<captured_valid_token>"

A vulnerable instance returns a new access_token in the response body with HTTP 200.

Automated Scanning

Tool: Nuclei (ProjectDiscovery)

# Pull latest community templates (includes CVE-2025-34291 once updated)
nuclei -u http://<langflow-host> -tags langflow,cors

# Manual CORS check template
nuclei -u http://<langflow-host> -t ~/nuclei-templates/misconfiguration/cors-misconfiguration.yaml

Expected vulnerable output:

[cors-misconfiguration] [http] [medium] http://<langflow-host>/api/v1/auth/refresh

Tool: OWASP ZAP — Active Scan

  1. Proxy an authenticated Langflow session through ZAP.
  2. Active Scan → enable Cross-Origin Resource Sharing and CSRF scan rules.
  3. Look for alerts on /api/v1/auth/refresh with severity High or Critical.

Tool: Burp Suite Professional

  1. Capture a POST to /api/v1/auth/refresh in Proxy history.
  2. Send to Repeater, change Origin header to https://attacker.example.com.
  3. Check whether response reflects the injected origin in Access-Control-Allow-Origin.

Code Review Checklist

  • Confirm CORS_ALLOW_ALL_ORIGINS is False in backend/langflow/main.py or config
  • Verify ACCESS_CONTROL_ALLOW_ORIGIN does not echo back arbitrary origins
  • Confirm refresh_token cookie uses SameSite=Strict or SameSite=Lax
  • Check /api/v1/auth/refresh requires a CSRF token or same-site enforcement
  • Confirm /api/v1/run/ and code-execution endpoints validate authenticated sessions post-patch
  • Review .env / deployment config for LANGFLOW_CORS_ALLOW_ORIGINS setting

4. How to Fix It (Mitigation)

Step-by-Step Remediation

Option A — Upgrade (Strongly Recommended)

  1. Review the changelog for v1.9.3:

    https://github.com/langflow-ai/langflow/releases/tag/v1.9.3
    
  2. Upgrade via pip:

    pip install --upgrade langflow==1.9.3
    # or for production
    pip install langflow==1.9.3 --break-system-packages
    
  3. Upgrade via Docker:

    docker pull langflowai/langflow:1.9.3
    docker stop <old_container>
    docker run -d \
      --name langflow \
      -p 7860:7860 \
      -v langflow_data:/app/langflow \
      langflowai/langflow:1.9.3
    
  4. Upgrade via docker-compose:

    # docker-compose.yml
    services:
      langflow:
        image: langflowai/langflow:1.9.3   # was: latest or <1.9.3
    
    docker-compose pull && docker-compose up -d
    
  5. Restart and verify:

    curl -s http://localhost:7860/api/v1/version
    # Should return {"version":"1.9.3",...}
    

Option B — Temporary Workarounds (if immediate upgrade is not possible)

These reduce risk but do not fully mitigate the vulnerability — upgrade as soon as possible.

  • Restrict CORS to trusted origins in your reverse proxy (nginx/Apache):

    # nginx — allow only your internal domain
    add_header Access-Control-Allow-Origin "https://langflow.yourdomain.com" always;
    add_header Access-Control-Allow-Credentials "true" always;
    
  • Network-level isolation: Place Langflow behind a VPN or firewall so it is inaccessible from the public internet and from browser contexts that could reach attacker pages.

  • Invalidate all active sessions: Force-rotate the SECRET_KEY environment variable to invalidate all existing refresh tokens:

    # Regenerate a strong secret
    python -c "import secrets; print(secrets.token_hex(32))"
    # Update .env: SECRET_KEY=<new_value>
    # Restart Langflow
    

Code Fix Example

The upstream fix in v1.9.3 addresses all three weaknesses:

Before (vulnerable):

# backend/langflow/main.py (simplified)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],          # ← accepts any origin
    allow_credentials=True,        # ← combined with above = critical flaw
    allow_methods=["*"],
    allow_headers=["*"],
)

After (fixed):

# backend/langflow/main.py (v1.9.3)
origins = settings.CORS_ALLOW_ORIGINS or []   # explicit allowlist only

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,         # ← explicit allowlist
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Refresh token cookie (fixed):

# Before: SameSite=None (cross-site deliverable)
response.set_cookie("refresh_token", token, samesite="none", secure=True)

# After: SameSite=Lax (cross-site blocking)
response.set_cookie("refresh_token", token, samesite="lax", secure=True, httponly=True)

Configuration Hardening

Add to your .env or langflow.env:

# Restrict CORS to known origins (space-separated list)
LANGFLOW_CORS_ALLOW_ORIGINS=https://langflow.yourdomain.com

# Disable public API if not needed
LANGFLOW_AUTO_SERVING_ENDPOINT=false

# Enforce login for all API access
LANGFLOW_AUTO_LOGIN=false
LANGFLOW_NEW_USER_IS_ACTIVE=false

5. How to Test the Fix (Validation)

Regression Test Scenarios

  • Scenario A: Upgrade applied — verify CORS headers no longer reflect arbitrary origins
  • Scenario B: Verify token refresh fails from attacker-controlled origin
  • Scenario C: Verify application functions normally for legitimate same-origin users
  • Scenario D: Verify existing API keys and flows are intact after upgrade

Security Test Cases

Test Case 1: CORS Origin Reflection No Longer Present

  • Precondition: Langflow v1.9.3 installed and running
  • Steps:
    curl -s -I \
      -H "Origin: https://evil.attacker.com" \
      http://localhost:7860/api/v1/auth/refresh
    
  • Expected Result: Response does NOT contain Access-Control-Allow-Origin: https://evil.attacker.com; instead returns only the configured allowed origin or omits the header entirely

Test Case 2: Cross-Origin Token Theft Blocked

  • Precondition: Valid refresh_token cookie in hand
  • Steps: From a different origin (e.g., browser with evil.html), send a credentialed fetch to /api/v1/auth/refresh
  • Expected Result: Browser enforces same-origin policy and blocks the response; no access token is returned to the cross-origin page

Test Case 3: SameSite=Lax Cookie Not Sent Cross-Site

  • Precondition: Authenticated session with Langflow v1.9.3
  • Steps: Visit a cross-site page that initiates a top-level navigation POST to /api/v1/auth/refresh
  • Expected Result: SameSite=Lax prevents the cookie from being sent on cross-site non-navigation requests; CSRF attack fails

Test Case 4: Normal User Login Still Works

  • Precondition: Langflow v1.9.3 with correct LANGFLOW_CORS_ALLOW_ORIGINS
  • Steps: Log into Langflow from the legitimate domain, create/run a flow
  • Expected Result: Application functions normally; no broken authentication or CORS errors in browser console

Automated Tests

# test_cve_2025_34291_remediation.py
import requests

LANGFLOW_URL = "http://localhost:7860"
ATTACKER_ORIGIN = "https://evil.attacker.com"

def test_cors_no_wildcard_or_reflection():
    """CORS must not reflect arbitrary origins."""
    response = requests.options(
        f"{LANGFLOW_URL}/api/v1/auth/refresh",
        headers={
            "Origin": ATTACKER_ORIGIN,
            "Access-Control-Request-Method": "POST",
        }
    )
    acao = response.headers.get("Access-Control-Allow-Origin", "")
    assert acao != ATTACKER_ORIGIN, "FAIL: Server reflects attacker origin — still vulnerable!"
    assert acao != "*", "FAIL: Wildcard CORS still present — still vulnerable!"
    print(f"PASS: ACAO header = '{acao}' — does not reflect attacker origin")

def test_refresh_without_csrf_fails():
    """POST to /auth/refresh from foreign origin should not succeed."""
    response = requests.post(
        f"{LANGFLOW_URL}/api/v1/auth/refresh",
        headers={"Origin": ATTACKER_ORIGIN},
        cookies={"refresh_token": "fake_token"},
    )
    # Should be 401/403/422, not 200 with a token
    assert response.status_code != 200, \
        f"FAIL: Got HTTP {response.status_code} — cross-origin refresh succeeded!"
    print(f"PASS: Cross-origin refresh returned HTTP {response.status_code}")

if __name__ == "__main__":
    test_cors_no_wildcard_or_reflection()
    test_refresh_without_csrf_fails()
    print("All remediation tests passed.")

6. Prevention & Hardening

Best Practices

  • Pin CORS allowlists, never use wildcards with credentials. A CORS policy of allow_origins=["*"] combined with allow_credentials=True is always misconfigured — browsers block it in theory, but Langflow's bug bypassed the browser restriction at the server level.

  • Use SameSite=Strict or Lax for all session and refresh token cookies. SameSite=None should only be used for intentionally cross-site resources (e.g., embedded widgets), never for authentication cookies.

  • Isolate AI orchestration platforms from public internet access. Tools like Langflow, n8n, and Flowise are high-value targets because they store credentials for multiple downstream services. Put them behind VPN + SSO, and audit access logs regularly.

  • Rotate secrets and API keys stored in Langflow after any suspected compromise. Because the credential store is the primary prize, assume all stored secrets are compromised if an unpatched instance was reachable.

  • Subscribe to Langflow's GitHub security advisories at https://github.com/langflow-ai/langflow/security/advisories to receive patches before they are weaponised.

  • Apply the principle of least privilege to Langflow's service account. The OS user or container running Langflow should have minimal filesystem and network permissions to limit blast radius from RCE.

Monitoring & Detection

Watch for these indicators of compromise (IoCs) in your logs:

# Nginx/access logs — unusual cross-origin refresh requests
grep "POST /api/v1/auth/refresh" /var/log/nginx/access.log \
  | grep -v "Referer: https://langflow.yourdomain.com"

# Langflow application logs — unexpected flow executions
grep "flow_run" /var/log/langflow/app.log \
  | grep -v "<known_user_ids>"

# Unusual API key usage spikes in downstream services
# (OpenAI, AWS CloudTrail, etc.) correlated with Langflow login times

SIEM / Alerting rules to add:

  • Alert on POST to /api/v1/auth/refresh with Origin header not matching your approved domain
  • Alert on /api/v1/run/ calls from unusual IP ranges or at unusual hours
  • Alert on secrets vault access (cloud provider KMS / Secrets Manager) initiated from the Langflow host outside normal working patterns

References

Latest from the blog

See all →