引言
9 月 25 日,微软安全团队发布了对 Storm-3168(外部厂商 Sysdig 称之为 JADEPUFFER)的完整分析报告:一个企业租户的云端资源被两个被滥用的「机器身份」扫了一遍,随后 35 分钟内发起 150 多次破坏性操作,核心删除动作压缩在约 7 分钟里,100 多个存储账户大部分被删掉,连密钥库都没放过。
很多人看到「云上资源被删光」的第一反应是:又出大漏洞了?黑客攻破微软了?这两个理解都不对。这起事件里没有一个被利用的 CVE,攻击者用的全是正规 API 和合法凭据。真正值得每个开发者警醒的,是凭据是怎么泄露的、为什么删了也没用。
一、事件复盘
先交代主角:service principal(服务主体),俗称机器身份。人在 Azure 里用账号密码登录,应用程序和自动化脚本则用 service principal 登录,靠的是三元组:租户 ID、客户端 ID、客户端密钥。这串密钥相当于程序的「密码」,一旦泄露,别人就能冒充这个程序干活。
微软报告还原的时间线是这样的:
- 6 月初,第一个失陷的机器身份开始「踩点」,花了约 15 个半小时、300 多次读取操作,把租户里的虚拟机、订阅、资源组摸了个遍;
- 90 分钟后,第二个身份登场,用 5 秒钟完成了同样的枚举动作,典型的脚本自动化特征(两者用同一批基础设施、同样的 python-requests/2.34.2 客户端标识);
- 16 小时后进入破坏阶段:持续约 35 分钟,核心删除约 7 分钟,100 多个存储账户大部分被删,同资源组里的一个密钥库、一个函数应用和一个应用服务计划也被删除;
- 删除结束半小时后,同一个身份又回来发了 30 多次「取存储账户密钥」请求,大部分成功。
结局有两个值得注意的细节:攻击者试图同时删除 Azure SQL 数据库,全部失败——原因是攻击脚本用了一个不支持的旧 API 版本,纯属技术失误;部分存储账户没被删掉,是因为资源锁和删除保护挡住了。微软没有观察到勒索信,也没有确认数据被窃取,但「删资源、拆备份保护、偷密钥」的组合符合勒索 prep 的典型套路。
源头在哪?微软发现,其中一个机器身份的完整凭据(三元组俱全),曾被该企业的一名员工明文贴在一个公开的 GitHub issue 里。issue 后来被编辑删除了密钥,但公开的编辑历史里仍然能看到。微软无法百分百确认这就是入口,但时间线和证据链条都指向这里。
二、技术核心拆解
1. 为什么没有漏洞也能造成这么大破坏
这起事件不利用任何软件缺陷。攻击者拿着真凭据、调正规 API、走的是身份权限体系内部的道路。安全行业常说「身份就是新的边界」,意思就是:当边界防御越来越严时,一张合法的门票比一次翻墙更容易。事件里被滥用的身份带着「存储账户参与者」这类角色授权,删除操作完全在它的权限范围内。
2. 机器身份为什么容易失守
人输了密码会有人提醒、有登录验证;机器身份的密钥通常写在配置里、环境变量里、代码仓库里,没人盯着它「有没有异常登录」。安全团队管这类身份叫非人类身份(NHI),数量往往是人类账号的十倍以上,却普遍缺少轮换、审计和最小权限。这次攻击里两个身份分工明确——一个踩点、一个执行——微软判断这是自动化编排,属于「AI 编排攻击」趋势的一部分。不过外部专家有不同看法:证据只能证明是高度自动化的协同,还证明不了「AI 自主决策了每一步」。这个边界我们在误区部分展开。
3. 为什么「删了就完事」是幻想
GitHub 的 issue 编辑会保留完整编辑历史,任何人都看得到改动前后的内容。安全行业的共识是:秘密一旦出现在公开渠道,就应视为已泄露,唯一的补救是吊销和轮换,而不是悄悄抹掉记录。此外攻击分两条线:先删资源,再回来偷密钥——后者可能是为了后续解密数据或二次渗透,这意味着恢复业务之前必须先搞清楚哪些密钥被碰过。
4. 那道救了部分数据的防线
资源锁(CanNotDelete)和存储账户级删除保护,本质上是独立于权限体系之外的保险丝。攻击者拿着相当大的管理权限,尝试拆除 Azure 备份和站点恢复的保护锁也全部失败。这就是行业里说的「纵深防御」:不是指望每一道锁都防住,而是指望坏运气发生时,总有一道不依赖前一道。
三层后果区分:功能层面,业务直接不可用,备份体系被攻击者盯上导致恢复链路存疑;安全层面,大量存储密钥被合法取走,数据机密性面临长期风险;合规层面,涉及用户数据的云资源被删除或密钥外泄,企业要面对数据安全法和个人信息保护法项下的通报与责任认定。
三、常见技术误区
误区一:密钥贴出去又删掉了,就当没发生过
错。公开渠道出现过的密钥必须视为已泄露。编辑历史、缓存、爬虫快照、fork 的副本都可能留存原文。正确动作只有两个:立即吊销、换新密钥,然后倒查这期间它的调用日志。
误区二:云资源被删光,肯定是出了大漏洞被入侵了
这次事件恰恰相反——没有漏洞被利用,没有系统被攻破,攻击者全程「持票入场」。对云上业务来说,凭据泄露的破坏力不亚于零日漏洞,但排查思路完全不同:前者查身份调用日志,后者查软件补丁。方向错了,排查就会扑空。
误区三:攻击者权限那么大,锁不锁没什么用
事实正相反。资源锁和删除保护是少数在权限体系之外独立生效的控制,这次事件里它们实实在在挡下了一部分删除。同理,把备份权限和日常管理权限分开,也是这个逻辑——破坏者拿到的身份再大,也够不到所有层。
四、实操指引
给开发者和团队几条可以今天落地的动作:
- 扫一遍仓库和 issue:用 gitleaks、trufflehog 这类工具扫代码仓库、issue、PR 评论里的密钥,CI 里加一道泄露检测门禁;
- 能不用静态密钥就不用:Azure 上优先用托管身份(Managed Identity)或工作负载联合身份凭据,让「密码」根本不存在于任何文件里;
- 轮换纪律:所有机器身份密钥设置过期时间,定期轮换,长期不轮换的密钥等于不设防;
- 最小权限:逐个审查 service principal 的角色授权,把「参与者」缩到具体资源组,清理不再使用的身份;
- 上保险丝:关键资源加 CanNotDelete 资源锁,开启存储账户删除保护,并把拆锁权限单独管控;
- 盯日志:给机器身份的调用开告警,尤其是大规模枚举、批量删除、取密钥这类高频操作——这次事件里 5 秒枚举两个订阅的异常行为,本来就是最好的报警信号。
五、结尾
这起事件最扎心的地方在于:几百小时的侦察和自动化攻击链,源头可能只是一个员工顺手贴出的一段配置。云时代的安全短板,越来越不在于墙够不够高,而在于有多少张「合法门票」散落在没人看管的地方。把你的密钥当密码管,把你的机器身份当员工管——这是本篇最想留下的一句话。
如果觉得有用,欢迎转发给还在配置文件里存密钥的同事。