Unauthenticated Remote Code Execution in Langflow via Public Flow Build Endpoint
Critical9.8CVE-2026-33017 · Published Mar 17, 2026 · updated Jun 29, 2026
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| langflow PyPI | < 1.9.0 | 1.9.0 |
Details and references
## 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 ```python @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: 1. `start_flow_build(data=attacker_data)` → `generate_flow_events()` -- `build.py:81` 2. `create_graph()` → `build_graph_from_data(payload=data.model_dump())` -- `build.py:298` 3. `Graph.from_payload(payload)` parses attacker nodes -- `base.py:1168` 4. `add_nodes_and_edges()` → `initialize()` → `_build_graph()` -- `base.py:270,527` 5. `_instantiate_components_in_vertices()` iterates nodes -- `base.py:1323` 6. `vertex.instantiate_component()` → `instantiate_class(vertex)` -- `loading.py:28` 7. `code = custom_params.pop("code")` extracts attacker code -- `loading.py:43` 8. `eval_custom_component_code(code)` → `create_class(code, class_name)` -- `eval.py:9` 9. `prepare_global_scope(module)` -- `validate.py:323` 10. `exec(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 ```python 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 EXECUTION ``` **Critical 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 1. Target Langflow instance has at least **one public flow** (common for demos, chatbots, shared workflows) 2. Attacker knows the public flow's UUID (discoverable via shared links/URLs) 3. No authentication required -- only a `client_id` cookie (any arbitrary string value) When `AUTO_LOGIN=true` (the **default**), all prerequisites can be met by an unauthenticated attacker: 1. `GET /api/v1/auto_login` → obtain superuser token 2. `POST /api/v1/flows/` →
- CVSS 3.1
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-306, CWE-94, CWE-95
- Also known as
- CVE-2026-33017, PYSEC-2026-379
- github.com/langflow-ai/langflow/security/advisories/GHSA-vwmf-pq79-vjvx
- nvd.nist.gov/vuln/detail/CVE-2026-33017
- github.com/langflow-ai/langflow/issues/12345
- github.com/langflow-ai/langflow/pull/12160
- github.com/langflow-ai/langflow/commit/73b6612e3ef25fdae0a752d75b0fabd47328d4f0
- github.com/advisories/GHSA-rvqx-wpfh-mfx7
- github.com/langflow-ai/langflow
- github.com/langflow-ai/langflow/releases/tag/1.8.2
- medium.com/@aviral23/cve-2026-33017-how-i-found-an-unauthenticated-rce-in-langflow-by-reading-the-code-they-already-dc96cdce5896
- www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2025-33017
- www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-33017
- www.sysdig.com/blog/cve-2026-33017-how-attackers-compromised-langflow-ai-pipelines-in-20-hours
More Langflow advisories
All Langflow| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Mar 18 | Langflow is Missing Ownership Verification in API Key Deletion (IDOR) CVE-2026-33053Highfixed in 1.9.0 | High | 1.9.0 |
| Mar 19 | Langflow has an Arbitrary File Write (RCE) via v2 API CVE-2026-33309Critical9.9fixed in 1.9.0 | Critical9.9 | 1.9.0 |
| Mar 20 | langflow has Unauthenticated IDOR on Image Downloads CVE-2026-33484High7.5fixed in 1.9.0 | High7.5 | 1.9.0 |
| Mar 20 | langflow: /profile_pictures/{folder_name}/{file_name} endpoint file reading CVE-2026-33497Highfixed in 1.7.1 | High | 1.7.1 |
| Mar 26 | Langflow has Authenticated Code Execution in Agentic Assistant Validation CVE-2026-33873Criticalfixed in 1.9.0 | Critical | 1.9.0 |
| Mar 27 | Langflow: Authenticated Users Can Read, Modify, and Delete Any Flow via Missing Ownership Check CVE-2026-34046Highfixed in 1.5.1 | High | 1.5.1 |