导语:Heym 工作流 RCE 漏洞(CVE-2026-67544)是近期自托管自动化平台中最值得警惕的高危缺陷之一——一个 freshly registered 的普通账号,仅凭往 Playwright 节点的 playwrightCode 字段塞入一段 Python 字符串,就能让后端以 root 身份执行任意代码。本文以架构师视角完整复盘这条从「字段注入」到「主机沦陷」的攻击链路,并给出可直接落地的应急与加固方案。
企业级架构规范
flowchart TD
subgraph 接入层 ["表现层与接入网关 (Client & Gateway Tier)"]
A["任意已认证用户 (含新注册账号)"] --> B["工作流编辑器 / API 提交"]
B --> C["Playwright 节点 playwrightCode 字段"]
end
subgraph 规则层 ["校验与编排层 (Validation & Orchestration Tier)"]
C --> D{"是否存在 AST 校验 / 沙箱 / 角色门禁?"}
D -->|"否 (0.0.71 之前)"| E["字符串直接落盘为临时 .py 文件"]
D -->|"是 (0.0.71 修复后)"| F["拒绝执行并记录审计日志"]
end
subgraph 执行层 ["后端执行引擎 (Execution Tier)"]
E --> G["subprocess.Popen([sys.executable, script_path])"]
G --> H["以服务进程用户身份执行 (默认 Docker 为 root)"]
end
subgraph 影响层 ["主机与数据面 (Impact Tier)"]
H --> I["任意命令执行 / 凭据窃取"]
H --> J["横向移动 / 持久化后门"]
H --> K["业务数据与密钥泄露"]
end
一、漏洞速览:Heym 工作流 RCE 漏洞的核心事实
CVE-2026-67544 的官方描述非常克制,但信息量极大:Heym 在自托管基础设施上构建并运行 agentic systems 与业务流程自动化,其工作流引擎接受 Playwright 节点 playwrightCode 字段中的任意 Python 代码字符串,将其写入临时 .py 文件后,通过 subprocess.Popen([sys.executable, script_path]) 直接执行,全程没有沙箱、没有 AST 校验、没有角色门禁。
| 评估维度 | 详情 | 架构影响 |
|---|---|---|
| CVE 编号 | CVE-2026-67544 | 需纳入资产漏洞台账 |
| 严重等级 | High · CVSS 8.8 | 高优先级修复 |
| 受影响版本 | < 0.0.71 | 默认 Docker 部署风险最高 |
| 攻击前提 | 任意已认证账号(含新注册) | 认证边界形同虚设 |
| 执行身份 | 服务进程用户,默认 Docker 为 root | 主机完全沦陷 |
| 修复版本 | 0.0.71 | 升级即阻断主链路 |
CVSS 8.8 意味着这是「可远程利用、低复杂度、高机密性与完整性影响」的典型组合。更危险的是攻击门槛极低——不需要管理员权限,不需要社工,注册一个账号即可。任何暴露在公网、允许自助注册的 Heym 实例都应视为已被入侵的高危候选。
二、攻击链路拆解:从字段注入到 root 执行
2.1 第一步:认证边界被「新注册账号」击穿
很多团队在评估 RCE 漏洞时会下意识认为「需要管理员权限」,从而降低优先级。CVE-2026-67544 恰恰相反:官方明确指出 Any authenticated user — including a freshly registered account。这意味着只要实例开放注册,攻击者从零到 RCE 的路径只有「注册 → 创建工作流 → 添加 Playwright 节点 → 填入 payload → 运行」五步。
2.2 第二步:playwrightCode 字段的信任错位
Playwright 节点的设计初衷是让用户自定义浏览器自动化脚本,因此 playwrightCode 天然被当作「用户可控的代码内容」。问题在于,工作流引擎把这个字段当成了「可信代码」而非「不可信输入」——它没有区分「配置数据」与「可执行代码」的边界,直接把字符串落盘为 .py 文件。
2.3 第三步:subprocess.Popen 的裸执行
真正致命的是执行方式:subprocess.Popen([sys.executable, script_path])。这行代码等价于「用当前 Python 解释器运行一个用户完全可控的脚本」,且继承父进程的全部权限与环境变量。在默认 Docker 部署中,服务进程以 root 运行,于是攻击者拿到的就是一个 root shell 的等价能力。
# 漏洞版本中的执行逻辑示意(非官方源码,仅用于说明攻击面)
import subprocess
import sys
import tempfile
def run_playwright_node(playwright_code: str):
# 1. 用户可控字符串直接落盘
with tempfile.NamedTemporaryFile(suffix=".py", delete=False, mode="w") as f:
f.write(playwright_code)
script_path = f.name
# 2. 无沙箱、无 AST 校验、无角色门禁
# 3. 以服务进程身份(默认 Docker 为 root)执行
proc = subprocess.Popen([sys.executable, script_path])
proc.wait()
不要以为「内网部署就安全」。自托管自动化平台往往持有大量业务系统凭据(数据库密码、API Token、SSH Key),一旦 RCE 成功,攻击者可以立即读取环境变量与配置文件,完成横向移动。内网不是安全边界,只是延迟被发现的时间。
三、根因分析:为什么这类漏洞在 Agent 平台反复出现
从架构视角看,Heym 工作流 RCE 漏洞不是孤例,而是「低代码/Agent 平台」这一类系统的结构性风险。它们天生需要执行用户定义的逻辑,而「执行用户逻辑」与「执行任意代码」之间只隔着一层薄薄的抽象。
- 信任边界模糊:把「工作流配置」与「可执行代码」混在同一个数据模型里,字段级权限缺失。
- 执行模型过于直接:用
subprocess起独立解释器,绕过了应用层所有拦截器与审计钩子。 - 默认配置不安全:Docker 默认以 root 运行,放大了任何代码执行缺陷的后果。
- 缺少纵深防御:没有 AST 白名单、没有 seccomp/AppArmor、没有网络出口限制,单点失守即全线失守。
对于任何「允许用户提交可执行逻辑」的平台,必须默认假设「用户输入 = 恶意代码」。正确的架构不是「校验字符串」,而是「把执行放进不可信域」:独立容器、只读文件系统、无网络、非 root 用户、资源配额、执行超时。校验只能降低概率,隔离才能兜住底线。
四、应急响应:四步完成 Heym 工作流 RCE 漏洞处置
4.1 第一步:资产盘点与暴露面收敛
立即梳理所有 Heym 实例,确认版本号是否低于 0.0.71。对于公网暴露且开放注册的实例,优先下线或加访问控制,再谈修复。
4.2 第二步:升级到 0.0.71 并验证
官方已在 0.0.71 中修复该问题。升级后务必回归验证 Playwright 节点的正常功能,确认修复没有破坏合法工作流。
点击展开:升级命令与修复后验证脚本
# 1. 备份当前配置与数据卷 docker compose down tar -czf heym-backup-$(date +%F).tar.gz ./data ./config # 2. 拉取并升级到 0.0.71 docker compose pull docker compose up -d # 3. 确认版本 docker compose exec heym heym --version # 4. 验证 Playwright 节点功能正常(使用合法脚本) # 在 UI 中创建一个最小工作流,确认执行成功且无异常日志 docker compose logs --tail=100 heym | grep -i "playwright"
4.3 第三步:入侵痕迹排查
如果实例在修复前长期暴露,必须假设可能已被利用。重点排查临时目录中的可疑 .py 文件、异常子进程、非预期的网络连接与新增的持久化项。
# 查找近期创建的临时 Python 脚本 find /tmp /var/tmp -name "*.py" -mtime -30 -ls 2>/dev/null # 检查异常子进程与网络连接 ps auxf | grep -E "python|playwright" | grep -v grep ss -tunap | grep -E "ESTABLISHED|LISTEN" # 检查容器内新增用户与计划任务 getent passwd | tail -20 crontab -l 2>/dev/null; ls -la /etc/cron.* 2>/dev/null # 检查环境变量是否被外带(重点看是否有异常外联) docker compose exec heym env | grep -iE "token|secret|password|key"
4.4 第四步:凭据轮换与加固
一旦确认或高度怀疑被利用,立即轮换 Heym 实例可访问的所有凭据:数据库密码、API Token、SSH Key、云厂商 AK/SK。凭据轮换的优先级高于日志分析,因为攻击者可能已经完成数据外带。
不要在受感染的宿主机上直接做取证分析后再重建——攻击者可能已植入后门。正确顺序是:隔离网络 → 快照取证 → 重建实例 → 轮换凭据 → 复盘。任何在「可能已被 root 控制」的机器上继续运行业务的行为都是不可接受的。
五、纵深防御:让 Heym 工作流 RCE 类漏洞不再致命
补丁解决的是「这一个」漏洞,架构解决的是「这一类」漏洞。以下加固措施建议作为自托管 Agent 平台的标准基线。
| 防御层级 | 传统做法 | 推荐加固方案 | 架构收益 |
|---|---|---|---|
| 进程权限 | Docker 默认 root 运行 | 非 root 用户 + 只读根文件系统 | 爆炸半径收敛 |
| 执行隔离 | subprocess 直接执行 | 独立容器 / gVisor / Firecracker 微虚机 | 内核级隔离 |
| 代码校验 | 无校验 | AST 白名单 + 禁用危险模块导入 | 降低利用概率 |
| 网络出口 | 默认全通 | 执行域无外网 + 出站白名单 | 阻断数据外带 |
| 角色门禁 | 任意认证用户可用 | Playwright 节点仅管理员可用 + 审计 | 最小权限原则 |
| 资源限制 | 无限制 | CPU/内存配额 + 执行超时 | 防资源耗尽 |
5.1 最小权限容器配置示例
即使暂时无法引入微虚机隔离,也应先把容器权限压到最低。以下 compose 片段可直接作为基线参考。
services:
heym:
image: heym:0.0.71
user: "10001:10001" # 非 root 运行
read_only: true # 只读根文件系统
tmpfs:
- /tmp:size=64m,noexec,nosuid # 临时目录禁止执行
cap_drop:
- ALL # 丢弃全部 Linux capabilities
security_opt:
- no-new-privileges:true
networks:
- internal_only # 仅内网,无默认外网出口
deploy:
resources:
limits:
cpus: "2"
memory: 2g
networks:
internal_only:
internal: true
noexec 挂载 /tmp 能阻断「落盘即执行」的经典路径,但攻击者仍可通过 python -c 或内存加载绕过。因此挂载选项只是纵深防御的一层,不能替代真正的执行隔离。不要把「加了 noexec」当成「已经安全」。
5.2 代码级校验的正确姿势
如果业务确实需要允许用户提交受限脚本,AST 校验可以作为第一道过滤网,但必须明确它的定位:降低误用与低阶攻击,不承担对抗高级攻击者的职责。
import ast
FORBIDDEN_MODULES = {"os", "sys", "subprocess", "socket", "shutil", "ctypes", "importlib"}
FORBIDDEN_CALLS = {"eval", "exec", "compile", "__import__", "open"}
class SafetyVisitor(ast.NodeVisitor):
def visit_Import(self, node):
for alias in node.names:
if alias.name.split(".")[0] in FORBIDDEN_MODULES:
raise ValueError(f"禁止导入模块: {alias.name}")
self.generic_visit(node)
def visit_ImportFrom(self, node):
if node.module and node.module.split(".")[0] in FORBIDDEN_MODULES:
raise ValueError(f"禁止导入模块: {node.module}")
self.generic_visit(node)
def visit_Call(self, node):
if isinstance(node.func, ast.Name) and node.func.id in FORBIDDEN_CALLS:
raise ValueError(f"禁止调用: {node.func.id}")
self.generic_visit(node)
def validate_user_code(code: str) -> None:
tree = ast.parse(code, mode="exec")
SafetyVisitor().visit(tree)
AST 白名单必须与「执行隔离」组合使用,且校验逻辑应放在独立进程或独立服务中,避免校验器本身被绕过。更重要的是:校验失败必须记录完整审计日志(用户、工作流 ID、原始代码哈希),这是事后溯源的关键证据。
六、总结:从 Heym 工作流 RCE 漏洞看 Agent 平台的安全基线
CVE-2026-67544 给所有自托管 Agent 与低代码自动化平台敲响了警钟:当平台的核心能力就是「执行用户定义的逻辑」时,安全边界不能建立在「用户不会写恶意代码」的假设上。Heym 工作流 RCE 漏洞的本质,是把「不可信输入」当成了「可信代码」,并用最直接的方式执行它。
对架构师而言,可复用的结论有三条:第一,任何代码执行能力都必须运行在独立、非 root、无外网、有配额的不可信域中;第二,字段级权限与角色门禁要在数据模型层就设计好,而不是事后补;第三,默认配置必须安全,因为绝大多数生产事故发生在「没人改过默认值」的实例上。补丁会过时,基线不会。
