摘要

  • 暴露在公网的 memcached UDP 服务会相信数据包中的源地址,并把一个很小的请求放大成远大得多的响应;攻击者因此可以借第三方缓存服务器的带宽冲击并未发起请求的受害者。
  • 修复不属于单一权威:memcached 1.5.6 默认关闭 UDP,源网络可在客户边缘拦截伪造地址,GitHub 则依靠自身监测、BGP 撤告与选定的清洗路径恢复服务。

一次未经授权的带宽支出

Memcached 原本服务于可信应用网络中的高速缓存。它的 UDP 模式不先建立连接,也就没有证明报文上写的源地址确实能接收返回流量。只要服务被暴露到公网,这项效率选择就会变成授权漏洞:陌生人写下另一个人的地址,缓存服务器便替陌生人向那个地址发送数据。

Cloudflare 记录的操作分为两步。攻击者先向开放的缓存写入大值,再发送伪造受害者源地址的微小 get 请求。一次实验中,15 字节请求触发了 134 KB 响应;其另一次观测中,15 字节触发 750 KB,约为 51,200 倍。两个数字不能套用到所有键、所有服务器或整场 GitHub 攻击,但它们准确揭示了成本转移:攻击者只支付小请求,缓存运营者支付大响应,受害者承担入口洪流。

这条链必须同时满足两个条件。缓存允许不可信 UDP 请求;某个上游网络又允许客户发出并不属于自己的源地址。关闭任一扇门,memcached 反射链就会断开。

GitHub 能控制的是自己的边缘

GitHub 的一手事故报告给出了清晰边界。2018 年 2 月 28 日,GitHub.com 在 17:21 至 17:26 UTC 不可用,之后间歇性不可用至 17:30。GitHub 表示,数据机密性和完整性没有面临风险。

攻击流量来自一千多个自治系统、数万个端点,峰值达到 1.35 Tbps 和每秒 1.269 亿包。这是 GitHub 对该事件的测量,不是 Cloudflare 实验数据的简单放大。

17:21,监测系统发现入口与出口流量比异常。一个设施的入口传输超过 100 Gbps 后,团队决定把流量移交 Akamai。17:26,GitHub 通过 ChatOps 发出命令,从传输提供商撤回 BGP 宣告,仅通过 Akamai 链路宣告 AS36459。路由随后收敛,Akamai 边界的 ACL 开始缓解攻击;17:30,监测显示服务完全恢复。

17:34,GitHub 又撤回互联网交换点路由,再移走 40 Gbps。18:00 之后不久还有一次约 400 Gbps 的峰值。事后 GitHub 表示将研究自动启用 DDoS 缓解提供商,以缩短平均恢复时间。这证明了计划,不证明后来已经部署。

这九分钟没有让 GitHub 获得远程修改数万个缓存的权力。它真正拥有的是本地检测、自己的 BGP 宣告、对清洗伙伴的选择,以及验证服务是否恢复的能力。

默认关闭不是删除协议

Memcached 1.5.6 于 2 月 27 日发布。上游说明称,这个修复版本主要把 UDP 改为默认禁用。对应提交很小:settings.udpport = 11211 变成 settings.udpport = 0;仅指定 TCP 端口时,也不再隐式开启同号 UDP 端口。测试随之修改。

UDP 并未被删除。有真实需求的运营者仍可用 -U 11211 明确开启。这种设计没有用中心命令禁止所有本地选择,而是取消了“什么都不做就意外成为公网放大器”的初始许可。

然而,版本号不是运行事实。发行包、服务单元或本地参数都可能重新开启 UDP。旧版本也可能只绑定私网地址并由防火墙隔离。有效的安全问题只有一个:这台正在运行的主机是否会回应来自不可信网络的 UDP 11211 请求,它实际会返回多少字节?

这正是运行代码优先。发布说明表达意图,代码差异改变初始行为,测试固定预期,而套接字清单、外部探测和抓包才证明某台机器真正采用了新边界。

源地址验证处理的是另一种谎言

关闭 UDP 处理放大器;源地址验证处理攻击者借用受害者身份的问题。

RFC 2827,也就是 BCP 38,建议互联网服务提供商在下游客户的汇聚边界进行过滤,只允许客户合法使用的源前缀进入网络。如果攻击者所在接入边缘执行这一规则,伪装成 GitHub 的请求会在抵达缓存之前被丢弃。

共同目标简单,实际拓扑并不简单。RFC 3704 针对多宿主和非对称路由讨论了入口 ACL、严格反向路径转发、可行路径检查和较宽松的变体。错误套用严格检查可能误伤合法非对称流量;只看“全网是否有到该源地址的路由”又可能无法证明这个客户有权使用该地址。

最小初始规范应固定安全性质:客户边界不得输出客户无权使用的源身份。至于 ACL、uRPF、SAVI、路由数据同步和例外处理,则由承担后果的运营者本地选择和验证。自由选择实现,不等于可以免除结果证明。

三个控制面,三份证据

缓存运营者需要用有效监听配置、防火墙状态、外部探测和异常出口指标证明自己没有成为反射器。源网络需要用客户边缘测试和流量证据证明伪造地址出不去。潜在受害者需要用告警、容量、BGP 演练、清洗接入和恢复时间证明自己能够接管事件。

这些控制不能互相抵账。受害侧清洗发生在远端缓存已经付出出口流量之后;关闭 memcached UDP 不会让攻击源网络自动诚实;BCP 38 能阻断大量反射请求,却不能替 GitHub 准备路由逃生口。

权力应留在承担后果的一方。缓存运营者决定 UDP 是否有本地价值;接入网决定怎样验证自己的客户;GitHub 决定何时启动引流;Akamai 只在 GitHub 选择的路径内过滤。标准文档和软件项目可以协调,却不必成为所有流量的常设统治者。

证据边界

公开资料证明了强放大机制、GitHub 的 1.35 Tbps 峰值和 memcached 默认值改变。它没有证明每个 memcached 都可从公网访问,也没有证明 GitHub 每个包都放大 51,200 倍,更没有证明 BCP 38 在所有相关网络中都未部署。GitHub 提出的自动化也不能被写成已经完成的事实。

更可靠的结论是:默认值能静默授予机器替陌生人花费带宽的权力;未过滤的边缘能让客户借用第三方身份;受害者只能依靠事先掌握的本地控制恢复。规范与公告只是建议,线路上的行为才显示谁仍在答复。

来源