You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[+] PASS control: Host attacker.example is refused ← 对照组:围栏对恶意 Host 生效
[+] PASS control: mismatched Origin is refused
[+] PASS anonymous non-browser request (loopback Host, absent Origin) passes the real fence unconditionally
[+] PASS anonymous caller lists an arbitrary host directory through the gate
[+] PASS anonymous caller creates a directory on the host through the gate
[+] PASS anonymous session.create triggers recursive mkdir of attacker-chosen cwd
RESULT: real fence logic accepts the anonymous local process; no token, nonce, or auth exists on the channel
(另有对真实 dsh --profile web 的端到端 PoC poc-2/poc.mjs,刻意不调用 session.prompt 以避免消耗真实 API key。)
危害
终端服务器 / 共享开发机上,任何其他本地账户或无特权恶意软件都能到达 127.0.0.1:PORT(dsh --profile web 绑定回环;startup.ts:69-71 已拒绝 --host 0.0.0.0,说明威胁模型本应包含本地主体):
以受害者身份匿名执行任意 shell(session.prompt + standard 预设的 tool-bash/tool-pwsh);
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
摘要
node:vm,绕过会话沙箱/权限预设,无沙箱执行宿主机代码packages/workflow/workflow-worker-thread/src/runtime.ts:90-113AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:Hpackages/extensions/cordis-host-runner/src/sandbox.ts:129-145、src/index.ts:270-275AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H/apiRPC 桥仅有 Host 头围栏、无任何令牌 → 任何本地进程可匿名驱动带 shell 能力的会话packages/client/connection/src/api-request-trust.ts:96-123AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)三个漏洞均附零依赖、非破坏性的可运行 PoC(只读写自建金丝雀文件,不触碰真实凭据)。
漏洞一(DSH-01,CVSS 8.8):Workflow 脚本逃逸
node:vm位置
packages/workflow/workflow-worker-thread/src/runtime.ts:90-113(逃逸点)packages/workflow/workflow-worker-thread/src/host.ts:148-149(worker 裸派生,无沙箱包裹)packages/workflow/tool-workflow/src/index.ts:150, 217-334(无审批门 + 误导性工具描述)机理
面向模型的
workflow工具把模型编写的脚本体交给 worker 线程,后者用new vm.Script(...)编译并在vm.createContext({})中运行。上下文中仅有的全局量agent、parallel、pipeline、phase、log都是 worker 域的宿主函数,仅经Object.freeze(runtime.ts:112)。冻结并不能隐藏.constructor,因此在"沙箱"内部:该 worker 以
new Worker(entry, options)裸派生(host.ts:148-149),没有经过dsh-sandbox-local(bwrap/Landlock/Seatbelt)包裹,仅清洗了环境变量。同时workflow工具不在任何审批瀑布之下,其工具描述还向模型声称"不提供文件系统、网络、定时器或 Node.js API"(index.ts:150)。PoC
security-audit/poc-0/escape-demo.mjs(逐字复刻 runtime.ts 的引擎机制,非破坏性):核心载荷(即被注入的模型会提交给
workflow工具的脚本体):预期输出:
PoC RESULT: ESCAPE CONFIRMED,并打印:read-only/workspace-write预设下本不可能);process对象的 pid / uid / Node 版本。危害
用户将 会话固定在
read-only/workspace-write等限制性预设下时,所有 shell/fs 工具调用(包括委派子代理)都被 OS 级限制在工作区内;而workflow工具是唯一旁路:~/.ssh、~/.claude凭据、.env、其他 会话数据;可写用户可写的任意路径;拥有不受限的网络访问;保留意见(如实说明):
.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md已记录"脚本可以逃逸到具有进程级权限的 Node API"并推迟沙箱化;但已发布的基础 bundle(packages/bundle/base/cordis.patch.yml)把该引擎与承诺 OS 级隔离的沙箱提供者、权限预设打包在同一 profile 中,因此这构成现实的产品边界突破,而非纯理论问题。修复建议
dsh-sandbox-local包裹或专用受限 worker 入口);workflow工具置于审批瀑布之下;漏洞二(DSH-02,CVSS 8.8):动态插件宿主代码逃逸 vm,且零审批
位置
packages/extensions/cordis-host-runner/src/sandbox.ts:129-145(逃逸点)packages/extensions/cordis-host-runner/src/index.ts:270-275(审批门缺失)packages/extensions/tool-cordis/src/index.ts:148-238(面向模型的cordis_define,仅做编译级预检)机理
createSandbox把btoa/atob作为宿主域箭头函数(基于宿主Buffer的闭包,sandbox.ts:137-138)注入,同时注入宿主域的TextEncoder/TextDecoder/console。require/fetch/定时器都是抛异常的陷阱、process被隐藏 —— 但逃逸根本不碰这些被陷阱化的名字:该模块自己的文件头即承认:"is not containment: host-realm helper functions remain an escape route"(sandbox.ts:7)。
审批缺口: 当
definition.clientCode === undefined("仅宿主"包)时,DynamicCordisRunnerService.run()(index.ts:270-275)立即激活该包;唯一的审批机制(index.ts:277-303,发出cordis/request-run供用户面板确认)仅在存在 client 半边时才武装。也就是说,更危险的形态(仅宿主)反而审批更少。PoC
security-audit/poc-1/poc-vm-escape.mjs(独立运行,逐字复刻createSandbox/evaluateHostCode):另附针对真实服务的 vitest 测试
poc-1/runner-no-approval-escape.poc.spec.ts:核心载荷(即被注入的模型会作为
cordis_define的code.host参数提交的字符串):预期输出:
危害
攻击链完全由网络侧发起(AV:N):攻击者控制的仓库 README / 网页内容 → 提示词注入 → 模型调用
cordis_define(仅宿主)+cordis_run→ 载荷以 dsh 进程完整权限运行,零用户交互:node:fs/node:child_process全量访问;process.env(含DEEPSEEK_API_KEY)直接可读;packages/sandbox/sandbox-local)与约束其他所有工具的ctx.fs/ctx.web能力策略。tool-cordis已装配在可运行的发布组合中:examples/web-cordis/cordis.yml:18、examples/headless-agent/advanced.cordis.yml:33,可达性为现实而非假设。修复建议
btoa/atob;漏洞三(DSH-03,CVSS 7.4):
/apiRPC 桥无鉴权位置
packages/client/connection/src/api-request-trust.ts:96-123(围栏)packages/client/connection/src/index.ts:161-172(路由挂载)packages/host/apiproxy/src/api/rpc-map.ts(可达方法面)机理
/api路由唯一的门是isTrustedApiRequest:Host 头检查(回环地址即通过)加浏览器标记检查。关键在 api-request-trust.ts:117:非浏览器本地进程根本不发送
Origin/Sec-Fetch-*头,因此只要Host: 127.0.0.1:<端口>就无条件通过。整条通道上没有任何令牌、nonce 或 UID 校验。围栏自己的文档(api-request-trust.ts:12-13)也写着 "this fence is not an auth layer"。可达面(无需任何凭据)
host.listDirectory/host.createDirectory—— 以 dsh 用户身份任意枚举宿主机文件系统、创建目录;session.create(api-proxy.ts:2167)接受攻击者选定的cwd,ensureSession甚至会mkdir(cwd, { recursive: true })(api-proxy.ts:1665);session.prompt(api-proxy.ts:2461)→agent.followup在默认standard预设(apps/cli/config/agent-presets/standard/agent.cordis.yml:44-60)下驱动回合,该预设装配了tool-bash、tool-pwsh、tool-fs—— 匿名执行 shell 并消耗受害者的DEEPSEEK_API_KEY;settings.*、credentials.*等)同样可达 —— 攻击者本来就在回环上。PoC
security-audit/poc-2/poc-fence.mjs(零依赖,围栏决策逻辑逐字取自 api-request-trust.ts / loopback-hostname.ts):攻击请求本质就是:
预期输出(全部 PASS):
(另有对真实
dsh --profile web的端到端 PoCpoc-2/poc.mjs,刻意不调用session.prompt以避免消耗真实 API key。)危害
终端服务器 / 共享开发机上,任何其他本地账户或无特权恶意软件都能到达
127.0.0.1:PORT(dsh --profile web绑定回环;startup.ts:69-71 已拒绝--host 0.0.0.0,说明威胁模型本应包含本地主体):session.prompt+standard预设的tool-bash/tool-pwsh);DEEPSEEK_API_KEY并消耗其额度;settings.*、credentials.*等特权方法。单用户主机上权限差会塌缩(这正是代码注释将其视为已接受边界的原因),但多主体主机场景下为真实且完整的本地权限提升 + 数据窃取通道。
修复建议
/api路由进入bridge()之前校验该令牌;POC
poc.zip
Contribution
Found by glm
All reactions