← 返回主报告:Kimi Agent(云端沙箱)技术报告 | GitHub 原文
合规与风险声明:本报告为安全研究与互操作学习目的的逆向分析,全程只读采集,未对目标系统做任何修改;文中所有密钥、token、账号级标识(chat_id / project_id / tenant_id 等)均已脱敏;沙箱内发现的默认弱口令(VNC/SSH)属平台侧配置,仅作安全发现披露,请勿用于访问任何不属于自己的系统。docx/pdf/xlsx/kimi-slides 等引擎二进制为 Moonshot 专有许可(禁再分发/逆向),本文仅作行为级描述。报告基于单次分析窗口(约 50 分钟生命周期),软件版本与技能库存随镜像更新可能变化。
采集时间:2026-08-18 08:14–09:26 UTC。来源:SSH 只读访问(kimi 用户,ssh -p 12222)+ root HTTP 兜底(http://127.0.0.1:18080/<绝对路径>)。所有路径均为沙箱内路径。 注意:沙箱在 09:26 UTC 左右被平台回收(frp 隧道 ssh-terminal 离线,三次重连均 Connection refused),少量收尾项未采集,见文末”未完成的取证缺口”。
1. OS 与内核:不是普通容器,是 cubevm 微虚拟机
- 用户态:
Debian GNU/Linux 12 (bookworm)(/etc/os-release)。 - 内核:
Linux 2d9b504a 6.6.69-cube.pvm.guest.005.x-g039db8913c80 #1 SMP PREEMPT_DYNAMIC Thu May 21 23:33:35 CST 2026 x86_64——定制 “cube.pvm.guest” 内核,明确是 guest 内核。 - 虚拟化证据:
/sys/class/dmi/id/sys_vendor=Cube Hypervisor,product_name=cube-hypervisor;systemd-detect-virt=kvm。/proc/cmdline:root=/dev/pmem0 rootflags=dax,errors=remount-ro ro rootfstype=ext4 panic=1 console=hvc0 net.ifnames=0 cubevm ... clocksource=kvm-clock。pmem0+DAX、hvc0virtio-console、cubevm标记、mitigations=off 都是 Firecracker/Kata 类 microVM 典型特征。- 设备文件:
/dev/pmem0、/dev/pmem1、/dev/vda~/dev/vdd、/dev/vsock、/dev/cubemsg0(宿主通信字符设备)、/dev/ndctl0/1、/dev/hvc0-7。没有/dev/kvm(guest 内不可嵌套)、没有/dev/dri(无 GPU,kasmvnc 配置里gpu.hw3d: false)。
- 结论:所谓”沙箱容器”= Moonshot 自研 cubevm microVM(腾讯云基础设施,见网络节)里再跑一层容器。容器边界在 VM 之内。
2. 容器边界:cgroup v1 + overlay2,非 Docker 标准运行时
/.dockerenv、/run/.containerenv均不存在。/proc/1/cgroup为 cgroup v1(12 个控制器单独挂载到/sys/fs/cgroup/<ctl>),各控制器路径为/default/<container-id>:
2d9b504a104a439f9374a6374a236fa7(memory/cpu/cpuacct/devices/freezer/net_cls/net_prio 有容器级子组;pids/cpuset/blkio/hugetlb/perf_event 在根 /)。
- 根文件系统是 overlay2:61 个 lower 层
/run/blk-cube/vdc/0..60,upperdir=/run/blk-cube/vda/disk/<container-id>/upper——镜像层放在独立块设备 vdc、可写层在 vda,由宿主的 blk-cube 机制热挂进 VM。 - tmpfs 覆盖
/etc/hosts、/etc/resolv.conf、/etc/hostname(运行时可写但不入镜像)。 - 业务 FUSE 挂载(proc/mounts 摘录):
portal /mnt/portal-overlay fuse.portal ... allow_other(只读技能/插件 overlay,claim 时绑定)drive9 /mnt/agents fuse.drive9 ... allow_other(可写工作区,详见 03-storage)
- 主机名 = 容器 ID 前 8 位:
2d9b504a;/etc/hosts 只有一行127.0.0.1 localhost 2d9b504a。 - 环境标识文件:
/run/e2b/.E2B_SANDBOX(5 字节标记文件,E2B 兼容信号,配合 envd 提供 E2B 协议命令执行)。
3. s6 初始化树与容器完整生命周期
3.1 引导链(/init → PID1)
/init 是 s6-overlay v3.1.6.2 标准入口(1012 字节 shell):自举 PATH(/command、/bin、/usr/bin)后 exec s6-overlay-suexec preinit → /package/admin/s6-overlay-3.1.6.2/libexec/stage0。 stage0 调 s6-linux-init-maker 生成 basedir /run/s6/basedir,最终 exec 出 PID1:
PID 1: /package/admin/s6/command/s6-svscan -d4 -- /run/service
s6 套件全部静态安装在 /package/admin/:execline-2.9.4.0、s6-2.12.0.2、s6-linux-init-1.1.2.0、s6-overlay-3.1.6.2、s6-rc-0.5.4.2 等。 关机由常驻的 s6-linux-init-shutdownd -d3 -c /run/s6/basedir -g 3000 -C -B 接管(3 秒 kill 宽限)。
3.2 服务目录结构
- 服务源定义在
/etc/s6-overlay/s6-rc.d/(编译进镜像);运行时编译结果在/run/s6-rc/servicedirs/,/run/service/下以符号链接挂入扫描目录。 - bundle
user(s6-overlay 默认启动目标)的 contents:browser-guard、envd、kasmvnc、kernel-server、portal、socat、sshd。user2bundle 为空。各服务均无 dependencies.d(除 browser-guard 在脚本内用s6-svwait -u /run/service/kasmvnc做显示就绪等待)。 S6_LOGGING=0(容器环境),即-B:无 catch-all 日志器,各服务日志直接到容器 stdout/stderr。- 镜像环境变量被 dump 到
/run/s6/container_environment/(每变量一个文件),各 run 脚本用#!/command/with-contenv bash继承——这是沙箱内所有服务环境变量的统一来源。
3.3 各服务 run 脚本要点(原文已取证)
| 服务 | 关键内容 |
|---|---|
| envd | exec envd -isnotfc。脚本内中文注释:AGS 不是 firecracker,无 MMDS(169.254.169.254 黑洞),-isnotfc 跳过 MMDS 探测,否则每次 /init 挂起 ~7s——这是针对非 firecracker 后端的定制补丁。提供 E2B 兼容命令执行通道。 |
| portal | Go 二进制 /usr/local/bin/portal -http-bind -allow-other -env prod -gateway-addr https://kimi-api-sandbox.msh.team/apiv2 /mnt/portal-overlay。注释说明:启动时是空 manifest,等 kimiwarden 在 claim 时 POST /api/v1/bind_token(HS256 JWT 换 STS)+ POST /api/v1/bind(overlay manifest:skills/plugins/auth/vault)。启用开关 KIMI_PROJECT_PORTAL_CAPABILITY_ENABLED=true 由 kimiwarden 注入。 |
| kernel-server | s6-setuidgid kimi python3 /app/kernel_server.py --host 0.0.0.0 --port 8888,WORKDIR=${KERNEL_SERVER_WORKDIR:-/mnt/agents},注入 DISPLAY=:99、HOME=/home/kimi(详见 02-runtime)。 |
| kasmvnc | 以 root 跑 /bin/bash /root/setup_kasmvnc.sh:生成 kasmvnc.yaml、vncpasswd(明文 ‘password’),启动 Xvnc :99 -SecurityTypes None -rfbport 5901 -websocketPort 6080 -interface 0.0.0.0,用 snakeoil 自签证书。脚本主进程是 sleep infinity,Xvnc(PID 155)实为孤儿挂在 PID1 下。 |
| browser-guard | s6-svwait 等 kasmvnc → s6-setuidgid kimi python3 /app/browser_guard.py --wait-display --display :99 --timeout 60 --monitor。它拉起 Chromium 151(--remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --no-sandbox --disable-blink-features=AutomationControlled --user-data-dir=/tmp/chromium_user_data,加载 /app/pdf-viewer 扩展,JS heap 限 512MB)。 |
| socat | execline 单行:python3 /opt/moonbox-project-template/bin/project-cdp-proxy.py——CDP 代理,名字留自旧架构。 |
| sshd | exec /usr/sbin/sshd -D -e,无自定义配置目录(sshd_config.d 为空)。 |
3.4 claim 时注入的第二批服务(生命周期关键观察)
进程树时间戳显示:boot 于 08:36(s6 全家桶拉起),而 drive9-fuse、drive9-fuse-watchdog 两个服务的 supervise 目录创建于 08:43——它们不是镜像 s6-rc 源,而是在沙箱被 claim(绑定具体项目)时由外部控制面直接写进 /run/service/ 的:
/run/service/drive9-fuse/run:重启前先fusermount3 -uz清残留死挂载(带防误删校验),source 环境文件后
exec drive9 mount --foreground --mode=fuse -allow-other --server http://10.213.5.144 --profile kimi-project --durability close-sync :/projects/<项目UUID> /mnt/agents,日志到 /tmp/kimi-project-drive9.log,自带 2 秒防抖动重启限速。
/run/service/drive9-fuse-watchdog/run:每 15s 探活(stat /mnt/agents/.+ls .drive9-health小目录,避免大目录 readdir 触发预取),连续 3 次失败pkill -9 -x drive9交给 s6 重启,日志写同一文件。- 同时创建的还有
/run/kimi-project/preview.json(root:kimi 0440,claim 元数据)和两个 root 调试 http.server(18080 暴露/tmp/project-probe、18082 绑 127.0.0.1——疑为控制面探针)。
完整生命周期:cubevm 微 VM 启动(pmem0 DAX rootfs)→ 容器层 overlay2 组装 → /init(s6-overlay) → s6-svscan → s6-rc 编译并拉起 7 个基础服务(envd/portal/sshd/kasmvnc/kernel-server/browser-guard/socat)→ 沙箱进入空闲池 → 用户会话 claim:kimiwarden 通过 portal 8080 下发 bind_token+manifest(skills overlay 挂上),同时把项目级 drive9-fuse + watchdog 服务注入 /run/service(工作区挂上)→ 会话结束沙箱回收(本次实测存活约 50 分钟,09:26 UTC 被回收)。
4. 用户与权限模型
- 用户:
kimi:x:999:995::/home/kimi:/bin/bash,仅属自己的组;root 正常存在;另有 Debian 常规系统用户(systemd-*、avahi、polkitd 等,来自镜像预装包)。kimi 不在 sudo 组。 - sudo:
/etc/sudoers是 Debian 原版(root + %sudo ALL),sudoers.d 只有 README;sudo -n true实测a password is required——kimi 无免密 sudo,事实上也无法 sudo(密码未设置)。 - /etc/shadow(经 18080 root 通道读取,已脱敏):
root有 yescrypt 哈希($y$j9T$KQhGRcPm...,仅记前 8 位);kimi:!(锁定,无密码)。其余系统账户全锁。 - SSH:
AllowUsers kimi(白名单只放行 kimi,PermitRootLogin yes 被其架空),PubkeyAuthentication + PasswordAuthentication 双开,sshd_config 里SetEnv给 SSH 会话注入 npm 镜像NPM_CONFIG_REGISTRY=https://npm.mirrors.msh.team/和一条把各 skills 模板node_modules/.bin塞进前面的定制 PATH。 - 提权辅助:不存在
/etc/s6/sudod规则目录;s6-sudod 仅以s6rc-oneshot-runner的 IPC socket 形式存在(/run/s6-rc/servicedirs/s6rc-oneshot-runner,用于 root 跑 oneshot),与业务无关。 - 权限分工总览:root 持有基础设施(s6、portal、drive9、Xvnc、sshd、envd);kimi 持有用户负载(kernel-server、browser-guard、Chromium、ipykernel)。工作区
/mnt/agents由 kernel-server run 脚本chown -R kimi:kimi。
5. 资源限制(cgroup v1 实测值)
| 维度 | 限制 | 证据 |
|---|---|---|
| 内存 | 4 GiB 硬限(4294967296 B),无 swap | /sys/fs/cgroup/memory/memory.limit_in_bytes;free -h:total 3.9Gi、采集时已用 1.5Gi、buff/cache 2.2Gi |
| CPU | 配额 2 核(cfs_quota 200000/100000),可见 3 个逻辑核 | nproc=3;/sys/fs/cgroup/cpu/cpu.cfs_* |
| 磁盘 | overlay 根 41G(可用 39G);/dev/shm 仅 64M;/mnt/agents 显示 1.0T(远端配额) | df -h |
| 进程数 | nproc ulimit 16152;pids 控制器未按容器挂载(无 pids.max 可读) | ulimit -u |
| 文件句柄 | nofile 1024(偏小,高并发 FD 型负载是潜在瓶颈) | ulimit -n |
| 其他 | core=0、stack 8M、locked memory 8M | ulimit -a;/etc/security/limits.conf 为空 |
6. 网络
- 接口(/proc/net/dev):
lo、eth0、dummy0、sit0(dummy0/sit0 无流量,为内核默认残留)。 - 编址(/proc/net/route 解码):eth0 为 169.254.68.4/30 链路本地段,默认网关
169.254.68.5——宿主侧 NAT,沙箱无真实内网地址。 - DNS:
/etc/resolv.conf(tmpfs,”Generated by NetworkManager”)指向183.60.83.19、183.60.82.98(腾讯 DNSPod 公共 DNS)。 - 出站:可直连公网,未做 egress 过滤。实测
https://www.baidu.com200、https://github.com200;出口公网 IP43.153.86.66(腾讯云)。SCF_POD_ID环境变量进一步指向腾讯云 SCF/云函数宿主。 - 内网依赖:drive9 存储服务端
http://10.213.5.144;portal 网关https://kimi-api-sandbox.msh.team/apiv2;pip/npm 走*.mirrors.msh.team内网镜像。 - 镜像精简到没有 ip、iptables、ss 命令(网络工具不预装,诊断只能靠 /proc)。
- 对外暴露链路:sshd(22)/CDP(9222)/VNC(5901/6080)/portal(8080)/kernel(8888) 监听在容器 eth0,由沙箱内 frpc 反向连到我们的 frps(127.0.0.1:12222 等端口)——沙箱本身无入站 NAT 规则。
7. 时区与环境变量来源
- 系统时钟/时区:
/etc/localtime → Etc/UTC、/etc/timezone=Etc/UTC,date 输出 UTC。TZ=Asia/Shanghai只存在于容器环境变量(cubevm 平台注入),不改系统时区——业务进程见上海时区,系统日志见 UTC。 - 环境变量三级来源:
- 平台注入(cubevm/控制面 → /run/s6/container_environment → with-contenv → 所有 s6 服务):
CUBE_CONTAINER_ID_SANDBOX/MONITOR_SIDECAR、SCF_POD_ID、KIMI_PROJECT_PORTAL_CAPABILITY_*、MOONBOX_BASE_IMAGE=msh-sandbox.tencentcloudcr.com/kimiclaw/moonbox-okc:project-20260811-2、SSH_PASSWORD、VNC_PASSWORD等。 - sshd SetEnv:SSH 会话专属(npm 镜像、含 skills 模板 bin 的 PATH)。注意 SSH 会话的 PATH 与 s6 服务的 PATH(/command 开头)不同。
- 服务 run 脚本内 export:如 kernel-server/browser-guard 的 DISPLAY、HOME、XAUTHORITY。
- 容器环境里
HOME=/root(镜像遗留),但各服务脚本显式覆盖成/home/kimi;直接 SSH 进 kimi 时 HOME 正常。 /proc/1/environ对 kimi 不可读,经 root HTTP 通道读到为空——PID1 (s6-svscan -d4) 启动时环境已被 stage0 清理,敏感值只留存在 container_environment 文件(kimi 可读)。
8. 密钥/凭据盘点(全部脱敏)
| 凭据 | 位置 | 格式/状态 | 用途 |
|---|---|---|---|
| SSH_PASSWORD | /run/s6/container_environment/SSH_PASSWORD | 11 字符明文,kimi 可读 | 平台注入的 kimi SSH 密码(与 shadow 中 kimi:! 不一致,疑供控制面它用) |
| VNC_PASSWORD | 同上 | 11 字符明文 | VNC/web 桌面访问密码(Xvnc 实际 -SecurityTypes None,疑给 web 层用) |
| root 密码哈希 | /etc/shadow | yescrypt $y$j9T$KQhGRcPm... | root 本地登录 |
| kasmvnc 密码 | /root/setup_kasmvnc.sh 内嵌 | 字面量 ‘password’ | vncpasswd 初始化(实例密码文件 /root/.vnc/passwd、/root/.kasmpasswd) |
| Xauthority | /root/.Xauthority、/home/kimi/.Xauthority | MIT cookie | X 显示访问 |
| TLS 私钥 | /etc/ssl/private/ssl-cert-snakeoil.key | 自签 snakeoil | kasmvnc websocket TLS |
| portal bind_token | 运行时由 kimiwarden POST 到 portal :8080 | HS256 JWT(未落盘可见) | claim 时换 STS,授权 overlay 挂载 |
9. 未完成的取证缺口(沙箱 09:26 UTC 被回收导致)
- stage0 尾部与
/run/s6/basedir/scripts/rc.init(s6-rc 拉起 user bundle 的确切调用)未抓到原文;§3 的引导链结论基于 s6-overlay 3.1.6.2 标准行为 + 已取证片段。 - 监听端口全量清单(/proc/net/tcp 解码)未取回;已知端口来自进程参数与既有架构知识。
- shell 级环境来源(/etc/profile.d/*、/home/kimi/.bashrc)未读取。
/opt/moonbox-project-template/模板内容(socat 实际执行的 CDP 代理)未深入——留给对应章节。
10. 对 Hermes 的可移植性速评
- s6-overlay 标准容器初始化(/init→stage0→s6-rc bundle user + with-contenv 环境分发)可直接照搬,Hermes 若要多服务编排这是最省事且久经考验的方案;服务 run 脚本的
s6-setuidgid降权 +s6-svwait依赖等待模式值得复刻。 - “启动时空 manifest、claim 时绑定”的两阶段生命周期(portal bind_token → drive9-fuse 注入 /run/service)是池化沙箱的关键设计:镜像与环境无关,项目身份延迟绑定——Hermes 要做沙箱池,这套模式价值最高。
- envd
-isnotfc补丁说明 Kimi 沙箱抽象层兼容 E2B API 但后端换成了自研 cubevm;Hermes 若复用 E2B 协议,注意元数据服务探测必须可开关。 - nofile=1024、/dev/shm=64M、2 核/4G 的限额是低成本高密度取舍,浏览器场景已踩到边缘(Chromium 限 JS heap 512M),Hermes 容量规划可参考。