169.254.169.254 这个地址,是我自己敲进测试框的。

那阵子在给七亿批量远程管理工具的 AI 助手做「逐步执行」:模型每次只出一条命令,客户端在目标机上跑,把真实回显喂回去,它再决定下一条。除了 SSH,还配了两个从用户电脑出发的小工具,一个测端口,一个测地址通不通。理由很实际——服务器上 curl 127.0.0.1 通,只说明服务自己活着;用户从外面连不上,问题常在中间的网络或者安全组。

测地址那个工具,界面上就是一个输入框加一个按钮。我随手填了 169.254.169.254,点下去,连上了。

这个地址属于链路本地网段,云厂商把它留给元数据服务。连上不奇怪,机器就在云上。我盯着结果看了几秒才反应过来不对:这个框不是只有我在填,AI 也会填。它排障的时候完全可能"顺手"去连一下这个地址,看看是不是网络问题。

连上去能读到什么,是另一回事。云主机的元数据服务,不少环境里不认证(IMDSv1),一个 GET 就能拿到实例的临时凭证、user-data、实例 ID。临时凭证拿到手,再往外发一个请求,就是一次很标准的外泄。整条链上,我们这头只是"提供了一个测端口的工具",谁都没干坏事。

一开始我只把它按"工具误用"记了一笔,后来觉得不对:模型不是人,它没有"这个地址不该点"的常识。它看得到的只有提示词和工具列表。工具列表里有一个"连任意地址"的函数,它就会用。

改的时候还绕了一下。第一版想按字符串匹配,域名黑名单加 IP 前缀,写着写着就觉得不踏实——域名指过去就绕开了。后来改成先解析,拿到 IP 再判网段,并且拿解析出来的那个 IP 去连,不再解析第二次。保留段也不止一个:127.0.0.0/8、169.254.0.0/16、::1、fe80::/10,再加几个云厂商自己的元数据地址。这些不是从日志里学的,是做这行早晚会撞上的。

同一批还收了另一个口子:密码提示。逐步执行跑到 sudo、ssh key 的 passphrase、mysql -p 这类地方,程序原来会顺着往下应答。现在见到"密码/口令/passphrase/私钥"就停,把控制权交回人。模型在试错的时候,你没法保证它下一步会不会顺手把口令写进日志或者记忆里。密钥进记忆那个坑就是这么来的。

这条线其实跟我们之前折腾防爆破是一回事——都在判断"谁在连、连到哪"。那篇里为了不误封自己人,最后做到按归属地放行,绕了不少弯。这次干脆反过来,默认不许连,只放用户明确填的地址。

AI 助手是 V95 到 V113 那一天里做出来的,这个闸是当天后来补的,具体哪一版忘了。现在那个输入框还在,填 169.254.169.254 会弹一句"目标地址不允许",文案生硬得很,但总比它能连上去强。七亿批量远程管理工具的下载页在这,更新的版本都在那儿。