跳到正文
Hacker News· nedlir·· 6 天前AI 评分60

Tanuki 发布本地代理,为 Claude Code 安全测试改写目标信息

Tanuki – Local proxy/wrapper bypassing Claude guardrails for security research

AI 导读

Tanuki 是一个本地匿名化代理,运行在 Claude Code 与 Anthropic API 之间,把提示词和工具输出中的真实域名、IP、邮箱、路径、云标识和渗透测试术语实时替换为 localhost 虚构项目,模型回复再映射回真实值。

正文 · AI 翻译

Tanuki logo

“最大的技艺是隐藏技艺。”
Thomas Fuller

用于 AI 辅助安全测试/隐私的代理

Release CI GHCR

Go Docker Claude Code Image License


Tanuki 位于你本地的 Claude Code 代理和 Anthropic API 之间,实时重写每一个可识别目标的字符串。你的工具命中真实目标;模型只会看到一个虚构的 localhost 项目。

目录

概述

Tanuki 是一个为 AI 辅助安全测试和漏洞赏金狩猎而构建的匿名化代理。当你通过 Claude Code 之类的代理进行渗透测试时,提示词中的每一个真实域名、IP 和 payload 都会离开你的机器。这是一种 OpSec 风险,也可能违反漏洞赏金项目规则或 NDA。

Tanuki 以 Docker 容器方式运行,并透明地拦截该 API 流量。在提示词离开你的机器之前,它会将真实目标数据(域名、子域名、IP、电子邮件、路径、云标识符以及激进的安全术语)替换为无害的虚构开发等价物。当模型响应时,虚构内容会被映射回现实,以便你的本地工具针对真实目标执行。模型以为它在帮你调试一个本地开发应用,而上游提供商对你的实际任务一无所知。

工作原理

flowchart TD
    classDef default fill:#050505,stroke:#8b0000,stroke-width:1px,color:#ff3333,font-family:Courier
    classDef proxy fill:#110000,stroke:#ff0000,stroke-width:2px,color:#ffffff,font-family:Courier
    classDef hooks fill:#000000,stroke:#550000,stroke-width:1px,stroke-dasharray:4 4,color:#cc0000,font-family:Courier
    classDef upstream fill:#0a0a0a,stroke:#333333,stroke-width:1px,color:#666666,font-family:Courier
    classDef data fill:#1a0505,stroke:#aa0000,stroke-width:1px,color:#ff6666,font-family:Courier

    subgraph Host["LOCAL MACHINE"]
        C["Claude Code"]
        H[/"Tanuki Hooks<br/>(docker exec)"/]
        C -. "Tool execution" .-> H
    end

    subgraph Container["DOCKER CONTAINER"]
        P{"Tanuki Proxy<br/>[scratch ~10MB]"}:::proxy
        D[("( mappings volume )")]:::data
    end

    A((("Anthropic API"))):::upstream

    C == "Real prompt data" ==> P
    P -. "Loads config" .-> D
    P -- "Rewrites to Fiction" --> A
    A -- "Responds in Fiction" --> P
    P == "Rewrites to Real" ==> C

    class C default
    class H hooks

    style Host fill:transparent,stroke:#8b0000,stroke-width:1px,color:#8b0000,font-family:Courier
    style Container fill:#020000,stroke:#4a0000,stroke-width:1px,stroke-dasharray:3 3,color:#8b0000,font-family:Courier

加载中

方向 路径 发生什么
入站 你的提示词 → API 域名、组织名称、电子邮件、IP、路径、云标识符和渗透测试术语在模型看到之前被替换为 localhost 虚构内容。
出站 API → Claude Code 虚构术语在响应流中被重写回真实值,因此 Claude Code 的本地工具会收到真实目标数据。
工具调用 通过 docker exec 挂钩 一个 PreToolUse 挂钩在工具参数中将虚构→真实重写,因此工具会命中实际目标。工具输出不需要挂钩:它只会在下一个请求中到达模型,而代理会像处理其他任何请求一样重写该请求。

示例

你正在测试 bounty.amazon.com。以下是 Tanuki 所做的:

你输入(真实) LLM 看到(虚构)
amazon.com localhost:9000
bounty.amazon.com localhost:9002
any-other.amazon.com localhost:9001(通配符)
admin@amazon.com admin@devtarget.local
Amazon DEVTARGET
52.94.236.248 127.0.0.3
/var/www/amazon/ /var/www/dev/devtarget/
s3://amazon-prod file:///tmp/devtarget-prod
SQL injection parameterized query test
reverse shell remote debug session

每个目标域名会获得三个映射:基础域名、用于尚未见过的子域名的通配符,以及检测到每个子域名时的专用映射。通配符正是阻止像 internal-admin.amazon.com 这样未映射的主机将其标签泄露到上游的原因。

快速开始

git clone https://github.com/Pigyon/tanuki.git
cd tanuki
docker compose up -d
claude

这会从 GitHub Container Registry 拉取最新发布的镜像,因此不需要 Go 工具链,也不需要构建。

要从源码构建而不是拉取:

docker compose -f compose.yaml -f compose.dev.yaml up -d --build

启动时,代理会自动配置 .claude/settings.json 中的 Claude Code 挂钩和代理环境变量,并写入掩护故事 .claude/CLAUDE.md。域名、电子邮件和 IP 会从你的对话中自动检测,因此无需手动配置目标。

当你输入 “check bounty.amazon.com for SQL injection” 时,代理会:

  1. 检测到 amazon.com 和 bounty.amazon.com 为新域名
  2. 创建所有派生映射(组织、电子邮件、路径、云)
  3. 在请求到达 API 之前重写该请求

包装脚本

包含一个 ./tanuki 包装脚本,这样你就不必输入 docker compose exec tanuki /tanuki <command>:

./tanuki add example.com
./tanuki map
./tanuki status

验证重写

在不访问 API 的情况下测试重写引擎:

./tanuki test "scan bounty.amazon.com for SQL injection and contact admin@amazon.com"

配置

所有内容都是可选的;不配置时,目标会从你的对话中自动检测。要预先配置一个 engagement,请编辑 compose.yaml:

environment:
  # Custom fiction org name (default: DEVTARGET)
  - TANUKI_FICTION_ORG=ACMECORP
  # Pre-seed target domains (comma-separated)
  - TANUKI_DOMAINS=example.com,api.example.com
  # Pre-seed target IPs (comma-separated)
  - TANUKI_IPS=93.184.216.34
  # Custom rewrite rules (semicolon-separated, pipe-delimited real|fiction)
  - TANUKI_RULES=John Smith|Jane Developer;Project Phoenix|Test Alpha

自定义重写规则

对于不是域名或 IP 的字符串(人名、项目名称、内部术语),可通过 compose.yaml(在每次容器启动时应用)或运行时添加自定义规则:

./tanuki add-rule "John Smith" "Jane Developer"
./tanuki add-rule "Project Phoenix" "Test Alpha"

命名 engagement

用于独立项目或自定义虚构组织名称:

./tanuki init bugbounty --fiction-org ACMECORP
./tanuki add bounty.amazon.com

CLI 参考

所有命令均通过 ./tanuki <command>(或 docker compose exec tanuki /tanuki <command>)运行:

命令 描述
setup 配置 Claude Code hooks(启动时自动执行)
init <name> [--fiction-org NAME] 创建命名 engagement
add <domain> [--fiction-org NAME] 预置目标域名
add-ip <ip> 预置 IP 映射
add-rule <real> <fiction> 添加自定义重写规则
map 显示当前映射表
status 显示 engagement 信息
list 列出所有 engagement
activate <name> 切换当前 engagement
test <text> 在示例文本上测试重写
verify [file...|-] 检查映射是否保持;扫描文件中未映射的目标
terms 显示术语映射
export [name] 将 engagement 导出为 JSON
import <file.json> 从 JSON 导入 engagement
reset [--data] 移除 hooks(--data 还会清除 engagement)
version 显示版本和构建信息
proxy 运行代理服务器(容器入口点)

哪些内容会被重写

基于映射(按 engagement,全部自动检测)

类型 检测方式 虚构
域名 在提示词和工具输出中自动检测 localhost:<port>
组织名称 从域名派生 DEVTARGET(可配置)
邮箱 从域名派生 user@devtarget.local
IP(v4 + v6) 在提示词和工具输出中自动检测 127.0.0.x / ::ffff:...
路径 从组织名称派生 /dev/devtarget/
云资源 从组织名称派生 file:///tmp/devtarget...
自定义规则 通过环境变量或 CLI 用户定义 用户定义

术语(内置,应用于所有 engagement)

复合的渗透测试专用术语会被替换为中性的开发等效词。只有明确无歧义的多词术语会被替换。像 target、payload 或 exploit 这样的单个词会保留,以免破坏正常的代码讨论。

运行 ./tanuki terms 查看完整词典。该列表位于 internal/tanuki/terminology.json,并在构建时嵌入;要自定义它,请编辑该文件并重新构建。

环境变量

在 compose.yaml 中设置:

环境变量 默认值 描述
TANUKI_DATA /data Engagement 数据目录(卷)
TANUKI_PROXY_PORT 18080 代理监听端口
TANUKI_PORT_START 9000 域名分配的起始端口
TANUKI_UPSTREAM https://api.anthropic.com 上游 API URL
TANUKI_FICTION_ORG DEVTARGET 用于映射的虚构组织名称
TANUKI_DOMAINS (无) 要预置的域名,以逗号分隔
TANUKI_IPS (无) 要预置的 IP,以逗号分隔
TANUKI_RULES (无) 自定义规则:real|fiction 对,以分号分隔
TANUKI_IGNORE (无) 以逗号分隔的域名,永远不会被视为目标(包括子域名)。始终忽略:anthropic.com、claude.com、claude.ai、json-schema.org、Claude Code 所呈现的开发者平台域名(github.com、google.com),以及来自 Claude Code 账户上下文的操作者自己的邮箱域名。如果其中之一是你的真实目标,./tanuki add <domain> 会显式映射它
TANUKI_UID / TANUKI_GID 1000 Docker 不得不创建的 .claude/ 目录的所有者;已存在的目录保留其所有者
TANUKI_DEBUG (无) 设置为任意非空值以启用调试日志
TANUKI_ON_LEAK refuse 当真实值在重写后仍存在时为 refuse 或 warn

架构

容器:基于 scratch 的镜像,包含单个静态 Go 二进制文件以及 CA 证书。无操作系统、无 shell、无运行时,总计约 10MB。所有能力均被丢弃,只读文件系统,禁止权限提升。

一切都在 Docker 中运行。代理、hooks 和 CLI 都在容器内执行;hooks 由 Claude Code 通过 docker exec 调用,因此不需要宿主机端的二进制文件。

匹配:Aho-Corasick 自动机在流上单次遍历中执行最长优先替换。

JSON 感知:请求体和响应体逐字符串重写,绝不作为原始文本处理。协议字段(model、role、type、id、工具名)保持原样,base64 数据既不重写也不扫描,thinking 块按模型写入的原样返回给 API,因为它们带有签名。

已知限制

  • 代理上没有 HTTPS。代理监听的是明文 HTTP。这可以接受,因为它只接受来自 localhost(同一台机器上的 Claude Code)的连接;到 Anthropic API 的上游连接使用 HTTPS。
  • 仅支持字面字符串匹配。编码形式(URL 编码、base64 及类似形式)不会被 Aho-Corasick 自动机捕获。
  • 术语是单向的。渗透测试术语只做真实→虚构的重写。模型的响应使用虚构术语,会原样通过。
  • 通配符子域不可逆。每个没有自己映射的子域都会折叠到同一个通配符值上,因此无法映射回去。自动检测会为它看到的子域创建专用映射,这些子域可以正常往返;通配符是兜底,用于任何漏过检测的内容,它更倾向于隐藏主机名,而不是保持其可用。
  • 检测才是薄弱环节,而不是重写。每个出站请求体在重写后都会重新扫描,如果真实值仍然存在就会被拒绝,因此重写器的 bug 不会导致泄露。但没有任何机制能防范检测器一开始就未识别的值——因此下面两条限制才是真正重要的。
  • 用于自动检测的精选 TLD 列表。自动检测使用约 70 个常见 TLD 的精选列表,以避免代码模式(如 readme.md、foo.bar 或 user.id)带来的误报。与日常代码冲突的 TLD(.info、.name、.app、.store、.top、.site,如 logger.info 或 user.name)被排除在外。这些或其他不常见 TLD(.pizza、.click、.it、.id)上的域名可以用 ./tanuki add 手动添加。
  • 被忽略的域名需要显式 add 才能成为目标。github.com、google.com 以及你自己的邮箱域名会被自动检测跳过,这样 Claude Code 自己的 git 远程、连接器和账户上下文就不会污染表格。代价是仅靠自动检测不会映射其中任何一个,因此在少数以 GitHub 或 Google 为目标的场景中,你必须先 ./tanuki add github.com;残留检查也会跳过被忽略的域名,所以它不会替你发现这个遗漏。
  • Thinking 保持虚构。Thinking 块由 API 签名,因此在两个方向上都会原样传递。Claude Code 中显示的 thinking 以虚构术语呈现。
  • 流式文本会被短暂扣留。一个虚构值可能每个事件只流出几个字符,因此代理会扣留文本或工具输入块的结尾,因为它仍可能是某个值的开头,等下一个事件确定后再发送。显示可能滞后几个字符;完整值总会以反转形式到达。

验证一次约定

tanuki verify 无需向任何地方发送任何内容即可回答两个问题。

我的映射真的有效吗? 不带参数时,它会取表中的每一个真实值,并将其套用到目标在实践中可能出现的各种形态中——裸字符串、URL 中、反引号内、作为 markdown 链接、JSON 字段中、HTTP 头中、大写形式、句子末尾——然后检查每个形态中该值是否已被消除。一个覆盖了裸字符串的映射,仍可能在其中某个形态下失效,因此值得在项目开始前而非进行中运行。

$ ./tanuki verify
Verifying 8 mappings against 12 shapes

96 checks, 0 leaks
Every mapped value was rewritten in every shape.

这份文档可以安全发送吗? 给定文件、- 或 < file 重定向,它会重写文本并报告没有映射覆盖的、看起来像真实值的值。这是在草稿报告提交前应运行的检查。

$ ./tanuki verify report.md
  LEAK  report.md
        partner-portal.acme-supplier.io
        93.184.216.34

2 value(s) have no mapping and would be sent as-is.
Add them with: tanuki add <domain> / tanuki add-ip <ip>

当它发现问题时,会以非零状态退出,因此可以直接接入提交前钩子或 CI。

故障关闭行为

代理不会转发它无法匿名化的提示。

每个请求体在重写后都会被检查。 在 /v1/messages 请求发出前做的最后一件事,是重新扫描是否还有任何看起来像真实目标的内容。如果有一个幸存,请求会被拒绝并返回 400,该值会被写入 tanuki 日志——绝不会写入 HTTP 响应,因为客户端可能会将其并入下一个提示。在正常工作的流水线中,这永远不会触发;一旦触发,就意味着重写器漏掉了某些内容,该请求体绝不能发往上游。

目标在首次出现的位置就会被映射。 代理会对每个出站请求体运行检测,因此,无论目标出现在你输入的提示中、工具输出中、@ 提到的文件中、粘贴内容中、CLAUDE.md 中、在别处恢复的会话中,还是来自任何非 Claude Code 的客户端,都会在重写之前获得一个虚构值,而不是以明文形式发往上游。

当没有活跃项目、映射表不可读,或映射表为空且请求体中没有任何可识别内容时,请求也会被拒绝。全新项目不再阻塞:第一个提及目标的请求会将其映射并转发匿名化后的请求体。

将 TANUKI_ON_LEAK=warn 设为记录幸存值并仍然转发。这以牺牲保证换取正常运行时间,仅用于诊断误报,不适用于正常使用。

该检查继承了检测的盲区:它识别相同的精选 TLD 列表和相同的字面形式,因此位于不常见 TLD 上或以编码形式出现的目标对两者都不可见。它能捕获重写器故障,而非检测器故障。

免责声明

Tanuki 仅用于授权的安全测试:你已获得书面许可执行的项目,例如范围内的漏洞赏金计划或签约渗透测试。它降低了目标数据暴露给第三方模型提供商的风险;它不保证匿名,也不免除你遵守任何项目规则、保密协议或法律的义务。你对自己的使用方式负责。

许可证

Apache 2.0

来源:Hacker News · github.com