代码 etcd Watch 授权绕过:鉴权前归一化请求,单 key 权限放行走全表

2026-09-01 21:02:46

etcd Watch 授权绕过漏洞分析:鉴权检查与请求归一化的顺序问题

漏洞概述

  • 漏洞编号:GO-2026-6114
  • 影响组件:go.etcd.io/etcd/server/v3
  • 影响版本:3.5.33、3.6.14、3.7.1 之前
  • 修复版本:3.5.33 / 3.6.14 / 3.7.1
  • 风险类型:RBAC 授权绕过
  • 前提条件:已认证用户,且仅对单个 key 拥有 READ 权限

核心问题:鉴权检查与请求归一化的顺序

直白地说:Watch RPC 处理器在鉴权检查之前,就把开放式范围(open-ended range)的末尾归一化成了“精确 key”。这个顺序错位导致权限评估逻辑(isWatchPermitted)拿到的请求看起来是一个“单 key watch”,于是按单 key 权限放行。但底层 watch 流实际订阅的是字典序大于等于目标 key 的整个范围

WithFromKey 语义回顾

clientv3.WithFromKey() 表示创建一个“从某个 key 开始,到正无穷”的 watch 范围。即:

  • watch 起点:传入的 key(包含)
  • watch 终点:\0 之后所有 key,逻辑上无上限

正常来说,这类请求需要用户具备该范围的 READ 权限。但由于归一化在鉴权之前完成,服务端将范围末尾“折叠”成目标 key 本身,从而在授权检查阶段被误判为单 key 访问。

漏洞触发路径

  1. 用户通过 RBAC 只被授予 key "foo" 的 READ 权限。
  2. 攻击者使用已认证身份发起 watch,key 设为 foo,并附加 WithFromKey()
  3. 服务端收到请求后,先对 range end 做归一化处理,使其成为单 key 形式。
  4. 鉴权器执行 isWatchPermitted,看到的是单 key foo,权限检查通过。
  5. 实际 watch 流开始推送所有 >= foo 的 key 更新事件,远超授权范围。

结果是:单 key 读权限被放大成整段 key 读取

影响评估

  • 攻击者必须已通过 etcd 认证(非匿名)。
  • 攻击者本身拥有至少一个 key 的只读权限。
  • 绕过仅影响 watch 路径,不影响 raw range 或 txn 路径(至少本漏洞未涉及)。
  • 敏感信息泄露:只要还有更高排序的 key,攻击者就能持续观察其变更。

升级建议

  • 升级到 etcd server v3 3.5.33+(3.5 系列)
  • 3.6.14+(3.6 系列)
  • 3.7.1+(3.7 系列)

如果无法立即升级,临时缓解措施:

  • 不要为单个 key 授予仅 READ 权限的独立用户,除非业务完全隔离。
  • 对所有使用 WithFromKey() 的客户端做代理层校验,确认其 RBAC 角色覆盖完整范围。
  • 审查审计日志,寻找非预期的开放式 watch 动作。

运维自查清单

1. 是否启用 RBAC?

etcdctl auth status

如果返回 Authentication enabled: true,继续检查。

2. 是否存在“只读单 key”用户?

枚举用户和角色:

etcdctl user list
etcdctl role list

对每个角色,查看其权限:

etcdctl role get

重点关注形如:

RangeRead:
[key: "app/foo/secret"]

这类权限仅覆盖一个 key。如果该角色被分配给了真实用户,则存在被利用的潜在条件。

3. 是否有可疑的开放式 watch 请求

开启 etcd debug 日志,或通过 metrics 检查 watch 流数量、范围分布。若发现大量 range end 为 \00xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 的 watch,且发起者的角色只覆盖单 key,则需立即标记并排查。

4. 版本核验

etcd --version

确认是否低于修复版本。若低于 3.5.33 / 3.6.14 / 3.7.1,按上述升级计划处理。

一句话总结

鉴权前做请求归一化,等于让权限检查“看着单 key,放行走全表”。升级到指定版本,并重新梳理单 key 只读角色的必要性。

复制全文 生成海报 etcd 安全 RBAC Go

推荐文章

程序员茄子在线接单