Skip to content

v1.7.0

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50
· 63 commits to master since this release

新增

  • 新增 Claude Code 自身配置文件篡改检测:改 ~/.claude/settings.json(或项目内
    .claude/settings.json/settings.local.json)、.claude/hooks/ 目录下的脚本、
    CLAUDE.md 之前完全没有规则覆盖。这是目前发现的最大缺口——改配置文件删掉 hook
    注册,比 kill -9 探针进程更隐蔽(不需要杀任何进程,Claude Code 下次工具调用
    时应用层监测就已经悄悄失效了),跟已有的 kill_monitoring_process 是同一个
    "防绕过"目标,但覆盖的是配置层面而不是进程层面。新增 claude_config_tamper
    (risk: high, action: confirm),tools: ["Write", "Edit", "NotebookEdit"],
    field: "file_path"。用 confirm 而不是 block,是因为正常给自己项目加一个
    新 hook、改 CLAUDE.md 本来就是合理操作。插在 shell_rc_write 之后。
  • Docker 特权/挂载检测新增 Docker socket 挂载识别:docker_privileged_or_host_mount
    原来只认 --privileged 和 -v /:/ 这两种写法,-v /var/run/docker.sock:/var/run/docker.sock
    这个更常见、更经典的容器逃逸手法(挂载宿主机 docker socket 等于把宿主机 root
    权限直接送进容器)完全漏检。扩展正则加上 docker.sock 这个子串匹配,同时把
    这条规则的 risk 从 medium 提到 high(--privileged/docker.sock 挂载都是
    宿主机 root 等价的风险,medium 偏低了;action 维持 confirm,兼容合法的
    DinD/CI 场景)。
  • 新增写入内容密钥格式扫描:之前所有规则全部按文件路径/命令文本判断,Claude
    把 API key 写进任意一个不带敏感文件名特征的文件(比如 config.py、
    notes.txt)完全不会触发任何规则。新增 secret_pattern_in_write
    (risk: high, action: confirm),tools: ["Write", "Edit", "NotebookEdit"],
    新增的 field: "content" 扫描私钥文件头(-----BEGIN ... PRIVATE KEY-----)、
    AWS Access Key(AKIA/ASIA 前缀)、GitHub token(ghp_/gho_/ghu_/ghs_/
    ghr_/github_pat_)、Anthropic/OpenAI key(sk-ant-/sk-proj-/sk-)、Slack
    token(xox[baprs]-)、Google API key(AIza)、npm token(npm_)、Stripe
    live key(sk_live_)这几类高置信度的固定前缀格式。cc_monitor/policy.py 的
    FIELD_CANDIDATES 新增 "content": ["content", "new_string", "new_source"]
    映射——Write 用 content、Edit 用 new_string、NotebookEdit 用
    new_source,三个工具语义上都是"即将写进文件的内容",一条规则要同时认这三个
    字段名,跟已有 file_path 的多候选写法是同一个道理。已经写进 .env/.ssh/
    这类本来就被 sensitive_file_write 覆盖的路径不会重复触发——规则顺序保证更
    具体的路径规则先命中。局限性:按字符类型+长度的固定前缀匹配,不看信息熵,
    文档里的示例占位符密钥(比如全用 x 填充的假 key)如果长度凑巧够长也会被
    误判,这是所有轻量级密钥扫描工具(gitleaks/trufflehog 的非熵值模式)共有的
    局限,action: confirm 而不是 block 也是为了给这种误判留人工确认的余地。
  • 新增 git hooks / git config 持久化攻击面检测:core.hooksPath(把 git hooks
    重定向到别的目录)、url.<url>.insteadOf(悄悄把依赖源换成攻击者控制的仓库,
    真实供应链攻击手法)、直接用 Bash 重定向写入 .git/hooks/,之前都没有覆盖——
    跟已有的 crontab_persistence/systemd_persistence 是同一类"植入持久化后门"
    风险,但 git 生态里的对应手法完全是盲区。新增两条规则:git_hooks_persistence
    (risk: medium, action: confirm,tools: ["Bash"],覆盖上面三种命令行写法)
    和 git_hooks_file_write(同样 medium/confirm,tools: ["Write", "Edit", "NotebookEdit"],field: "file_path",覆盖直接用 Write/Edit 工具往
    .git/hooks/ 底下写文件这个命令行规则覆盖不到的写法)。git_hooks_persistence
    插在 git_hard_reset_clean 之后。
    以上四项用 26 个正负测试用例、外加 13 个覆盖全部旧规则类别的回归测试,全部
    直接调用 cc_monitor/policy.py 的真实 evaluate() 函数验证(不是简化版
    正则复现),确认新规则匹配正确、FIELD_CANDIDATES 新增 "content" 映射没有
    影响 command/file_path/url 这些既有字段的解析,node --test 5/5 全部
    通过。已有用户的 ~/.cc-monitor/rules.json 不会自动更新,想要这些新规则生效
    需要手动同步。
  • 落地 slowmist-agent-security
    致谢里提到的 5 条规则思路
    :credential_grep_scan(grep -r/rg -r 之类递归
    搜索 password/secret/api_key/token 等凭据关键字,medium/confirm)、
    npx_pipx_ephemeral_run(npx/pnpm dlx/bunx/pipx run/uvx 这类跳过本地
    安装痕迹直接执行远程包的一次性执行,medium/confirm)、proc_env_read(读取
    其它进程的 /proc/<pid>/environ/cmdline,跨进程偷凭据的经典手法,high/
    confirm)、browser_credential_read + browser_credential_read_bash(Chrome/
    Chromium/Brave/Edge/Firefox 的 Cookies/Login Data/cookies.sqlite/
    logins.json/key4.db 等登录态文件,分别覆盖 Read 工具和 Bash 命令行两种访问
    方式,high/confirm)、dynamic_exec_in_write(写入内容里出现 eval(/
    exec(/os.system(/subprocess.*shell=True/new Function(/
    child_process.exec( 这类动态执行代码,常见于植入后门,但正常代码里也很常见,
    故意压低到 medium/log 不打扰正常写代码)。
  • 新增编码混淆执行检测:encoded_payload_exec(base64 -d/xxd -r -p 解码后
    管道给 sh/bash/zsh/python3,是 curl_pipe_shell 最常见的绕过变体——同样
    是"下载/构造一段东西直接丢给解释器执行",只是用编码绕开了对 curl|wget 关键字
    的文本匹配,high/block,跟 curl_pipe_shell 同等对待)。
  • 新增 SSH 隧道/反向代理检测:ssh_tunnel_reverse_proxy(ssh -R/-D/-L
    建隧道、socat、chisel client/server,medium/confirm——很多合法用途
    比如连内网数据库,所以没有做成 block)。插在 reverse_shell_pattern 之后,
    跟已有更严格的 socat 反弹 shell 特征是同一个"隐蔽出网通道"主题,评估顺序上
    reverse_shell_pattern 更精确的匹配优先命中。
  • claude_config_tamper 扩大覆盖面到 MCP/Skills 配置:原来只认
    .claude/settings*.json/.claude/hooks//CLAUDE.md,现在加上项目级
    .mcp.json 和 .claude/skills/ 目录——写入一个恶意 MCP server 配置或恶意
    skill 定义,是比改 hooks 更隐蔽的持久化后门手法,也正是 slowmist 清单聚焦的
    攻击面。
    以上共新增 8 条规则(default_rules.json 从 49 条增至 57 条)+ 1 条既有规则的
    patch,用真实 policy.evaluate() 跑了 18 个正负测试用例全部通过验证。已有
    用户的 ~/.cc-monitor/rules.json 不会自动更新,需要手动同步。
  • 接入 6 个会话生命周期 hook:UserPromptSubmit/SessionStart/SessionEnd/
    PreCompact/Stop/SubagentStop——之前只接了 PreToolUse/PostToolUse/
    PermissionRequest,纯对话、没有触发任何工具调用的轮次完全没有审计留痕。
    这 6 个新 hook 全部是纯审计留痕,不参与 confirm/block(生命周期事件没有
    "允许/拒绝"语义):UserPromptSubmit 记录用户原始输入(唯一能看到"用户到底
    让 Claude 干了什么"的钩子);SessionStart/SessionEnd 记录会话的开始来源
    (startup/resume/clear/compact)和结束原因;PreCompact 在长会话被
    压缩前记一笔(避免审计细节随上下文压缩丢失);Stop/SubagentStop 记录主
    任务/子代理的结束。cc_monitor/hook.py 新增对应 6 个 handle_* 函数,
    bin/CC-Monitor-hook <mode> 新增 prompt/session_start/session_end/
    precompact/stop/subagent_stop 六种 mode;install.py 的 merge_hooks()
    改成用 extra_hooks 字典批量注册,新老用户重跑一遍 install.py/install.sh
    就会补上这几条(已装的 PreToolUse/PostToolUse/PermissionRequest 不动)。
    事件分别落到 source="hook_prompt"(UserPromptSubmit)和
    source="hook_lifecycle"(其余 5 个),cc_monitor/format.py 和
    webui/lib/format.js 同步加了对应的 TOOL_LABELS/STAGE_LABELS/describe()
    分支,Web UI 的 i18n.js 也补了中英文标签——首页"日志类型分布"、Log 审计页、
    终端 CC-Monitor tail 都能直接看到这些新事件,不需要额外改 UI 代码(沿用
    events 表已有的通用 source/tool_name/detail 结构)。用独立 CC_MONITOR_HOME
    测试目录端到端验证过 6 个 hook 的 stdin→SQLite 落库全过程,以及 Python/JS 两份
    describe() 输出完全一致。

English

Added

  • New detection for tampering with Claude Code's own config: editing
    ~/.claude/settings.json (or a project's .claude/settings.json/settings.local.json),
    scripts under .claude/hooks/, or CLAUDE.md had zero rule coverage before. This is the
    biggest gap found so far — rewriting the config to drop a hook registration is stealthier
    than kill -9-ing the probe process (no process needs to die; the application-layer
    monitoring just silently stops firing on the next tool call). Same "anti-bypass" goal as the
    existing kill_monitoring_process, just at the config layer instead of the process layer.
    Added claude_config_tamper (risk: high, action: confirm),
    tools: ["Write", "Edit", "NotebookEdit"], field: "file_path". confirm rather than
    block since adding a new hook to your own project or editing CLAUDE.md is a completely
    normal action. Inserted right after shell_rc_write.
  • Docker privileged/mount detection extended to Docker-socket mounts:
    docker_privileged_or_host_mount previously only recognized --privileged and -v /:/ —
    -v /var/run/docker.sock:/var/run/docker.sock, a more common and classic container-escape
    technique (mounting the host's Docker socket hands the container host-root-equivalent
    control), was a complete blind spot. Extended the regex with a docker.sock substring match,
    and bumped this rule's risk from medium to high (both --privileged and a docker.sock
    mount are host-root-equivalent risk, medium undersold it; action stays confirm to
    accommodate legitimate DinD/CI use cases).
  • New secret-format scanning on written content: every existing rule judged by file path or
    command text — Claude writing an API key into a file with no sensitive-looking name (e.g.
    config.py, notes.txt) triggered nothing at all. Added secret_pattern_in_write
    (risk: high, action: confirm), tools: ["Write", "Edit", "NotebookEdit"], using a new
    field: "content" to scan for private-key headers (-----BEGIN ... PRIVATE KEY-----), AWS
    access keys (AKIA/ASIA prefix), GitHub tokens (ghp_/gho_/ghu_/ghs_/ghr_/
    github_pat_), Anthropic/OpenAI keys (sk-ant-/sk-proj-/sk-), Slack tokens
    (xox[baprs]-), Google API keys (AIza), npm tokens (npm_), and Stripe live keys
    (sk_live_) — high-confidence, fixed-prefix formats. Added a
    "content": ["content", "new_string", "new_source"] mapping to FIELD_CANDIDATES in
    cc_monitor/policy.py — Write uses content, Edit uses new_string, NotebookEdit uses
    new_source, all three semantically "the content about to be written," so one rule needs to
    recognize all three field names, the same trick already used for file_path. Writes to paths
    already covered by sensitive_file_write (like .env/.ssh/) won't double-fire — rule
    ordering ensures the more specific path rule wins first. Limitation: this is a fixed-prefix,
    character-class-and-length match, not an entropy check, so a sufficiently long placeholder key
    in documentation (all xs, say) can false-positive — the same limitation every lightweight,
    non-entropy secret scanner (gitleaks/trufflehog's non-entropy patterns) shares; confirm
    rather than block leaves room for a human to wave off exactly that case.
  • New git-hooks/git-config persistence-attack-surface detection: core.hooksPath
    (redirecting git hooks to another directory), url.<url>.insteadOf (quietly swapping a
    dependency's source for an attacker-controlled repo — a real supply-chain technique), and
    writing directly into .git/hooks/ via Bash redirection all had zero coverage before — the
    same "plant a persistent backdoor" risk category as the existing crontab_persistence/
    systemd_persistence, but the git-ecosystem equivalent was a complete blind spot. Added two
    rules: git_hooks_persistence (risk: medium, action: confirm, tools: ["Bash"], covering
    the three command-line forms above) and git_hooks_file_write (same medium/confirm,
    tools: ["Write", "Edit", "NotebookEdit"], field: "file_path", covering writing into
    .git/hooks/ directly via the Write/Edit tool — a form the command-based rule can't see).
    git_hooks_persistence is inserted right after git_hard_reset_clean.
    All four items above were verified with 26 positive/negative test cases, plus 13 regression
    cases covering every pre-existing rule category, all calling cc_monitor/policy.py's real
    evaluate() directly (not a simplified regex re-implementation) — confirming the new rules
    match correctly and that adding the "content" mapping to FIELD_CANDIDATES didn't disturb
    how the existing command/file_path/url fields resolve. node --test still passes 5/5.
    As with prior rule additions, an existing user's ~/.cc-monitor/rules.json won't auto-update
    to pick these up.
  • Implemented the 5 rule ideas named in the slowmist-agent-security
    acknowledgment
    : credential_grep_scan (recursive grep -r/rg -r searches for
    password/secret/api_key/token-style keywords, medium/confirm); npx_pipx_ephemeral_run
    (npx/pnpm dlx/bunx/pipx run/uvx — one-shot execution of a remote package without a
    local install footprint, medium/confirm); proc_env_read (reading another process's
    /proc/<pid>/environ/cmdline — a classic cross-process credential-theft technique,
    high/confirm); browser_credential_read + browser_credential_read_bash (Chrome/
    Chromium/Brave/Edge/Firefox Cookies/Login Data/cookies.sqlite/logins.json/key4.db
    files, covering both the Read tool and Bash command-line access paths, high/confirm);
    dynamic_exec_in_write (eval(/exec(/os.system(/subprocess.*shell=True/
    new Function(/child_process.exec( appearing in written content — common in planted
    backdoors, but also extremely common in ordinary code, so deliberately kept at
    medium/log to avoid interrupting normal coding).
  • Added encoded-payload execution detection: encoded_payload_exec (base64 -d/
    xxd -r -p decoding piped into sh/bash/zsh/python3 — the most common bypass variant
    of curl_pipe_shell, same "fetch/construct something and hand it straight to an interpreter"
    pattern, just obfuscated past the literal curl/wget text match; high/block, treated
    the same as curl_pipe_shell).
  • Added SSH tunnel / reverse-proxy detection: ssh_tunnel_reverse_proxy (ssh -R/-D/
    -L tunnels, socat, chisel client/server; medium/confirm — plenty of legitimate
    uses like reaching an internal database, so not a block). Inserted right after
    reverse_shell_pattern; the more specific existing socat reverse-shell pattern still wins
    evaluation order when both could match.
  • Extended claude_config_tamper to cover MCP/Skills config too: previously only matched
    .claude/settings*.json/.claude/hooks//CLAUDE.md; now also covers project-level
    .mcp.json and the .claude/skills/ directory — planting a malicious MCP server config or
    a malicious skill definition is a more subtle persistence backdoor than editing hooks, and is
    exactly the attack surface the slowmist checklist is focused on.
    Together this is 8 new rules (default_rules.json grows from 49 to 57) plus one patch to an
    existing rule, verified with 18 positive/negative test cases against the real
    policy.evaluate(). As always, an existing user's ~/.cc-monitor/rules.json won't
    auto-update — resync manually to pick these up.
  • Wired up 6 session-lifecycle hooks: UserPromptSubmit/SessionStart/SessionEnd/
    PreCompact/Stop/SubagentStop — previously only PreToolUse/PostToolUse/
    PermissionRequest were wired, so a purely conversational turn that never called a tool left
    zero audit trail. All 6 new hooks are pure audit trail, never confirm/block (lifecycle
    events have no "allow/deny" semantics): UserPromptSubmit records the user's raw input (the
    only hook that can show what the user actually asked Claude to do); SessionStart/
    SessionEnd record how a session started (startup/resume/clear/compact) and why it
    ended; PreCompact records a marker right before a long session gets compacted (so audit
    detail doesn't silently vanish with the compaction); Stop/SubagentStop record when the
    main task or a subagent finishes. cc_monitor/hook.py gained 6 corresponding handle_*
    functions, and bin/CC-Monitor-hook <mode> gained prompt/session_start/session_end/
    precompact/stop/subagent_stop modes; install.py's merge_hooks() now takes an
    extra_hooks dict to register them in bulk — existing users just need to rerun
    install.py/install.sh to pick these up (the already-installed PreToolUse/
    PostToolUse/PermissionRequest entries are left untouched). Events land under
    source="hook_prompt" (UserPromptSubmit) and source="hook_lifecycle" (the other 5);
    cc_monitor/format.py and webui/lib/format.js got matching TOOL_LABELS/STAGE_LABELS/
    describe() branches, and i18n.js got the matching bilingual labels — the home page's
    "event type breakdown", the Log page, and terminal CC-Monitor tail all show these new
    events out of the box, no further UI wiring needed (they reuse the events table's existing
    generic source/tool_name/detail shape). Verified end-to-end against an isolated
    CC_MONITOR_HOME test directory (stdin → SQLite for all 6 hooks), and confirmed the Python
    and JS describe() implementations produce identical output.

完整更新日志见 CHANGELOG.md (English)。