Langflow 1.0.0-1.10.3 在开启自定义组件时,认证用户可通过 POST /api/v1/custom_component 执行任意 Python 代码,CVSS 8.5。
Langflow 是一个开源的低代码平台,用于可视化的构建 LLM 应用和 Agent 工作流。其中的"自定义组件"(Custom Components)功能允许用户直接用 Python 定义组件的行为——而这正是本次漏洞的攻击面。IBM 于 2026 年 8 月 5 日发布的安全公告披露了 Langflow OSS 1.0.0–1.10.3 中的一组问题,CVE-2026–17633 即为其中可通过 /api/v1/custom_component 达成的认证后 RCE。
出自 langflow/api/v1/endpoints.py(约第 1271 行):
@router.post("/custom_component", status_code=HTTPStatus.OK, include_in_schema=False)
async def custom_component(
raw_code: CustomComponentRequest,
user: CurrentActiveUser,
request: Request,
) -> CustomComponentResponse:
…
# 唯一的关卡:"自定义组件功能是否全局启用?"
if not settings.allow_custom_components and not code_hash_matches_any_template(raw_code.code, all_known):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, …)
# scan_code_security() 在此处从未被调用
component = Component(_code=effective_code)
built_frontend_node, component_instance = build_custom_component_template(component, user_id=user.id)
关键细节并不在于"没有校验",而是校验检查了错误的东西。allow_custom_components 回答的是"该用户是否被允许创建自定义组件",而非"这段代码的内容是否安全"。在生产环境中,LANGFLOW_ALLOW_CUSTOM_COMPONENTS=true 是常见配置,一旦开启,这项检查轻易通过,代码便以零内容审查的姿态流过执行路径。Langflow 确实附带了一个独立的基于 AST 的扫描器 scan_code_security()(详见第 5 节),但该端点的执行路径从未调用过它。
prepare_global_scope() 中custom_component() 调用 build_custom_component_template(),进而流入 lfx/custom/validate.py 中的 create_class()。该函数调用 prepare_global_scope(),随后提交的代码会在不远处被执行。
def prepare_global_scope(module):
exec_globals = globals().copy()
…
for node in module.body:
if isinstance(node, ast.Import | ast.ImportFrom):
imports.append(node)
elif isinstance(node, ast.ClassDef | ast.FunctionDef | ast.Assign | ast.AnnAssign):
definitions.append(node)
…
if definitions:
compiled_code = compile(combined_module, "<string>", "exec")
exec(compiled_code, exec_globals) # ← exec() 在此发生
该函数遍历提交代码的 AST,仅将 import 语句以及 class/def/赋值语句节点收入 definitions 列表。这就是陷阱所在。
像 os.system(...) 这样的顶层语句,在 AST 层面是一个 ast.Expr 节点。
ast.Expr 不在上面的 isinstance 白名单中 → 被静默丢弃。
然而,放在 class body 中的代码是 ClassDef 节点的一部分——因此当这个 class 被定义时(即 exec() 执行时),其中的代码也随之执行。
# ❌ 模块顶层 — 被识别为 ast.Expr,被 prepare_global_scope() 静默丢弃
import os
os.system("id > /tmp/pwned.txt")
class PocComponent(Component):
…
# ✅ 位于 class body 内部 — 属于 ClassDef 的一部分,在 exec() 定义类时执行
class PocComponent(Component):
os.system("id > /tmp/pwned.txt") # ← 在类定义时执行
…
所以攻击者所需的全部伎俩就是:把 payload 放在 class body 内,而不是模块顶层。不需要编码 tricks,不需要绕过过滤器——只是一个简单的、堪称毁灭性的设计缺陷。
下图是从请求到命令执行的完整路径追踪。

Authenticated user (any privilege level)
│
▼
POST /api/v1/custom_component { "code": "<malicious Python class>" }
│
▼
build_custom_component_template() → create_class()
│
▼
prepare_global_scope() — only ClassDef nodes get collected into definitions/exec target
│
▼
compile_class_code() → exec(compiled_class, exec_globals)
│
▼
Class body executes at definition time → RCE achieved
无需 LLM 介入,无需规避扫描器。一条 HTTP 请求足矣。
Langflow 附带了一个独立的基于 AST 的扫描器 scan_code_security(),位于 langflow/agentic/helpers/code_security.py。但它仅接入在 Agentic Assistant 路径(验证 LLM 生成的组件代码)——从 /api/v1/custom_component 根本不会调用它。因此 CVE-2026–17633 实质上并非扫描器绕过,而是利用了一条扫描器从未附着过的代码路径。该扫描器自身的检测逻辑另有一个独立缺陷(缺少 vars() 覆盖导致误判 is_safe: True,编号为 CVE-2026–17632),这是在分析同一代码库时发现的,但属于不同的问题。
custom_component 端点新增了内容校验LANGFLOW_ALLOW_CUSTOM_COMPONENTS 保持为 false,除非确实需要该功能gid=0 成员身份),以降低横向移动 / 容器逃逸的攻击面