2026年9月22日,ThreatDown的研究人员描述了一个名为CARBONATO的僵尸网络。它通过没有密码的开放Docker API进入服务器,在主机上启动AI代理,首先寻找语言模型的密钥。SSH密钥和访问令牌随后才会被寻找。如果您的VPS上运行着容器中的解析器,并且在.env中存放着OpenRouter或OpenAI的密钥和代理登录信息,那么这就是您的风险档案。以下是对攻击如何进行的分析,以及一个15分钟的检查清单,可以关闭这个入口。
发现了什么:4.3 GB的镜像和名为GH0ST的代理
一切始于运营商自己未关闭的Docker注册表。根据ThreatDown的数据,里面有59个仓库,234个镜像标签和4.3 GB的数据。该档案涵盖了2024年10月至2026年8月的时间段,也就是说,僵尸网络在被描述之前几乎运行了两年。在仓库中发现了XMRig挖矿程序和名为fsociety/agent的镜像。
根据报告,感染链条如下:
- 扫描器寻找Docker API在TCP 2375上开放且没有身份验证的主机。
- 通过这个API,蠕虫启动了一个特权容器,并挂载了主机的文件系统。从这一刻起,它实际上在机器上拥有root权限。
- 建立一个反向SSH隧道连接到运营商的基础设施,并安装他们的SSH服务器。
- 通过cron、systemd定时器、rc.local和OpenRC进行持久化,文件被标记为不可更改。如果删除了任何东西,守护进程会重新下载镜像。
- 容器伪装成
systemd-resolved,而进程则伪装成内核流[kworker/u2:0]。 - 每五分钟,脚本扫描相邻的/24子网和Docker桥接以寻找下一个开放的2375端口。
传播过程完全自动化,不依赖于AI。AI在这里负责已经被攻陷的服务器内部发生的事情。
僵尸网络为何需要AI代理,以及为何需要LLM密钥
在主机上安装了开放框架Hermes Agent,其角色为GH0ST(其指令存放在SOUL.md文件中)。运营商在Telegram中写下任务,代理将其连同指令转发到LLM操作网关。模型解析任务,编写终端命令,读取输出并决定下一步该做什么。结果会返回到Telegram。
在代理的指令中,优先级被明确列出。ThreatDown引用道:“AI API密钥是绝对优先的。首先进行外泄”。列表中有14个模型提供商,包括OpenAI、Anthropic、Google、Groq、Mistral和OpenRouter。SSH账户、访问令牌和数据库数据则排在后面。
逻辑很简单:被盗的LLM密钥立即转化为免费的计算资源或可转售的商品,而支付这些费用的则是密钥的拥有者。与挖矿不同,这种盗窃在CPU负载上是不可见的。只有通过模型提供商的账单才能察觉到。
这与解析器有什么关系
2026年的典型解析栈如下:VPS、多个容器(爬虫、队列、数据库、无头浏览器)、用于解析页面的LLM和代理池。所有秘密都存放在一个.env文件或容器的环境变量中。对于一个“随便读取一切”的代理来说,这就是一份现成的猎物:
- LLM密钥:正是为了这些,CARBONATO才被创建;
- 代理的登录和密码:报告没有单独列出,但具有root权限的代理会收集它看到的任何账户和令牌,而代理凭证通常就放在旁边;
- 云密钥和数据库访问,用于解析结果;
- 服务器本身:同一档案中的XMRig意味着您的爬虫的CPU将用于挖矿,任务将因超时而失败。
声誉问题也很严重。蠕虫扫描其他子网的服务器很快会进入滥用列表,托管服务商可能会因投诉而封锁它。对于解析来说,这是一记双重打击:服务器的IP被标记,而被盗的代理凭证已经在消耗您的流量,发出他人的请求。
为什么2375端口会开放,尽管您没有打开它
默认情况下,Docker监听本地UNIX套接字,而不是网络。2375端口出现是因为有人故意在守护程序的配置中添加了-H tcp://0.0.0.0:2375。通常这样做是为了连接远程IDE、CI或容器管理面板,然后就忘记了。Docker文档明确警告,访问守护程序等同于对机器的root访问,并建议像保护root密码一样保护密钥。带有TLS的加密版本在2376端口上工作,而2375则意味着没有客户端验证的明文。
第二个陷阱打击那些认为“我有ufw”的人。Docker文档指出,已发布容器端口的流量在nat表中被重定向到INPUT和OUTPUT链,而ufw依赖于这些链。实际上,ufw的规则对这些端口根本不起作用。如果您启动了Redis、队列面板或代理管理器,并使用-p 6379:6379,那么该端口将暴露在互联网上,无论ufw status显示什么。
15分钟的解析器服务器检查清单
1. 检查Docker API是否未监听网络
- 查看监听的端口:
ss -tlnp | grep -E '2375|2376|dockerd'。如果输出中有0.0.0.0:2375或:::2375,请立即关闭。 - 检查标志来自哪里:
/etc/docker/daemon.json(键"hosts")和单元systemctl cat docker(ExecStart行中带有-H tcp://)。 - 移除TCP监听并重启守护程序。对于远程管理,请使用SSH上下文:
docker context create remote --docker host=ssh://user@server。此时不需要外部端口。 - 如果确实需要TCP(CI、编排器),那么仅使用2376,并进行相互TLS身份验证和源IP的白名单。
2. 检查容器中暴露的内容
- 执行
docker ps --format '{{.Names}} {{.Ports}}'。所有以0.0.0.0:开头的内容都可以从互联网访问,绕过ufw。 - 服务性服务(Redis、Postgres、Mongo、队列面板、Selenium Grid、代理管理器API)仅发布到本地地址:
-p 127.0.0.1:6379:6379。外部访问通过SSH隧道进行。 - 如果无法避免外部端口,请在
DOCKER-USER链中进行过滤:Docker不会重写它,并且它适用于容器流量。 - 不要在服务器上保持未授权的开放代理(3128端口的Squid,1080端口的SOCKS“为自己人”)。扫描器定期发现这些端口,并通过您的IP发送他人的流量,伴随他人的投诉。
3. 处理秘密
- 分开密钥:每个服务器或项目一个单独的LLM密钥,并在模型提供商处设置支出限制。被盗的限额为20美元的密钥是个麻烦,而没有限额则是预算的漏洞。
- 代理密钥也要按任务分开:每个解析器一个单独的登录(子账户)。这样泄漏时可以通过特定登录的消费来发现,并且只需撤销该登录,而不影响其他工作。
- 如果服务只需要二十个密钥中的两个,请不要通过
env_file将整个.env传递到容器中。 - 在可能的情况下,将访问权限绑定到服务器的IP:在代理提供商处的白名单或根据地址限制API密钥。
关于如何在脚本和容器中安全存储代理登录的更多信息,我们在安全存储代理凭据的分析中进行了详细说明。
4. 移除多余的权限
- 不要以
--privileged启动容器,也不要在没有极端必要的情况下挂载/或/var/run/docker.sock。容器内的套接字与主机上的root权限相同。 - 对于无头浏览器,通常只需
--shm-size和seccomp配置文件。特权模式“为了让Chrome运行”是一个糟糕的折中。
如何判断您是否已经被感染
ThreatDown及其报告分析提供了以下被攻陷的迹象:
- 文件
SOUL.md中包含GH0ST一词(例如,/root/.hermes/SOUL.md); - 环境变量或
.env中的行:CARBONATO_API_KEY; - 文件
/usr/local/bin/.docker-network-monitor和可疑的/usr/sbin/systemd-logind; - 名为
systemd-resolved的容器(真正的systemd-resolved是主机服务,而不是容器); - 意外的Telegram API出站流量和指向AS262145的反向SSH隧道;
- 与地址45.79.183.61、213.136.79.115、190.211.124.187的连接;
- cron和systemd中的不可更改文件:
lsattr /etc/cron.d/* /etc/systemd/system/*将显示标志i。
如果有任何一个迹象匹配,手动清理服务器是毫无意义的:持久化是分层的,守护进程会恢复植入物。正确的顺序是:
- 从另一台机器撤回服务器上的所有密钥:LLM、云、数据库、代理。
- 检查过去几周每个密钥的消费情况。代理登录的统计信息将立即显示他人的流量。
- 从干净的镜像中启动新服务器,发布新密钥,然后再迁移数据,不带旧机器的二进制文件和cron文件。
这里的代理是什么,它们解决不了什么
代理无法保护服务器免受CARBONATO的攻击。蠕虫不是通过您的出站请求进入,而是通过入站端口进入。然而,正确的代理使用方案可以减少损失。为任务分配单独的登录、流量限制和IP绑定将泄漏从“整个余额消失”转变为“撤销一个登录”。
还有一个反面,僵尸网络经常展示:被捕获的外部设备本身成为可疑网络的“驻留代理”。因此,对于解析,建议从具有明确来源池的提供商处获取流量。对于大多数数据收集任务,按千兆计费的住宅代理是合适的,其中每个代理的消费在控制面板中可见。对于没有严格反机器人保护的服务性任务,数据中心代理的更便宜选项就足够了。
总结
CARBONATO既不利用零日漏洞,也不使用复杂的利用方式。它通过服务器所有者自己打开的门进入:TCP 2375没有密码。它的新颖之处在于:内部运行着AI代理,负责首先获取模型的密钥。对于在自己的VPS上收集数据的人来说,实际的结论是。关闭Docker API,将服务端口发布到127.0.0.1,按任务分配LLM和代理密钥,并设置支出限制。这是15分钟的工作,之后您的服务器对这些僵尸网络将变得不再是一个有吸引力的目标。
