DeepSeek Harness(DSH)作为开源 Agent 运行时的明星项目,近期被安全研究者披露了四个高危漏洞,涵盖配置注入、沙箱逃逸、权限绕过等攻击面。本文将带你深入这些漏洞的技术细节、端到端攻击链,以及作为开发者和用户该如何应对。
一、从明星项目到安全焦点
DeepSeek Harness(简称 DSH)自开源以来迅速成为 AI Agent 领域的焦点。它基于 Cordis 插件化架构,主打"万物皆插件"------模型适配器、工具注册表、Session 日志、甚至 Agent 主循环本身都可以通过配置组合与替换。短短数周内,生态内就涌现出上千个插件,Star 数突破 15 万。
然而,就在生态高速扩张的同时,安全研究者对 DSH 进行了一次深度架构评审,结果令人警醒:在默认部署下,攻击者可以通过 Prompt Injection 实现从沙箱逃逸到宿主机 RCE 的完整攻击链。
官方也在最新版本(v0.1.2-rc.1 / v0.1.3-alpha.2)中更新了安全声明:
"DeepSeek Harness 尚未接受安全审计,沙箱、审批与权限控制不能保证隔离。"
这不是一句客套话,而是有实打实的 PoC 支撑。
二、威胁模型:我们防的是谁?
在深入漏洞之前,先明确 DSH 的威胁模型:
- 模型是半可信主体:它可能被 Prompt Injection(恶意网页、仓库内容、被 fetch 的文档)诱导
- read-only 模式的安全承诺:官方宣传该模式适合处理不可信仓库,意味着"只读即安全"
- 攻击者终极目标:在宿主机执行任意代码,或外泄凭据
基于这个模型,研究者从四个维度展开了攻击面分析。
三、漏洞全景:四个 PoC,三条独立漏洞 + 一条链式利用
3.1 V1:!!js 配置求值 = 加载期同步代码执行
漏洞本质 :DSH 的配置系统支持 !!js YAML tag,会在配置加载时同步执行 JavaScript 代码。这意味着任何能够写入配置文件的角色,都能在系统启动时执行任意代码。
PoC 验证:
lua
[V1] marker written at config-load time: true
[V1] marker content: "executed-at-config-load"
利用场景:
- 配置投毒 :恶意仓库诱导模型修改
cordis.patch.yml - 自修改部署 :DSH 的
demo:cordis场景下,workspace 与配置目录重叠,写边界被静默闭合
默认部署下的防线 :patch 文件位于 ~/.dsh/profiles/<name>/cordis.patch.yml,默认 workspace-write 权限恰好不包含该目录。但研究者指出:
"没有任何 boot-time 检查保证'已加载的配置目录 ∉ 可写 root'。"
一旦 workspaceRoot 被配到 home 目录、或启用 danger-full-access 模式,这道防线就不攻自破。
3.2 V2:"read-only" 沙箱读穿全盘
漏洞本质:read-only 模式对写操作有严格的 fence,但对读操作完全不设防。
技术细节:
- 进程内 fence(
fs-sandbox)只拦截writeText/editText,readText原样透传 - 进程级 bwrap 配置是
--ro-bind / /------整个宿主文件系统只读挂载进沙箱
PoC 验证:
arduino
[V2] read of a secret OUTSIDE the workspace under read-only mode:
"apiKey: dsh-poc-secret-value\n"
[V2] contrast --- write under the same mode denied with code: FS_SANDBOX_DENIED
更危险的插件正门 :动态插件声明 inject: ['fs', 'web'] 时,没有任何 allowlist 检查。这意味着 read-only 模式下,模型可以:
- 通过
fs.readText读取~/.ssh/*、.env、云凭据 - 通过
web服务将凭据外发
关键是零审批 :默认组合里没有任何 host 侧插件对工具调用发射 ask,"只读安全模式"下模型读凭据是零审批的。
3.3 V4:动态插件 VM 沙箱 exec 逃逸
漏洞本质 :DSH 允许模型通过 cordis_define/cordis_run 工具编写代码并挂载为动态插件,在 node:vm "沙箱"中运行。官方确实做了认真的对抗性设计------拒绝访问 ctx.root/ctx.parent、拒绝 ctx.extend、拒绝返回 Context 的服务......
但漏了一条路径 :execute(args, exec) 中的 exec 原样透传给了 VM realm 里的模型代码。
typescript
// guard.ts:582-584
execute(args, exec) {
return cloneJson(await rawExecute(args, exec), ...)
}
exec 是 ToolRunContext 类型,其 exec.agent.ctx 是真实的运行时 Context。facade 防住了语法面,没防住对象图。
PoC 验证:
csharp
[V4] dynamic package mounted and running: true
[V4] isError: false
[V4] sandbox-realm tool code retrieved from the host Context: "HOST_SECRET_LEAKED"
3.4 V4b:链式利用 ------ exec 逃逸 → 裸 subprocess → 无约束 RCE
这是整个审计中最具杀伤力的发现:将 V4 与其他漏洞链接,形成端到端攻击链。
为什么这个链成立? 沙箱约束(landlock/seatbelt/bwrap)只存在于 shell 层------bash-sandbox 在调用 ctx.subprocess.spawn 之前包装 argv。但 subprocess seam 自身对约束一无所知,没有任何 sandbox 引用。
这是典型的 enforcement 落点错误:安全策略落在 consumer(bash-sandbox)上,而不是执行点(subprocess seam)上。
PoC 验证:
ini
[V4b] dynamic package mounted and running: true
[V4b] tool result (isError=false): "spawned-and-exited"
[V4b] unconfined host process wrote marker: true
[V4b] marker content: "unconfined-spawn\n"
端到端攻击链:
sql
恶意网页 / 仓库内容 / fetch 的文档
│ prompt injection
▼
模型调用 cordis_define + cordis_run(默认组合零审批)
│
▼
VM 沙箱运行模型代码 → execute(args, exec) 拿到宿主 exec
│
▼
exec.agent.ctx.get('subprocess').spawn(...) ------ 无 landlock/seatbelt/bwrap
│
▼
宿主机任意进程执行 → 持久化(patch 文件 + !!js)→ 长期驻留
关键点:场景 A 不依赖任何部署配置失误。V1 单独评估时"默认部署下链是断的"的结论,在 V4b 面前不成立------写边界在插件路径上完全不参与。
四、外部利用场景汇总
| # | 攻击者入口 | 链条 | 影响 | 默认部署可达性 |
|---|---|---|---|---|
| A | Prompt Injection(恶意网页/仓库)→ 模型 | cordis_define → V4 exec 逃逸 → V4b 裸 subprocess | 宿主 RCE,零文件写入 | ✅ 是 |
| B | read-only 模式处理不可信仓库 | bash cat / fs read 工具 / 插件 inject:['fs','web'] |
凭据外泄(.ssh/.env/credentials) | ✅ 是 |
| C | 配置投毒 | 模型写 patch 文件 → !!js → HMR 执行 + 每次 boot 重放 |
持久化 RCE | ⚠️ 条件性 |
| D | 诱导模型 | skill 内容诱导模型走 A/B 路径 | 同 A/B | ✅ 是(放大器) |
五、方法论复盘:他们是怎么发现的?
这次评审的流程极具参考价值,按顺序是:
5.1 读架构文档,列"信任假设清单"
先精读 architecture.md、glossary、capability-seams 等文档,目标不是理解怎么用,而是找出系统在哪些地方假设了"模型会配合"。
列出的清单直接变成了候选漏洞:
- "配置里允许
!!js"------假设了配置文件的写入者可信 - "read-only 是安全模式"------假设了读不可怕
- "动态插件运行在受限环境"------假设了 VM 边界真的限制
- "sandbox 约束 shell 执行"------假设了所有执行都走 shell
5.2 分域并行审计
把代码库分成五个域并行审计:
- core loop + session 日志
- capability seam
- 工具管线与安全
- 编排
- typert/RPC/wire
关键技巧:画 enforcement 落点图。对每条安全属性,标出它在哪里被强制(provider?consumer?约定?),凡是落在 consumer 或文档语气里的,标记为高风险候选。
5.3 用仓库自己的规则当检测器
DSH 仓库有极其明确的工程规则(CLAUDE.md):
- "Enforce a decision in the operation that makes it"
- "Misconfiguration fails loud"
- "Apply bounds to the complete result"
把这些规则反过来当检测器:找出违反它们的地方,就是漏洞候选。
5.4 先验证原语,再分层 PoC
不直接写大 PoC,先逐个验证最小的"原语假设":
with(ctx)+eval引擎里process.getBuiltinModule可用?ToolExecutionInput.agent存在、registry 接受 agent?loader.create会触发 interpolate?writableRoots的语义与 patch 文件位置的关系?
然后分层 PoC,每层都比上一层更接近真实攻击,且全部用真实代码路径,零 mock。
5.5 失败路径也是证据
V4b 第一次尝试(loader.create 带 __jsExpr)失败了------但失败原因本身就是发现:VM-realm 对象过不了宿主 instanceof Object。如实记录失败路径,得到两个收益:
- 发现一条"偶然防御",评估它的脆弱性
- 转向 subprocess 路线,找到更直接的链
六、修复建议(按优先级)
研究者给出了清晰的修复路线图:
| 优先级 | 修复措施 | 封堵路径 |
|---|---|---|
| P0 | 给动态工具 execute 传剥离 agent.ctx 的 facade exec |
V4 本体 + V4b 整条链 |
| P0 | SubprocessRuntime 自身承担 confine 责任:约束在 seam 执行点落地 |
V4b 结构性根因 |
| P1 | 动态插件 inject allowlist(白名单收敛),define/run 接入审批管线 | V2 插件正门、场景 A 零审批入口 |
| P1 | read-only 排除 secret 目录;把默认 approval 真正接进 tools/pre-execute |
场景 B 凭据外泄 |
| P2 | boot 时校验"已加载配置目录 ∉ writableRoots",相交即 fail loud | 场景 C 纵深防御 |
七、官方回应与社区行动
7.1 官方态度
DSH 团队在 v0.1.2-rc.1 和 v0.1.3-alpha.2 中多次更新安全说明:
"DeepSeek Harness 尚未接受安全审计,沙箱、审批与权限控制不能保证隔离。"
这是一个诚实的声明,但也暴露了早期开源项目的普遍困境:功能迭代速度远超安全审计投入。
7.2 社区自救
生态的繁荣也催生了安全意识的觉醒:
- dshlib(DSH 插件图书馆):社区实验项目,为每个插件提供 CHA2A 认证等级(L0-L4)、安全扫描状态(硬编码密钥/危险命令/数据外传/越权)、OSV.dev 依赖漏洞检测
- dsh-pentest:面向 DSH 的渗透测试模式插件,支持探索链路、漏洞和资产视图
- PerryLink/dsh-permission-rules:Claude Code 风格的声明式权限规则,支持有序 allow/deny/ask + 工具/参数/路径匹配
- dsh-plugin-audit / dsh-plugin-market:插件静态权限审计与市场安全闸门
八、给开发者的安全实践清单
无论你是 DSH 用户还是插件开发者,以下清单都值得贴在显示器旁边:
8.1 安装插件前
- 看依赖树 :
package.json里有没有可疑依赖?体积异常大的要警惕 - 看权限诉求:一个"查天气"的插件为什么要声明文件系统写权限?
- 看网络出口 :
fetch/axios调用指向哪里? - 看数据回传:工具结果是否包含本地路径、环境变量、密钥片段?
8.2 运行时隔离
- 沙箱兜底:即使插件可疑,只要沙箱规则严格,破坏半径也可控
- 权限最小化:只给插件"它需要的",而不是"运行库默认开放的"
- 临时挂载测试:新插件先在隔离 workspace 里跑一个任务,观察行为再放行
8.3 可复用检查清单(适用于任何 Agent Harness)
研究者总结的这套方法论,可以套用到任何 Agent 运行时上:
- 配置求值面:配置文件里有没有代码求值钩子?谁能写这些文件?
- 读/写不对称:安全模式对写做 fence 时,读是否被同等对待?
- facade 的对象图:守卫防的是语法还是对象图?被守卫的上下文中是否有对象引用能走回真实运行时?
- enforcement 落点:每条安全策略在 provider/执行点强制,还是在 consumer/约定层?
- 注入面的下游能力:工具结果裸进上下文时,模型被注入后能触达的最大权限是什么?
- 授权语义:审批请求承载了什么?授权粒度是 per-request 还是 per-package?
九、结语
三个漏洞的共同根因可以压缩成一句话:
安全属性的 enforcement 落在了错误的层------配置求值信任了写入者、只读模式信任了"读不可怕"、VM 沙箱防了语法没防对象图、进程约束落在了一个 consumer 而不是执行点。
而链式利用告诉我们:在 Agent Harness 里评估单个漏洞的"可达性"时,必须画到端到端------V1 单看被写边界挡住,V4b 让这道防线根本不在路径上。
对这类系统的防御者,最大的一条建议来自研究者:
把"模型被注入后能走的最远路径"当作系统测试用例来写,而不是当作威胁模型文档来写。
DSH 的架构无疑是先进的------全插件化、可审计、模型无关。但正如一位社区成员所说:"赢了架构这一栏,输了成熟度这一栏。" 安全不是功能完成后的补丁,而是架构设计时的第一性原理。
在 Agent 生态爆发的前夜,DSH 的这次安全审计,或许能为整个行业敲响一记警钟。
参考链接:
本文基于公开安全研究报告整理,仅供技术交流与安全防御参考。