Langflow has Authenticated Cross-Project File Disclosure via Unscoped MCP Resource Handlers
Summary
Langflow's project-scoped MCP transport authenticates the caller for the project_id in the connection URL, but the subsequent resources/read operation does not authorize the resource URI being requested. An authenticated user can connect to their own project-scoped MCP endpoint and supply a crafted file-download URI that points to another user's flow-backed file. The server then reads and returns the victim file without verifying ownership or project membership.
This is a cross-user authorization bypass (userA -> userB) that allows arbitrary read access to files stored under other users' flow namespaces.
Details
Verified against local checkout:
- Repository:
langflow-ai/langflow - Verified on release tag:
v1.8.3 - Commit:
08bf98404cfd7737fde57a2588785766cdf1b42e - Earliest stable release known to contain the vulnerable code path:
v1.6.8
Relevant code path:
1. src/backend/base/langflow/api/v1/mcp_projects.py:147-193
verify_project_auth_conditional() authenticates the caller and checks access only to the project_id in the MCP transport URL.
2. src/backend/base/langflow/api/v1/mcp_projects.py:1238-1241
The project-scoped MCP server registers read_resource() and forwards the attacker-controlled URI directly to handle_read_resource(uri=uri).
3. src/backend/base/langflow/api/v1/mcp_utils.py:163-182
handle_read_resource()...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| langflow PyPI | >= 1.6.8, < 1.9.1 | 1.9.1 |
Details and references
### Summary Langflow's project-scoped MCP transport authenticates the caller for the `project_id` in the connection URL, but the subsequent `resources/read` operation does not authorize the resource URI being requested. An authenticated user can connect to their own project-scoped MCP endpoint and supply a crafted file-download URI that points to another user's flow-backed file. The server then reads and returns the victim file without verifying ownership or project membership. This is a cross-user authorization bypass (`userA -> userB`) that allows arbitrary read access to files stored under other users' flow namespaces. ### Details Verified against local checkout: - Repository: `langflow-ai/langflow` - Verified on release tag: `v1.8.3` - Commit: `08bf98404cfd7737fde57a2588785766cdf1b42e` - Earliest stable release known to contain the vulnerable code path: `v1.6.8` Relevant code path: 1. `src/backend/base/langflow/api/v1/mcp_projects.py:147-193` `verify_project_auth_conditional()` authenticates the caller and checks access only to the `project_id` in the MCP transport URL. 2. `src/backend/base/langflow/api/v1/mcp_projects.py:1238-1241` The project-scoped MCP server registers `read_resource()` and forwards the attacker-controlled URI directly to `handle_read_resource(uri=uri)`. 3. `src/backend/base/langflow/api/v1/mcp_utils.py:163-182` `handle_read_resource()` parses the last two URI path segments as `flow_id` and `filename`, then directly calls: ```python storage_service.get_file(flow_id=flow_id, file_name=filename) ``` No check ties the supplied `flow_id` back to the authenticated user or the current project. 4. `src/backend/base/langflow/services/storage/local.py:141-149` `src/backend/base/langflow/services/storage/s3.py:200-217` The storage layer performs a raw namespace read and is not authorization-aware. The result is that authorization is enforced at MCP connection time, but not at resource-read time. Once an attacker has access to any project-scoped MCP endpoint they own, they can read another user's flow-backed file by passing a victim-controlled URI. This issue is also easier to exploit because the global MCP helpers expose cross-user discovery data: - `src/backend/base/langflow/api/v1/mcp_utils.py:102-121` `handle_list_resources(project_id=None)` lists files for all flows. - `src/backend/base/langflow/api/v1/mcp_utils.py:333-380` `handle_list_tools(project_id=None)` queries all flows and includes flow IDs in tool metadata. That global enumeration is not required for exploitation, but it makes obtaining victim identifiers much easier. ### PoC Preconditions: - Langflow is running locally with authentication enabled. - Two separate users exist on the same instance. - The victim has a flow with an uploaded file. - The attacker has access to any project they own. Verify the issue locally by starting Langflow with: ```bash cd '/Users/r1zzg0d/Documents/CVE hunting/targets/langflow' set -a source .env.verify set +a export LANGFLOW_CONFIG_DIR="$PWD/.langflow-verify" uv run python - <<'PY' from langflow.main import setup_app import uvicorn app = setup_app(backend_only=True) uvicorn.run(app, host="127.0.0.1", port=7860, log_level="debug") PY ``` Then, in a second terminal, the following script creates a victim user, an attacker user, a victim flow-backed file, and demonstrates that: - the normal file download route is blocked for the attacker (`404`) - the same file is readable through the attacker's own project-scoped MCP connection ```bash cd '/Users/r1zzg0d/Documents/CVE hunting/targets/langflow' set -euo pipefail export BASE='http://127.0.0.1:7860' export PASS='Passw0rd!Passw0rd!' export VICTIM_USER="victim.$RANDOM@example.com" export ATTACKER_USER="attacker.$RANDOM@example.com" curl -sS -X POST "$BASE/api/v1/users/" \ -H 'Content-Type: application/json' \ -d "{\"username\":\"$VICTIM_USER\",\"password\":\"$PASS\"}" >/dev/null curl -sS -X POST "$BASE/api/v1/use
- CVSS 4.0
- CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-639
- Also known as
- CVE-2026-105699
- github.com/langflow-ai/langflow/security/advisories/GHSA-4hmc-cfm3-w43c
- nvd.nist.gov/vuln/detail/CVE-2026-105699
- github.com/langflow-ai/langflow/pull/12818
- github.com/langflow-ai/langflow/commit/b8fe970493fd5fb2e1fc71dfccc79f90b76058fa
- github.com/langflow-ai/langflow/commit/f0fd436fe9829192ee550e6cb46961a01dd37032
- github.com/langflow-ai/langflow
- github.com/langflow-ai/langflow/releases/tag/v1.9.1
More Langflow advisories
All Langflow| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Oct 7 | Langflow: OS command injection (RCE) via arbitrary command in MCP stdio server configuration | Critical9.9 | 1.10.3 |
| Oct 7 | Langflow: insecure direct object reference | Medium5.4 | 1.10.1 |
| Oct 7 | Langflow: IP Spoofing Bypass via `X-Forwarded-For` Allowing Remote Configuration Write | High7.1 | 1.10.3 |
| Oct 6 | Langflow: server-side request forgery | Medium | 1.10.3 |
| Oct 6 | Langflow: remote code execution | Critical9.9 | 1.10.1 |
| Oct 5 | Langflow: Weak Fernet Key via random.seed() | Critical9.1 | 1.10.1 |