问题背景
公司内部有一套日志与业务数据检索平台,底层是三节点的 Elasticsearch 7.x 集群,跑在云上的三台 CVM 上,存放了近半年的订单流水日志和部分脱敏不彻底的客户信息,约 4 亿条文档。集群最初由业务组自建,图省事没有开启 X-Pack Security,也就是说——没有任何认证,谁能连上 9200 端口,谁就是集群管理员。
一直以来大家的心理防线是"反正在内网,安全组挡着"。直到某次业务临时联调,有同事为了让驻场外包在家里访问 Kibana,在云安全组上开了一条 0.0.0.0/0 → 9200-9300 的放行规则,联调结束后没人记得收回去。这条规则安静地躺了二十多天。
故障现象
周一早上 08:40,业务群里陆续有人反馈"检索平台查不到历史数据"。登录 Kibana 一看,情况比"查不到"严重得多:
- 原有的
order-log-*、customer-*等几十个索引全部消失; - 集群里只剩下一个陌生索引,名字叫
read_me_to_recover,里面只有一条文档,内容是一段英文勒索信:数据已被"备份",向指定比特币地址支付 0.05 BTC 可赎回,否则数据将被公开出售; - 集群健康状态是 green——对 ES 来说删库是一次再正常不过的合法操作,它毫无怨言地执行了;
- 监控侧回看,凌晨 03:00 前后集群写入 QPS 有一波异常尖峰,随后存储用量从 1.8 TB 断崖式跌到不足 1 GB。
这是典型的 Meow 攻击 / ES 勒索擦除套路:全自动扫描 + 删库 + 留勒索信,大概率根本不存在所谓"备份"。
排查过程
**第一步:确认攻击入口。**先查这台机器的 9200 是否真的暴露在公网。在外部网络直接测试:
|
|
再翻云安全组规则,找到了那条 0.0.0.0/0 放行 9200-9300 的规则,创建时间正好在二十多天前,备注写着"临时联调"。入口确认。
**第二步:还原攻击时间线。**ES 本身默认不记审计日志,但还有两个地方可以挖:
一是 ES 的慢日志和 GC 日志间接佐证了凌晨 03:00 前后有异常批量操作;二是云平台的 VPC 流日志(Flow Log)幸好开着,按目的端口 9200 过滤,抓到了关键证据:
|
|
来源 IP 属于境外某扫段大户,威胁情报平台上有大量"ES/MongoDB 勒索擦除"的历史标记。会话总时长约 10 分钟——4 亿条数据被"拖走"是不可能的,10 分钟只够发几条 DELETE /_all 级别的请求。这基本可以判断:数据是被直接删除而非被完整窃取,勒索信是吓唬人的标准话术。
**第三步:评估泄露面。**虽然全量拖库不成立,但无法排除攻击者在删库前用 _search 抽样翻看过数据,且这二十多天里可能不止一波扫描器光顾过。用流日志把窗口期内所有命中 9200 的外部源 IP 拉了个清单,共 17 个不同来源,多数是 Shodan/Censys 类测绘平台的探测节点,有 3 个可疑 IP 存在多次交互会话。结论按最坏情况上报:存在部分数据被抽样读取的可能,客户信息字段涉及姓名和手机号中段,触发内部数据安全事件流程。
**第四步:检查主机层是否失陷。**ES 未授权访问在老版本上还能配合脚本引擎做 RCE,必须排除主机被种马:
|
|
三台节点逐一检查,均无持久化痕迹。7.x 已移除危险的动态脚本能力,本次攻击停留在"数据层删库",主机层干净。
**第五步:确认恢复手段。**集群没做 snapshot 仓库(又一个欠账),万幸数据源头还在:订单日志的上游 Kafka 保留了 7 天,更早的数据在数仓 Hive 里有 T+1 落盘副本。数据可以重建,只是要花时间。
解决方案
按"先止血、再恢复、后加固"的顺序处置:
- 止血:立即删除安全组中
0.0.0.0/0 → 9200-9300规则,恢复为仅允许内网业务网段访问;同时在三台节点的elasticsearch.yml确认network.host绑定内网地址; - 取证留存:勒索索引导出存档,流日志窗口期数据打包,供安全团队与合规上报使用;
- 开启认证:启用 X-Pack Security(7.x 基础版免费),配置
elastic超管强密码,为 Kibana、Logstash、业务应用分别创建最小权限角色,杜绝匿名访问; - 数据重建:近 7 天数据从 Kafka 重放消费重建索引;更早数据从 Hive 批量导回,历时约 30 小时全部恢复;
- 补快照:挂载对象存储作为 snapshot 仓库,配置 SLM 策略每日自动快照、保留 30 天。
外包远程联调的合理需求,改用公司 VPN + 堡垒机方案满足,不再直接对公网开数据端口。
根因分析
直接原因是那条"临时联调"的全网段放行规则——但它只是压垮骆驼的最后一根稻草。真正的根因是三层防线同时缺位:ES 集群零认证(第一层没有)、安全组变更无审批无回收机制(第二层靠自觉)、无暴露面持续监测(第三层不存在,裸奔二十多天无人知晓)。任何一层存在,这次事件都不会发生。数据组件"默认无密码 + 默认监听"的组合,在公网上的平均存活时间以小时计,Meow 类全自动脚本不会给任何侥幸窗口。
预防措施
- 暴露面持续监测:用定时任务从外部视角对公司全部公网 IP 做端口扫描(nmap 关键数据端口 9200/27017/6379/3306 等),结果与白名单比对,新增暴露即告警;
- 安全组变更管控:
0.0.0.0/0类规则必须走审批,并强制填写过期时间,到期自动回收;云平台配置审计(Config)对全放行规则设置合规基线告警; - 数据组件强制认证基线:ES/Redis/MongoDB 等组件上线检查清单中,认证与内网绑定为必检项,零认证实例不允许接入生产 VPC;
- 备份底线:所有有状态服务必须有独立于集群本身的备份(快照 + 异地),并定期做恢复演练;
- 联调场景标准化:外部人员访问一律走 VPN + 堡垒机,禁止以开安全组的方式"给个方便"。
总结
这次事件损失可控纯属运气好:攻击者是删库勒索的脚本流,而非有耐心的拖库者;上游 Kafka 和数仓恰好能重建数据。复盘时最让人后怕的不是攻击本身,而是那条放行规则安静躺了二十多天没有任何机制能发现它。安全上最贵的不是防火墙和设备,而是"临时"两个字——所有临时的放行、临时的免密、临时的绕过,只要没有强制回收机制,最终都会变成永久的洞。数据端口不上公网、组件必须带认证、备份独立于集群,这三条底线缺一不可。