智能体
dev.to ·
@GUIDANCE WHITE
CVE-2026-17633:Langflow OSS 经 /api/v1/custom_component 的认证后 RCE
Langflow OSS 1.0.0–1.10.3 存在经认证的远程代码执行漏洞 CVE-2026-17633,CVSS 8.5(高危),CWE-94。触发条件为任意已认证用户且 LANGFLOW_ALLOW_CUSTOM_COMPONENTS=true,攻击面为 POST /api/v1/custom_component 端点。文章引用 IBM 于 2026 年 8 月 5 日发布的安全公告,并给出源码级根因分析。Langflow 是用于可视化构建 LLM 应用与 Agent 工作流的开源低代码平台。
AI推荐理由 披露 Langflow OSS 1.0.0–1.10.3 中经认证的远程代码执行漏洞 CVE-2026-17633(CVSS 8.5),给出受影响端点、前置条件与源码级根因分析,并引用 IBM 安全公告。对使用 Langflow 构建 LLM 应用与 Agent 工作流的开发者有直接修复价值。
78
正文
Summary
Field
Value
CVE ID
CVE-2026-17633
CVSS
8.5 (HIGH)
CWE
CWE-94 (Improper Control of Generation of Code)
Affected
Langflow OSS 1.0.0 – 1.10.3
Preconditions
Any authenticated user + LANGFLOW_ALLOW_CUSTOM_COMPONENTS=true
Vulnerable endpoint
POST /api/v1/custom_component
Langflow is an open-source low-code platform for building LLM applications and agent workflows visually. One of its features, Custom Components, lets users define a component's behavior directly in Python. That feature is the attack surface for this vulnerability.
IBM's security advisory (published August 5, 2026) disclosed a cluster of issues in Langflow OSS 1.0.0–1.10.3. CVE-2026–17633 is the authenticated RCE reachable through /api/v1/custom_component.
Root Cause - Source-Level Analysis
1.1 The vulnerable endpoint
From langflow/api/v1/endpoints.py (around line 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:
…
# The only gate: "is the custom-component feature enabled at all?"
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() is never called here
component = Component(_code=effective_code)
built_frontend_node, component_instance = build_custom_component_template(component, user_id=user.id)
Enter fullscreen mode
Exit fullscreen mode
The important detail isn't that there's "no validation" - it's that the validation checks the wrong thing. allow_custom_components answers "is this user allowed to create custom components," not "is this code's content safe." In production, LANGFLOW_ALLOW_CUSTOM_COMPONENTS=true is a common setting, and once it's on, this check passes trivially and the code flows through with zero content inspection.
Langflow does ship a separate AST-based scanner, scan_code_security() (covered in section 5), but this endpoint's execution path never calls it.
1.2 The actual bug lives in prepare_global_scope()
custom_component() calls build_custom_component_template(), which flows into create_class() in lfx/custom/validate.py. That function calls prepare_global_scope(), and the submitted code is exec()'d shortly after.
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, "", "exec")
exec(compiled_code, exec_globals) # ← exec() happens here
Enter fullscreen mode
Exit fullscreen mode
This function walks the submitted code's AST and only collects import statements and class/def/assignment nodes into definitions. That's the trap.
A top-level statement like os.system(…) is, at the AST level, an ast.Expr node.
ast.Expr isn't in the isinstance allow-list above → it's silently dropped.
Code placed inside a class body, however, is part of that ClassDef node - so when the class is defined (i.e., when exec() runs), that code executes right along with it.
# ❌ Module level - classified as ast.Expr, silently dropped by prepare_global_scope()
import os
os.system("id > /tmp/pwned.txt")
class PocComponent(Component):
…
# ✅ Inside the class body - part of ClassDef, runs when exec() defines the class
class PocComponent(Component):
os.system("id > /tmp/pwned.txt") # ← executes at class-definition time
…
Enter fullscreen mode
Exit fullscreen mode
So the entire trick an attacker needs is: put the payload inside the class body, not at module level. No encoding tricks, no filter bypass gymnastics - just a simple, and simply devastating, design flaw.
Exploit Chain
The diagram below traces the full path from request to command execution.
In short:
Authenticated user (any privilege level)
│
▼
POST /api/v1/custom_component { "code": "" }
│
▼
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
Enter fullscreen mode
Exit fullscreen mode
No LLM involvement, no scanner to evade. One HTTP request is enough.
Why the Scanner Didn't Catch This
Langflow ships a separate AST-based scanner, scan_code_security(), in langflow/agentic/helpers/code_security.py. But it's wired only into the Agentic Assistant path (validating LLM-generated component code) - it's never called from /api/v1/custom_component at all.
So CVE-2026–17633 isn't really a scanner bypass; it's exploiting a code path the scanner was never attached to in the first place. The scanner's own detection logic has a separate weakness (missing vars() coverage leading to a false is_safe: True verdict, tracked as CVE-2026–17632), which surfaced from analyzing the same codebase but is a distinct issue.
Patch and Mitigation
Upgrade to Langflow 1.10.4+, which adds content validation to the custom_component endpoint
Keep LANGFLOW_ALLOW_CUSTOM_COMPONENTS set to false in production unless the feature is genuinely needed
If custom components must be enabled, restrict which accounts can reach that feature
Audit the group permissions of the container's runtime user (gid=0 membership) to reduce lateral-movement / container-escape surface