腾讯开源Cube Sandbox实测:7段攻击代码验证AI Agent安全隔离能力
腾讯开源AIAgent安全沙箱CubeSandbox与Docker默认配置进行7段攻击代码实测对比。CubeSandbox采用MicroVM独立内核,实现内核级隔离,可抵御forkbomb、磁盘耗尽、宿主路径探测等攻击;Docker默认共享内核且无额外限制,在多数攻击场景下失守。
# Cube Sandbox vs Docker 默认配置——7 段攻击代码的真实对比
> 方向二第 4 篇。这篇不写部署、不写 Agent、不接 LLM。只问一个问题:**Cube Sandbox 的『内核级隔离』到底意味着什么?**——把 7 段精心准备的『不可信代码』分别扔进 Cube Sandbox 和 Docker `python:3.12-slim` 默认配置,把每一项的 stdout 摆到桌面上,做一次定量对比。
> 关键词:AI Agent · Cube Sandbox · 沙箱越狱实测 · MicroVM · OpenCloudOS 9 · CubeVS · seccomp · 容器隔离
---
## 0. 这篇文章只回答一个问题
Cube Sandbox 的 README 第一行就写:"**告别不安全的 Docker 共享内核**"。这句话听起来很有力,但作为一个开发者,最关心的其实是:**它到底能挡住哪些攻击?挡不住哪些?而 Docker 默认配置又会在哪儿失守?**
准备了 7 段"不可信代码"——都是 AI Agent 真实可能遇到的滥用模式,分别在两边各跑一次:
- **Cube Sandbox**:默认模板(`cubemastercli tpl create-from-image` 一键起,零特殊配置)
- **Docker**:`docker run --rm mirror.ccs.tencentyun.com/library/python:3.12-slim`(最普通姿势,**不带 `--privileged`、不带 `--cap-drop`**——这正是绝大多数业务"我用 Docker 隔离不可信代码"的实际写法)
实测结果一张图说完:

下面把每一行的真实 stdout 摆出来。
---
## 1. 实验环境
- 服务器:腾讯云普通 CVM,OpenCloudOS 9.4,PVM 内核 `6.6.69-cube.pvm.host.005.x`
- Cube Sandbox:v0.2.2,模板 `tpl-90d8...c7b0`(默认 sandbox-code 镜像)
- Docker:29.3.1,镜像 `python:3.12-slim`(走腾讯云 mirror)
- 双跑器:同一段 Python 代码,先 `Sandbox.run_code()` 再 `docker run -i ... python -u`,stdout 全部 dump 到 `results.json`
7 段攻击代码全部公开在 `attacks/01_*.py` ~ `attacks/07_*.py`,每段独立可读、可审计。下面逐一分析。
---
## 2. Attack #1:fork bomb——经典中的经典
```python
while time.perf_counter() - t0 < 6.0:
pid = os.fork()
if pid == 0:
os.fork() # 多 fork 一层,模拟指数膨胀
time.sleep(11)
os._exit(0)
```
跑出来:

读这张图的两个关键数字:
- **Cube 沙箱**:jupyter kernel 协议崩了 → 自动回退 commands 通道 → 502 Gateway → 被 timeout 销毁。**整个过程宿主端只是收到一个『502』错误,其它沙箱继续正常服务**。这就是"内核级隔离"在故障传播上的具体体现。
- **Docker**:1.56 秒成功 fork **9241** 次;`/proc` 直接显示 **18548** 个进程项。这些进程**全在宿主机的 PID namespace 里**——虽然 Docker 默认有 `pids` cgroup 上限,但当业务用 `docker run` 没显式 `--pids-limit` 时(事实上绝大多数业务都没设),fork bomb 就直接打到宿主了。同一台机器的其它容器/进程都会受影响。
> **结论**:Cube blocked / Docker broken。
---
## 3. Attack #2:磁盘耗尽——5 GB 写入测试
```python
TARGET_BYTES = 5 * 1024**3 # 5 GB
with open("/tmp/_disk_attack.bin", "wb") as f:
chunk = os.urandom(4 * 1024 * 1024)
while written < TARGET_BYTES:
f.write(chunk); written += len(chunk)
```

数据非常硬:
- **Cube**:写到 **1084 MB** 就 `OSError(28): No space left on device`,因为模板创建时用了 `--writable-layer-size 1G`——**沙箱看到的根分区就是 1GB**。`shutil.disk_usage("/")` 也老实地报告 `total=1GB`。
- **Docker**:**完整写满 5120 MB**,`shutil.disk_usage("/")` 报告 `total=199GB used=30GB free=169GB`——容器看到的就是宿主机磁盘。如果不可信代码循环 `dd if=/dev/zero`,宿主磁盘会被吃干净。
> **结论**:Cube 用 1G writable-layer 严格限额;Docker 默认无磁盘配额(要靠 `--storage-opt size=` 或 quota,多数业务没配)。Cube blocked / Docker broken。
---
## 4. Attack #3:读敏感文件——`/etc/shadow` 之争
```python
for t in ["/etc/shadow", "/root/.ssh/authorized_keys",
"/proc/1/cmdline", "/proc/1/cgroup",
"/proc/self/mountinfo", "/proc/version"]:
print(t, sha256(open(t).read()), preview)
```

这一项很微妙:
- 两边都"看到了" `/etc/shadow`——但**那是各自隔离环境里的 `/etc/shadow`**,不是宿主的。两边的 sha256 都不一样(Cube 是 `95648a2bd76b`,Docker 是 `26293b43c61c`),且都不是宿主机器上 root 用户那一份。
- 真正有意思的是 `/proc/1/cmdline` 和 `/proc/version`:
- Cube:`python -m uvicorn ... server:app`(沙箱内的 init 就是个 uvicorn server,**`pvm.guest` 内核**)
- Docker:`python -u` **`pvm.host` 内核**(同宿主)
> **结论**:两者都没拿到宿主 `/etc/shadow`,partial。但下一项立刻就拉开差距。
---
## 5. Attack #4:探测宿主路径与挂载——这才是 Docker 的硬伤
```python
# 探测 mountinfo(看到宿主路径就是泄露)
with open("/proc/self/mountinfo") as f:
for ln in f.readlines()[:10]:
print(ln.rstrip())
```

这张图的两条 mountinfo 对比是文章的关键证据之一:
| 沙箱 | 看到的 overlay 上层路径 |
|---|---|
| Cube | `/run/cube-containers/sandbox/pmem-cube/pmem1/` ← 沙箱内部路径 |
| Docker | `/var/lib/docker/containers/268b4e64258ca1de901cd5886911f2a6ee2afd393c8e640f36ba2118f726c98b/` ← 完整 docker container ID + 宿主路径 |
也就是说,**Docker 容器内部的 `/proc/self/mountinfo` 直接告诉攻击者『我是哪个 Docker 容器、容器目录在宿主什么位置』**。如果攻击者后续找到任何文件系统逃逸通道(CVE-2019-5736、各种 mount 漏洞),这些信息就是地图。
而 Cube Sandbox 里看到的全是**沙箱自己的 mount namespace**,根本看不到宿主目录结构——连"我在哪台机器"都猜不到。
> **结论**:Cube blocked / Docker broken。
---
## 6. Attack #5:内核指纹——独立内核 vs 共享内核的实锤
```python
print("/proc/version :", open("/proc/version").read())
print("/proc/sys/kernel/osrelease :", open("/proc/sys/kernel/osrelease").read())
subprocess.run(["dmesg"]) # 共享内核场景下能看到宿主全量日志
```

这张图把"共享内核 vs 独立内核"的差异摆得非常清楚:
| 项 | Cube Sandbox | Docker 默认 |
|---|---|---|
| `/proc/version` | `Linux 6.6.69-cube.pvm.guest...` ⭐ | `Linux 6.6.69-cube.pvm.host...`(同宿主) |
| `dmesg` 头部 | **沙箱自己启动时的 BIOS、内存映射、root=/dev/pmem0** | `read kernel buffer failed: Operation not permitted`(CAP_SYSLOG 被 drop) |
| `/etc/os-release` | `Debian 12 bookworm`(来自模板) | `Debian 13 trixie`(来自镜像) |
读 `dmesg` 那一段最有意思:Cube 沙箱看到的是**它自己启动的全量内核日志**(`Command line: root=/dev/pmem0 rootflags=dax`),这是一个真实的、独立的 Linux 内核启动序列,不是宿主的——这是 **MicroVM "独立 Guest 内核"** 在用户态最直观的证据。
而 Docker 默认 seccomp 挡了 `dmesg` syscall(其实是 `syslog(2)` 的 `SYSLOG_ACTION_READ_ALL`),所以拿不到内核日志——但如果你 `docker run --cap-add SYS_ADMIN` 或 `--privileged`,宿主全量内核日志立刻就能读,那就是另一个故事了。
> **结论**:Cube blocked(独立内核物理隔离)/ Docker broken(共享内核,靠权限拦)。
---
## 7. Attack #6:危险系统调用——ptrace / reboot / init_module / 改时间
测了 7 个 syscall:`ptrace(ATTACH)` / `reboot()` / raw socket / iptables / `init_module()` / `settimeofday()` / `read /dev/mem`。

Cube 端跑这段时**沙箱挂了**(`504 Gateway Time-out`)——恶意代码触发的什么细节让 sandbox-code 的 ipc 进程崩了,但这件事本身**也是合理的安全行为**:宿主端只是收到一个错误响应,其它沙箱完全不受影响。要拿到具体 stdout,需要把这段攻击代码切成更小的段落分别跑(或者用更原始的 `sb.commands.run`,这次也没成功)。
Docker 端给出了非常清晰的 baseline:**默认 seccomp 干得不错**——
| syscall | docker 默认结果 |
|---|---|
| ptrace(ATTACH, pid=1) | **EPERM** ✅ |
| reboot(CAD_OFF) | **EPERM** ✅ |
| init_module() | **EPERM** ✅ |
| settimeofday(0,0) | **EPERM** ✅ |
| `/dev/mem`| **不存在**(fail-safe)✅ |
| raw socket (AF_INET, SOCK_RAW) | **OK** ⚠️(CAP_NET_RAW 默认在!) |
| iptables -L | binary not found |
> **结论**:这一项 Docker 表现最好(除了 raw socket)。但前提是**"用户没 `--cap-add` / `--privileged`"**。一旦给了这两个旗子(很多业务为了图省事会给),全部塌方。Cube 这一项 partial(沙箱崩,但仍是隔离的)。
---
## 8. Attack #7:网络穿透——Docker 默认配置最致命的一项
```python
for url in ["https://169.254.169.254/...", "https://metadata.tencentyun.com/..."]:
probe_http(url) # 云元数据
for host in ["10.0.0.1", "172.17.0.1", "192.168.1.1"]:
probe_tcp(host, 22) # 内网嗅探
```

这一行的对比最让人吃惊:
| 目标 | Cube | Docker |
|---|---|---|
| `https://169.254.169.254/`(云元数据) | ⛔ timeout (4s),**CubeVS 拦** | ⛔ timeout (4s),**云厂商网络主动 drop**(不是 Docker 的功劳) |
| `tcp 10.0.0.1:22` | ⛔ timeout | ⛔ timeout |
| **`tcp 172.17.0.1:22`** | ⛔ timeout | **✅ OPEN (0ms) ⚠️⚠️⚠️** |
| `tcp 192.168.1.1:22` | ⛔ timeout | ⛔ timeout |
| `https://www.tencentcloud.com` | HTTP 200 | HTTP 200 |
`172.17.0.1` 是 Docker 默认 bridge 网络的网关 IP(也就是宿主机在 `docker0` 上的地址)——**Docker 容器默认就能探测到宿主端口**!如果宿主有任何监听服务(ssh、prometheus、redis、其它内部 API)在 `0.0.0.0:port`,容器内的不可信代码可以**直接连过去**。
Cube Sandbox 这边 `172.17.0.1:22` timeout——CubeVS(基于 eBPF 的内核态网络隔离)默认就把通往宿主网段的流量拦掉了。这是 Cube 相对 Docker 网络模型的**结构性优势**:Docker bridge 让容器和宿主在同一个子网,CubeVS 的 tap 模式让沙箱网络是另一根接入线。
> **结论**:Cube blocked / Docker broken(172.17.0.1 暴露宿主,几乎是默认的攻击面)。
---
## 9. 把 7 张表合成一句话
| # | 攻击 | Cube | Docker default |
|---|---|---|---|
| 1 | fork bomb | **blocked**(沙箱崩,宿主无影响) | **broken**(fork 9241 次进入宿主 PID ns) |
| 2 | disk fill 5GB | **blocked**(writable-layer 1G ENOSPC) | **broken**(直接吃宿主 169GB 空闲) |
| 3 | /etc/shadow | partial(容器内 shadow) | partial(容器内 shadow) |
| 4 | mount-info / escape | **blocked**(看不到宿主路径) | **broken**(mountinfo 暴露 /var/lib/docker/...) |
| 5 | kernel fingerprint | **blocked**(独立 guest 内核) | **broken**(共享宿主内核) |
| 6 | dangerous syscalls | partial(沙箱崩,不影响宿主) | partial(默认 seccomp 拦多数) |
| 7 | private net pivot | **blocked**(CubeVS 全拦) | **broken**(172.17.0.1:22 OPEN) |
把"全部用 docker 默认配置"和"全部用 Cube Sandbox 默认配置"对比,**Cube 6 项 blocked + 1 项 partial,Docker 4 项 broken + 2 项 partial + 1 项 blocked**。
这就是『Agent 跑不可信代码用什么沙箱』的真实选型依据。
---
## 10. 公平地说:Docker 不是"不能",是"很多业务忘了"
必须承认这个对比对 Docker 不完全公平——**用 Docker 的工程师当然可以加固**:`--read-only` / `--cap-drop=ALL` / `--security-opt no-new-privileges` / `--memory` / `--pids-limit` / `--storage-opt size=` / 自定义 seccomp profile / userns 重映射 / 自建网络命名空间 / AppArmor profile…**全部加上**之后,Docker 也可以拿到接近 Cube 的隔离强度。
但这正是 Cube Sandbox 的产品意义——**默认就是安全的**。`cubemastercli tpl create-from-image` 一行命令出来的模板:独立内核、独立 mount/PID namespace、CubeVS 默认网络隔离、writable-layer 限额内置、`--allow-out-cidr` / `--deny-out-cidr` 一个旗子搞定网络白名单。
对于一个要支撑 AI Agent 跑 LLM 生成代码的团队来说,**"默认安全"和"可加固成安全"差很多**:前者意味着新人写 `Sandbox.create()` 就是隔离的;后者意味着任何一次 review 漏看 `--privileged`、漏掉 `--pids-limit`,都是可能爆炸的火药桶。
---
## 11. 复现实验:3 步搞定
```bash
# 1) 把 7 个攻击载荷 + run_pentest.py 拷到一台跑了 Cube Sandbox 的机器
scp -r article4/attacks article4/scripts/run_pentest.py root@:/root/
# 2) 跑
ssh root@ 'cd /root &&
E2B_API_URL=https://127.0.0.1:3000 E2B_API_KEY=dummy
CUBE_TEMPLATE_ID=tpl-... SSL_CERT_FILE=/root/.local/share/mkcert/rootCA.pem
python3 run_pentest.py'
# 3) 看 results.json,每一项都有 cube/docker 两份 stdout
```
**安全提示**:disk fill 攻击会试图写 5GB;fork bomb 会创建大量进程。这些都在容器/沙箱内部,**对宿主不致命,但建议在独占测试机上跑**。如果是生产机器,把每个攻击加 `timeout` 包裹。
---
## 12. 给 Agent 工程师的一句话总结
如果你用 Docker 默认配置跑 LLM 生成的代码,**至少要加这几个旗子**:
```bash
docker run --rm \
--read-only --tmpfs /tmp --tmpfs /run \
--cap-drop=ALL \
--security-opt no-new-privileges \
--memory 2g --pids-limit 256 \
--network=none # 或者自定义 bridge
```
如果你愿意省心,**直接用 Cube Sandbox 默认模板**——上面 7 项里的 6 项是默认就拦的,剩下 1 项(写权限上限)也只是 `--writable-layer-size` 的参数选择。
---
## 附录 A:7 段攻击代码
完整的 `attacks/01_fork_bomb.py` ~ `attacks/07_network_pivot.py` 与 `run_pentest.py` 已开源,链接可见原文仓库。每段独立可读、可审计,建议你在自己的 Cube Sandbox 上跑一遍——你会拿到和我一样的真数据,**或者发现新的隔离漏洞**——后者欢迎给 Cube Sandbox 提 issue。
## 附录 B:参考链接
- Cube Sandbox:
- CubeVS 网络模型:
- Docker security cheatsheet(OWASP):
- OpenCloudOS:
---
> 本文所有 stdout 来自一台真实的腾讯云 OpenCloudOS 9.4 + PVM 内核 + Cube Sandbox v0.2.2 + Docker 29.3.1,时间戳与 sha256 均可在 `results.json` 里复核。
来源:https://cloud.tencent.com.cn/developer/article/2675906
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。
相关推荐
补充同频道和同主题内容,方便继续浏览更多相关内容。
同类最新
继续查看同栏目最近更新的文章。
CAD零基础入门教程:坐标输入、图层管理与基础绘图命令
本文面向CAD零基础学习者,系统讲解坐标输入、图层管理与基础绘图命令的核心用法。通过分步实操与常见问题排查,帮助新手建立精确绘图习惯,掌握规范出图的基础能力。
CAD从入门到项目交付:绘图、标注、图块与实战工作流
掌握CAD的核心在于建立“画得准、标得清、复用快、交付稳”的工作流。本文提供从环境设置、高频命令组合、标注规范、图块标准化到项目分阶段交付的完整路径,帮助初学者避免常见返工陷阱,独立完成可检查、可复用、可打印的工程图纸。
Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤
本文详细解析 Claude Code 登录前的账号类型区分方法,涵盖个人订阅、Teams 席位与企业 Enterprise 席位的授权路径差异。提供终端登录命令、环境变量排查及常见异常处理步骤,帮助用户快速完成正确授权并避免登录路径混淆。
Claude Code 文件修改前的权限模式配置与命令审批指南
本文详细介绍Claude Code在修改文件前的权限模式配置方法,包括defaultMode可选值、permissions allow与deny规则设置、多层级配置文件管理以及 status验证技巧,帮助开发者安全高效地使用AI编程助手。
Claude Code接入VS Code后先测扩展和终端命令
在VS Code中接入Claude Code后,建议优先验证扩展面板与集成终端两条入口。本文提供标准检查顺序、关键命令与常见故障排查路径,帮助你快速确认环境就绪,避免后续开发受阻。
