我们追踪了35天的AI密钥窃取活动。以下是我们了解到的情况
为了发现会发生什么,我们创建了两个蜜罐LLM网关:一个镜像LiteLLM,另一个镜像通用的OpenAI兼容端点。然后我们继续创建了40多个独立组织,每个组织至少有一个GitHub仓库,方式对威胁行为者具有说服力。API密钥连同网关地址被添加到这些仓库中,方式看起来像是AI或人为错误误加的。由此我们观察到超过300万次请求发往我们的网关,其中大多数是探测攻击,其中一些最终以我们认定为转售商/
我们追踪了 35 天的 AI 密钥窃取。以下是我们了解到的情况
在早期批次中,有一个来源在每次发布后一分钟内检查了 37 个密钥。这些是模型列表请求。整个批次只产生了七个提示请求,全部来自另一个重复同一消息的来源。广泛收集并没有导致其发现的每个密钥都被持续使用。
两个 utility 批次让我们能够固定若干变量。全部 80 个项目都是 Python 工具,宣传 gpt-6-sol,并且在 config/development.env 中的网关 URL 旁边包含一个密钥。然而只有 17 个密钥被检查,五个被用于提示。其余 63 个在超过四天的观察期间从未出现在我们的网关日志中。
用于提示的四个 New York utility 密钥来自 api-response-code-map、http-header-safety-check、pagination-field-check 和 operation-id-audit。San Francisco 的密钥来自 cors-wildcard-credentials-check。它们都是与 API 相关的工具。这些项目在发布顺序和组织方式上不同,因此我们无法判断是否是它们的主题内容驱动了选择。
文件格式可能也有影响。早期批次中七个经过提示测试的密钥里,有六个来自使用 JSON 配置的 Go 项目;一个来自 Node 项目,而其 14 个 Python 项目一个都没有。全部七个提示请求都来自同一个来源。语言和文件格式同时变化,因此我们无法判断是哪一个影响了该来源的选择。
我们还尝试了其他泄露位置。一个 GitHub Gist 密钥收到了八个提示请求。两个文档页面和两个可下载的起始归档中的密钥在大约六天内没有收到任何网关请求。提交历史中暴露的六个密钥中有一个被检查并用于提示尝试。这些小群体显示了哪些暴露面产生了可观察的使用,但无法说明哪些被扫描过。
最新的密钥被找到的时间更少:19 个公开时间不到一天。但有 91 个密钥至少暴露了三天却没有请求。所有 64 个我们能记录首次请求时间的密钥都在发布后六小时内被检查。我们没有保留仓库爬取或下载遥测数据,因此无法区分从未被收集的密钥和被收集后忽略的密钥。
一旦少数密钥被采用,它们的重复使用就主导了流量。六个密钥占使用我们泄露凭据的提示请求的 98.9%。这些密钥的重复使用推动了请求量。
使用其他凭据的请求
大多数捕获的提示背后的算术问题
请求可以把任意字符串作为其 API 密钥。即使身份验证失败,我们也会记录这些尝试,因此保存的提示不一定意味着发送者拥有可用的密钥。许多攻击者尝试暴力破解 100 多个无效的管理密钥。这与 LiteLLM 的一个原始漏洞有关,其中许多网关附带默认管理密钥
重复的凭据测试看起来是什么样
提示请求携带了 93,100 个不在我们发布记录中的非空凭据值。其中,78,130 个长度只有 1–16 个字符。发布匹配之外的提示请求中有 93.7% 返回 HTTP 401。几乎所有这些尝试都带有 curl 标记的标头。
最清晰的重复模式是这条消息:
What is 483*927+1145? Answer with only the final number.
我们在 92,596 个提供的凭据值和 20 个请求的模型名称中捕获了 1,401,673 份副本。这些值没有一个与我们记录在案的泄露匹配。在更改令牌和模型的同时重放固定问题,与自动化凭据和模型测试一致。
我们泄露密钥的重复使用
一次密钥检查描述了中继如何收集 API 密钥
这个来源使用我们泄露的密钥发出了 1,215 个提示请求。在这些流量中,我们记录了一条完全相同的消息的 21 份副本。我们强调它,是因为它描述了中继如何收集和管理 API 密钥。它开头是:
“中继池维护着一个小型 SQLite 数据库,里面存储着从公共渠道收集的 API 密钥。”
文章标题:我们追踪了 35 天的 AI 密钥盗用情况。以下是我们了解到的情况
正文: 该 prompt 描述了一个“中继池”:由网关管理密钥和 API 连接。系统会每小时重试一次已达到配额的密钥,每六小时测试所有密钥,并每天更新网关的路由。它会根据吞吐量、响应时间和账户余额来选择连接,并且可以按聊天标题屏蔽采集来源。
在该描述之后,对模型的唯一指令是 Ответь одним словом: OK——“用一个词回答:OK。”这些请求的 user-agent 头中还带有软件标签 keypool。
在这些请求中,有 12 个返回 HTTP 200,7 个返回 429,2 个返回 404。
这是提交的 prompt 中所描述的系统;我们没有检查采集器的代码。
网关连接
顺着流量追踪到一个 API 中继
my.git.christmas 解析到一个正在向我们的 API 发送 prompt 的 IP。来自该 IP 的请求也提到了这个域名。Veridrop 将其列为兼容 OpenAI 的 API 中继。
来自同一 IP 的请求提到了一个 fo2api 部署。DNS 记录和 prompt 共同将该来源与中继基础设施联系起来,尽管它们并未告诉我们其背后有多少用户。
| 观察项 | 证据 |
|---|---|
| 发往我们 API 的请求 | 618 个 HTTP 成功的 prompt 请求使用了我们公开的密钥。 |
| DNS 匹配 | 我们检查时,my.git.christmas 解析到的地址与这些请求中记录的地址相同。 |
| 提交的上下文 | 请求提到了该域名以及 fo2api 部署相关工作。 |
较旧的日志保留了转发的源地址,但没有单独的 socket 对端记录。DNS 和上下文匹配支持这一关联;它们并不能识别出运营者。
中继开发与测试
通过我们的网关排查一个中继的问题
发送方还使用我们的网关来排查 fo2api 的问题。其中一个 prompt 要求一个编码 agent 修复一个影响 ChatCompletions、Responses 和 Anthropic 路由的生产环境 JSON tool-schema 错误,并指出要检查的特定后端文件。
从聊天导出到网关的密钥池
更早的轮次要求该 agent 检查聊天导出中的密钥,找出可用于 StepFun 模型的密钥,并将可用的密钥添加到一个基于 Sub2API 的网关中。NaraRouter 的端点出现在该导出内容和测试输出中。一次生成探测返回 402,随后的一次账户列表将该连接标记为错误。
用户随后提供了一个 OpenLux 端点和密钥,要求该 agent 检查它并将其添加到 API 中。接下来的工具输出包括模型测试、重试和路由映射。稍后的列表显示 OpenLux 账户为活跃状态。
接下来是一个 APIMart 密钥,以及一个对 auto-glm、auto-kimi、auto-qwen 和 auto-stepfun 的请求:这些模型名称会在每个系列中最佳可用模型之间路由请求。APIMart 测试输出报告了因余额不足导致的 402 错误,其账户被列为错误。
来自另一个 IP 的独立历史记录包含 CLIProxyAPI README。该任务是将一个编码 agent 配置为通过该代理发送 service_tier: "fast"。OpenLux 和 APIMart 也出现在该 README 的赞助商列表中;这些链接与文档查询只是偶然相关。
这些是提交的历史记录中的指令和工具结果,而不是那些提供商对我们网关的调用。
Telegram 机器人流量
Telegram 历史记录揭示了关于这些流量的什么
Telegram 消息元数据和更早的回复出现在来自三个已记录源地址的 88 个捕获请求中,其中包括与 my.git.christmas 关联的来源。
这些地址中的另一个位于赫尔辛基,属于 Xorek 标记的托管服务,位于 AEZA 网络中。它发送了 31 个包含 Telegram 对话元数据的捕获请求。提交的历史记录包括 12 个被标记为非机器人账户的不同发送者 ID。这与托管部分讨论的是同一来源;该位置描述的是其服务器,而不是对话中的人。
回复历史中突出显示了名为 @obed2021bot 的 Telegram 机器人,它在全部三个来源中具有相同的账号 ID。其公开资料中名称为“Обед”,并宣传 GPT 6 Astra Max,以及向任何拥有访问权限的人提供免费访问。该机器人请求中的消息元数据还包含两个字段:guest_query_id 和 guest_bot_caller_user:这是 Telegram 用于访客模式机器人交互的字段。这些字段提供了足够证据,使我们能够说 Telegram 对话正通过推理网关传递到我们的系统。
在移除重复的消息元数据后,我们发现了 29 个被标记为非机器人账号的不同发送者 ID。它们出现在 16 个私聊和 10 个超级群组中。这些是对话历史中的 ID,不是经过验证的用户。我们已省略个人标识符。
{
"format": "telegram_message_context/v1",
"message": {
"reply_to_message": {
"from": {
"is_bot": true,
"username": "obed2021bot"
}
}
}
}
我们无法核实谁运营该机器人,也无法核实我们的回复是否到达 Telegram。
浏览器测试
从浏览器测试我们泄露的密钥
使用我们已发布密钥的 6 个请求带有 www.hvoyai.com 的 referrer。每个都返回 HTTP 200,并使用了 Hvoy 当前测试器中的默认消息:
“计算 17 + 26。只回复数字。”
该测试器接收端点、密钥和模型名称,然后直接从访问者的浏览器发送请求。匹配的提示和浏览器标头指向访问者侧测试,而不是来自 Hvoy 服务器的请求。我们检查了当前实现;我们没有历史副本。
追踪测试请求
一次浏览器测试指向了 VPN 基础设施
来自 Hvoy 测试背后 IP 的其他请求包含维护 updates.listokdoma.ru 的指令。当我们检查 DNS 时,该域名及相关子域名指向同一个 IP。这将记录到的地址与提示中的 VPN 配置关联起来;并没有将 Hvoy 与 VPN 关联起来。
| 线索 | 记录到的证据 |
|---|---|
| 域名匹配 | 我们检查时,listokdoma.ru、manage.listokdoma.ru 和 updates.listokdoma.ru 解析到记录到的 IP。 |
| VPN 维护 | 7 个捕获到的请求包含系统上下文,其中将 updates.listokdoma.ru 与 Xray/3x-ui VPN 设置和 Nginx 反向
| 网络 · 出口国家 | 请求数 · 占比 | 请求内容 | 软件指纹 |
|---|---|---|---|
| PLAY2GO INTERNATIONAL LIMITED AS215439 芬兰、瑞典、荷兰 | 2,656 占总量 28.3% | 1,897 条短 prompt / 存活检查 584 条编程/agent 特征 53 条 Telegram 历史记录 6 条中继维护上下文 | Sub2API 1,575 claude-cli 336 Python urllib/urllib3 230 OpenCode 107 其他请求头 · 408 其他请求头 120 curl 67 OpenAI Python SDK 57 缺失请求头 55 Bun 31 OpenAI SDK 26 浏览器请求头 24 OkHttp 7 Python aiohttp 6 Python requests 6 Go HTTP client 4 Node undici 4 Python HTTPX 1 |
| AEZA INTERNATIONAL LTD AS210644 芬兰 | 2,208 占总量 23.6% | 1,088 条短 prompt / 存活检查 963 条编程/agent 特征 31 条 Telegram 历史记录 | claude-cli 867 Sub2API 764 Python urllib/urllib3 347 OpenCode 55 其他请求头 · 175 curl 65 缺失请求头 31 Python aiohttp 30 其他请求头 20 OkHttp 17 浏览器请求头 6 Bun 3 Python HTTPX 2 Node undici 1 |
| Liteserver AS60404 荷兰 | 1,215 占总量 13.0% | 457 条短 prompt / 存活检查 21 份密钥收集描述的副本 52 条身份 / 限额问题 | keypool 1,025 Go HTTP client 181 Python urllib/urllib3 9 |
| Hetzner Online GmbH AS24940 德国 | 972 占总量 10.4% | 662 条短 prompt / 存活检查 16 条编程/agent 特征 315 条精确回复 ok 的检查 | Zoltraak 591 Go HTTP client 381 |
| JSC Selectel AS50340 俄罗斯 | 521 占总量 5.6% | 521 条 ping 检查 | Python urllib/urllib3 521 |
| HOSTSLIM OU AS207083 爱沙尼亚 | 228 占总量 2.4% | 140 条短 prompt / 存活检查 15 条身份 / 限额问题 11 条推理 / 算术 prompt | 浏览器请求头 117 claude-cli 50 PowerShell 18 其他请求头 · 43 Python HTTPX 18 Python urllib/urllib3 13 Python requests 12 |
| Blockchain Creek B.V. AS207994 荷兰 | 201 占总量 2.1% | 201 条 hi 检查 | FelixKeyBot 201 |
| U1 DIGITAL SERVICES LTD AS213877 芬兰、德国 | 165 占总量 1.8% | 103 条短 prompt / 存活检查 14 条编程/agent 特征 17 条推理 / 算术 prompt | 浏览器请求头 93 Python HTTPX 27 Python urllib/urllib3 22 Oh My Pi 10 其他请求头 · 13 其他请求头 5 缺失请求头 4 OpenAI Python SDK 4 |
- 1,897 条短 prompt / 存活检查
- 584 条编程/agent 特征
- 53 条 Telegram 历史记录
- 6 条中继维护上下文
- Sub2API 1,575
- claude-cli 336
- Python urllib/urllib3 230
- OpenCode 107
- 其他请求头 120
- OpenAI Python SDK 57
- 缺失请求头 55
- OpenAI SDK 26
- 浏览器请求头 24
- Python aiohttp 6
- Python requests 6
- Go HTTP client 4
- Node undici 4
- Python HTTPX 1
- 1,088 条短 prompt / 存活检查
- 963 条编程/agent 特征
- 31 条 Telegram 历史记录
- claude-cli 867
- Python urllib/urllib3 347
- 缺失请求头 31
- Python aiohttp 30
- 其他请求头 20
- 浏览器请求头 6
- Python HTTPX 2
- Node undici 1
- 457 条短 prompt / 存活检查
- 21 份密钥收集描述的副本
- 52 条身份 / 限额问题
- keypool 1,025
- Go HTTP client 181
- Python urllib/urllib3 9
- 662 条短 prompt / 存活检查
- 16 条编程/agent 特征
- 315 条精确回复 ok 的检查
- Zoltraak 591
- Go HTTP client 381
- 521 条 ping 检查
- Python urllib/urllib3 521
- 140 条短 prompt / 存活检查
- 15 条身份 / 限额问题
- 11 条推理 / 算术 prompt
- 浏览器请求头 117
- claude-cli 50
- PowerShell 18
- Python HTTPX 18
- Python urllib/urllib3 13
- Python requests 12
- 201 条 hi 检查
- FelixKeyBot 201
- 103 条短 prompt / 存活检查
- 14 条编程/agent 特征
- 17 条推理 / 算术 prompt
- 浏览器请求头 93
- Python HTTPX 27
- Python urllib/urllib3 22
- 其他请求头 5
- 缺失请求头 4
- OpenAI Python SDK 4
占比基于全部 9,375 条泄露密钥的 prompt 请求计算。活动计数描述的是捕获到的输入,包含被拒绝的请求,且可能相互重叠。软件指纹为发送方自报的请求头标签。
HTTP 2xx 统计的是成功的请求状态码。ASN 持有者、托管品牌以及使用其服务的人是各自独立的身份。
| 记录的网络 | 出口位置 | 提示请求 | HTTP 2xx |
|---|---|---|---|
| PLAY2GO INTERNATIONAL LIMITED AS215439 | 芬兰、瑞典、荷兰 | 2,656 | 674 |
| AEZA INTERNATIONAL LTD AS210644 | 芬兰 | 2,208 | 831 |
| Liteserver AS60404 | 荷兰 | 1,215 | 886 |
| Hetzner Online GmbH AS24940 | 德国 | 972 | 689 |
| JSC Selectel AS50340 | 俄罗斯 | 521 | 405 |
| HOSTSLIM OU AS207083 | 爱沙尼亚 | 228 | 165 |
| Blockchain Creek B.V. AS207994 | 荷兰 | 201 | 199 |
| U1 DIGITAL SERVICES LTD AS213877 | 芬兰、德国 | 165 | 142 |
| Verizon Business AS5650 | 美国 | 156 | 27 |
| AS203087 AS203087 | 哈萨克斯坦 | 151 | 105 |
| SERV.HOST GROUP LTD AS207957 | 波兰 | 137 | 0 |
| SMARTNET Germany GmbH AS203446 | 法国 | 130 | 107 |
| OOO Post ltd AS12494 | 俄罗斯 | 129 | 84 |
| DpkgSoft International Limited AS215590 | 芬兰 | 86 | 37 |
| ZetServers AS25198 | 法国、德国 | 50 | 49 |
| IT7 Networks Inc AS25820 | 美国 | 48 | 43 |
| MSS LLC. AS44843 | 俄罗斯 | 35 | 35 |
| ETERNITY INTERNATIONAL LIMITED AS212743 | 爱沙尼亚、德国、荷兰 | 34 | 32 |
| Eons Data Communications Limited AS138997 | 日本 | 20 | 19 |
| Amazon.com, Inc. AS16509 | 日本 | 18 | 5 |
| M247 Europe SRL AS9009 | 比利时、英国 | 17 | 17 |
| Cloudflare, Inc. AS13335 | 爱沙尼亚、德国、美国 | 15 | 9 |
| Datacamp Limited AS212238 | 日本 | 15 | 8 |
| GTHost AS63023 | 瑞士 | 15 | 15 |
| Baxet Group Inc. AS26383 | 美国 | 13 | 4 |
| ISPCONNECTED CORP AS398712 | 美国 | 13 | 13 |
| PJSC Rostelecom AS12389 | 俄罗斯 | 12 | 12 |
| MTS PJSC AS13055 | 俄罗斯 | 12 | 12 |
| Black Mesa Corporation AS46997 | 美国 | 12 | 0 |
| OneAsiaHost AS59211 | 新加坡 | 10 | 10 |
总计为 9,375 次提示请求和 4,711 次 HTTP 成功。这些是缓存的源网络观测数据,包含接入网络和代理;它们并非 53 个已识别的攻击者或托管企业。
欧洲服务器,俄罗斯支付选项
PLAY2GO 和 Xorek 都在宣传可通过俄罗斯支付系统购买的托管服务。两者记录在案的来源合计占用了使用我们泄露密钥的 4,950 次请求(52.8%)。这只归并了它们的支付渠道,而非所有权。
PLAY2GO 标有卢布和欧元价格,其公开频道宣传支持俄罗斯银行卡和 SBP 支付。其当前网站列出的是一家迪拜公司 PLAY2GO - FZCO。我们 ASN 记录中的名称 PLAY2GO International 的英国备案列有一名乌克兰籍控股股东。
Xorek 提供莫斯科和欧洲的服务器位置,以卢布计价,支持 MIR/SBP 支付。其网站标明运营方为 DpkgSoft International;该公司的英国备案列有一名持股至少 75% 的俄罗斯居民。
Telegram 相关流量背后的受制裁网络
其中一个来源从地理定位在芬兰的地址连接,位于美国制裁的托管公司 AEZA International 的网络上。标注为 Xorek 的流量横跨两个 ASN:2,208 次请求位于 AEZA International 的 AS210644,86 次位于 DpkgSoft 的 AS215590。美国财政部称其为总部位于俄罗斯的 AEZA Group 的英国分支。
这是一条有据可查的与受制裁网络的连接。我们的日志并未显示这些服务器由谁付费,也无法证明 PLAY2GO 或 Xorek 存在规避制裁行为。
Rostelecom 的 AS12389 也出现在日志中,有 12 次使用泄露密钥的提示请求,全部返回 HTTP 成功。其在 OFAC 名单上的限制范围较窄:第 3 号指令禁止美国人交易期限超过 14 天的特定新债务或新股本。AEZA 的 SDN 认定范围更广:其受美国管辖的财产被冻结,美国人通常被禁止与该公司交易。
一半的提示流量来自两个芬兰地址
文章标题:我们追踪了 35 天的 AI 密钥窃取行为。以下是我们了解到的情况
正文: 两个已记录地址经地理位置定位位于芬兰,共发送了 4,749 次请求——占泄露密钥提示流量的 51%。其中一个地址关联到 PLAY2GO 网络上的 my.git.christmas。另一个位于 Xorek 标记的托管服务上,并且还发送了上文所述的 Telegram 对话历史。
较大的 Xorek 来源以 23 个模型名称发送了 2,208 次请求。它混合了简短检查和较长请求:其中 830 次包含编码代理系统指令。因此,该网络地址同时涵盖了自动检查和代理活动;其芬兰位置并不能告诉我们用户在哪里。
相同的检查在 Xorek 和 Hetzner 上运行
两个已记录地址反复发送 Reply exactly: ok:来自 Xorek 标记托管服务的 67 次请求,以及来自 Hetzner 的 315 次请求。全部 382 次请求都带有 Zoltraak 标记的请求头,包含一条用户消息、无流式传输,并且只请求五个或八个输出 token。
这些检查覆盖了 11 个模型名称和 6 个泄露密钥。Hetzner 地址将它们发送到了两个蜜罐。匹配的提示、请求头和请求设置表明它们共享检查软件;但这并不能证明这两个地址具有相同的操作者。
网关能力测试
5,000 次请求,以查明我们的网关能做什么
一台经地理位置定位位于纽约州布法罗、托管在 ColoCrossing 上的服务器不断要求我们的网关编辑一个 Python 文件、识别一只猫,并在长输入中找到一个埋藏的标记。它在不同的模型名称下重复这些任务,检查该端点能做什么。
在记录到流量的 27 天里,它以 18 个模型名称发送了 5,036 次提示请求,外加 368 次模型列表检查。3,254 次提示请求返回了 HTTP 成功。
这些请求带有 AutoGenX-Engine/9.1 请求头。其系统指令描述了一个“内部路由和日志系统”,通过“OpenRouter”为模型提供服务。我们在 172 次捕获的请求中发现了该描述。它描述的是发送方的设置;这并不能证明是 OpenRouter 发送了这些流量。
捕获记录中只有 183 条不同的用户消息。重复的任务解释了大部分流量:
| 测试 | 提示要求做什么 | 保存的检查次数 |
|---|---|---|
| 模型身份 | 精确地表明你自己的身份。返回一个固定的 JSON schema,描述该模型及其能力。 | 813 |
| 文件编辑 | 将 api_server.py 中的 PORT 从 8000 改为 9090,并添加 TIMEOUT = 30 。 | 959 |
| 函数调用 | 调用 get_weather 获取伦敦的温度。 | 314 |
| 视觉 | 识别内嵌猫照片中的动物,并只返回其名称。 | 260 |
| 长上下文检索 | 恢复 CTX_64K_OK ,这是一个隐藏在长段重复文本中的标记。 | 288 |
| 浏览 | 查找一个网页的标题,并将其作为 JSON 返回。 | 314 |
编码测试要求进行特定编辑:将 PORT 从 8000 改为 9090,并添加 TIMEOUT = 30。它提供了一个 Windows 风格的 C:\work 工作区和编码代理指令,然后在 17 个模型名称下重复该任务。历史记录中不包含助手或工具回合,因此我们可以看到任务正在被测试,但无法得知任何由此产生的工具调用是否被执行。
其他测试检查了图像和长输入。约 77,000 字符的图像载荷主要是一个内嵌 JPEG。检索测试将标记隐藏在重复填充文本约 15% 或 90% 的位置;最大的输入包含 396,862 个字符。在 283 次请求中,发送方要求最多一百万个输出 token。这是一个请求设置,而非实测消耗量。
另一个蜜罐在模型 API 探测中记录到了 AutoGenX-Engine 请求头系列。在这里,重复的任务表明的是能力测试。我们在该来源的捕获记录中没有发现客户对话历史、转售价格或密钥池配置。
记录到的反向 DNS 名称为 sportsmanslocker.com,但检查时正向 DNS 与来源地址不匹配。我们尚未识别出操作者。其凭证与我们的发布记录不匹配,因此这些计数与泄露密钥总数分开统计。
访问检查
密钥生效之后发生了什么
这些请求还显示出人们是如何测试密钥的:尝试不同的模型名、按计划重复检查,或者在多个地址上使用同一个密钥。在下面的示例中,我们把 IP 替换成了仅在各自示例内适用的标签。开始时间是根据完成时间戳和延迟估算的,其中包含在网关等待的时间。
模型检查
一句问候,29 个模型名
一个 IP 使用一个已公开的密钥发送 hi,同时请求了 29 个不同的模型名。这些请求在大约 27 秒内开始,并且全部返回了成功的 HTTP 状态。
| 估算时间偏移 | 请求 |
|---|---|
| +0.000s | Source A · claude-fable-5-1 · HTTP 200 |
| +0.005s | Source A · gpt-4o · HTTP 200 |
| +0.007s | Source A · gpt-6-luna · HTTP 200 |

约 27 秒内完成 29 次模型检查
一个 IP 向每个模型名发送了同一句问候。
折线按估算的开始时间统计请求数。它显示的是检查何时开始,而不是回复何时到达。
我们发现有 38 个持续不超过十分钟的会话,其中同一个 IP/密钥组合在至少五个模型名下发送了同一条消息。
定时检查
一个可用密钥大约每小时被检查一次
在另一组序列中,一个 IP 用同一个密钥和同一个模型名持续发送 ping。在观察到的 5 天里,我们记录了 95 次成功请求。这些请求带有 Python urllib 请求头。
| 估算时间偏移 | 请求 |
|---|---|
| +0h 00m 00s | Source A · claude-opus-5-5 · HTTP 200 |
| +1h 00m 25s | Source A · claude-opus-5-5 · HTTP 200 |
| +2h 01m 29s | Source A · claude-opus-5-5 · HTTP 200 |
检查之间的间隔接近一小时
一个来源 IP,一个已公开的密钥,一个模型,同样的 ping 提示词。
间隔基于估算的开始时间。这些请求分布在五天中的部分时段。
我们还看到大约每五分钟一次、带有 “sub2api” 请求头的检查。公开的 Sub2API 网关包含定时的模型检查,但仅凭该请求头无法判断这些请求来自哪个部署。
在用我们公开的密钥成功发送提示词的 80 个 IP 中,我们看到有 23 个 IP 在之后的日期再次出现。其中 3 个出现在七天或更多天里。
来自多个地址的使用
七个 IP 在一分钟内使用了同一个密钥
在一次集中活动中,7 个来源 IP 使用同一个已公开的密钥,在 10 个模型名下发送了 10 次成功检查。
| 估算时间偏移 | 请求 |
|---|---|
| +0.000s | Source A · gpt-5-mini · HTTP 200 |
| +0.879s | Source B · gpt-4.1 · HTTP 200 |
| +1.584s | Source C · o3 · HTTP 200 |
每个请求都带有 curl 请求头和同一条 8 个字符的消息:liveness。当时尚未开始记录响应;成功状态来自请求元数据。
这些 IP 可能属于多个用户,也可能属于一个使用多个网络出口的脚本。
同一个密钥检查出现在八个国家
我们曾埋入 payloadgrove-dev/http-header-safety-check 的一个密钥,出现在来自 8 个已记录来源地址的 23 次成功请求中。每个请求都使用同一条 24 个字符的消息和一个 undici 请求头。它们合计在大约 30 分钟内检查了 17 个模型名。
相符的密钥、消息和请求头表明这是一次协同测试。已记录的地址地理定位到八个国家;日志并未单独核实套接字对端。
脚本、工具与时间特征
从请求头中识别脚本与工具
我们检查了使用已公开密钥的 4,711 次成功提示词请求。它们来自 80 个不同的来源 IP,混合了脚本、浏览器和有名称的工具。
user-agent 请求头常常会标明某个库或工具。发送方可以更改它,因此我们对照请求行为和公开软件核对了这些标签。
图表展示的是最常见的请求头类别,而非全部请求。一个 IP 可能出现在多行中。
有名称工具这一行涵盖 “keypool”、“Zoltraak” 和 “sub2api”。我们查找了可能解释这些标签的公开软件:
| 表头中的名称 | 我们能确认的信息 | 我们在流量中看到的情况 |
|---|---|---|
| Sub2API | 一个开源网关,将对 AI 订阅账户和上游 API 密钥的访问转化为面向用户的 API。它负责身份验证、路由和计费。其监控代码会检查上游模型是否有响应。 | 2,421 条提示请求带有该标签;320 条返回 HTTP 成功。部分短检查间隔约五分钟。我们尚未确定发送这些请求的部署。 |
| keypool | 密钥池化指通过一个程序或网关管理多个 API 密钥。llm-keypool 等公开项目会这样做。我们未找到与发送这些请求的软件相匹配的已验证代码。 | 1,060 条带标签的提示请求;823 条返回 HTTP 成功。除短检查外,带有该标签的请求还包括反复出现的采集器描述。 |
| Zoltraak | 我们无法确定该标签背后的软件。另一名网关运营者在调查一个暴露的 AI 网关时也报告了 ZOLTRAAK 请求头。 | 675 条带标签的提示请求;410 条返回 HTTP 成功。其中包括短检查以及要求返回每次请求都会变化的值的提示。 |
每日活动
泄露密钥的使用如何随时间变化
我们追踪了在 GitHub 上泄露的密钥,以了解它们是如何被使用的。图表显示了使用这些密钥的全部 11,841 条请求:模型列表检查、提示以及失败尝试。
使用已公开密钥的请求
9 月 6 日至 10 月 11 日快照(UTC)的每日计数。纵轴使用对数刻度。
11,841 条请求
运营者要点
在自己的网关日志中应关注什么
这些示例给出了按凭据对网关请求分组时可调查的三种模式:
- 短时间内通过同一密钥请求许多模型名称。
- 密钥已经可用后,以稳定间隔重复检查。
- 使用某个密钥的地址发生变化,尤其是在提示请求激增期间。
在将这些模式视为滥用之前,请先与你自己的 SDK、负载均衡和监控进行对比。稳定的标识符让你无需存储原始密钥即可对请求分组。我们尚未测量检测准确率或误报率。
我们如何计数并关联这些请求
范围。聚合计数使用 10 月 11 日快照,且仅包含有我们发布记录支持的密钥。已知所有者流量(包括我们的 Israel 和 Westfield 地址)已排除。八地址测试来自截至 10 月 12 日的另一次 24 小时捕获;其计数未加入冻结总计。
捕获。响应记录在研究进行到中途时开始。HTTP 成功来自请求元数据,并不能确定回答质量。较早的捕获可能会截断长文本。
归因。请求头和对话历史由请求方提供。DNS 匹配可佐证记录的地址,而非某个人。较早的日志未单独保留 socket 对端,因此转发的地址无法全部独立验证。
标签。编码和代理类别使用最新用户消息中的词汇指标。嵌入指令、工具结果和 Telegram 字段是提交的上下文,而非经独立认证的活动。
暴露。密钥分批发布,被找到的时间长短不同。New York 和 San Francisco 的观察窗口和 API 表面也不同。这些总计描述的是本次实验,而非地理比较或一般泄露率。
在你的网关日志中看到类似模式?
我很乐意与你比较你所看到的情况,以及你的网关元数据中有哪些可用信号。