前几天,在对某个大型身份提供商(IdP)进行授权基础设施的安全测绘时,我意外撞见了一个关于 Keycloak 的配置缺陷。
本以为是一次摧枯拉朽的“关键信息泄露”,但在与厂商安全团队的交锋中,却让我对 OAuth2.0 和 OIDC 协议规范(RFC)有了更深刻的理解。虽然最后这个洞被定义为 Informative(信息级),但厂商依然大方地发了一笔现金赏金,权当请我这穷学生吃了几顿大餐。
今天,我们就来复盘一下这次充满戏剧性的身份认证基础设施挖掘之旅。
0x01 故事背景
随着云原生和微服务架构的普及,越来越多的企业开始采用 Keycloak 作为统一的身份与访问管理(IAM)解决方案。对于攻击者而言,IdP(身份提供商)就是整个皇冠上的明珠——只要拿下了它,下游的所有联邦业务都将沦陷。
在一次授权基础设施的常规摸排中,我的目光锁定在了一个域名:auth.redacted.com。指纹识别很快告诉我,这是一个标准的 Keycloak 服务。
一般来说,面对 Keycloak,大家的常规思路是寻找已知的 CVE(比如偏早期的 RCE 或路径穿越)。但在实际的商业环境中,打点哪有这么容易?版本往往都是最新的。于是,我决定转变思路:不打漏洞,打配置;不看代码,看业务逻辑。
我的直觉告诉我,庞大的业务线往往意味着复杂的自定义权限划分。如果他们没有做好访问控制,OIDC(OpenID Connect)协议自带的“发现机制”或许会成为出卖他们的内鬼。
0x02 信息收集
在 OIDC 规范中,为了让客户端能够动态获取服务端的配置信息,通常会暴露一个 .well-known/openid-configuration 端点。这就好比一本“API 电话簿”。
我熟练地在浏览器中敲下了目标域(Realm)的发现文档路径:
GET /auth/realms/target-realm/.well-known/openid-configuration HTTP/1.1
Host: auth.redacted.com
屏幕瞬间被海量的 JSON 数据填满,足足有 6.6 KB。这本是一次预料之中的 200 OK,然而,当我仔细往下翻阅时,接下来的发现推翻了我“这只是标准公开信息”的假设。
在这份配置文档的 scopes_supported(支持的自定义作用域)字段中,我看到了一些极具暗示性的命名:
"scopes_supported": [
"openid",
"email",
"profile",
"autologin", // <-- 疑点1:自动登录?是否存在认证绕过可能?
"api_keycloak_legacy", // <-- 疑点2:旧版API?往往意味着安全性较弱!
"target-roles",
"get-registration-ts", // <-- 疑点3:注册时间戳?可能泄露用户元数据
"service_account" // <-- 疑点4:服务端后台账户权限?
]
逻辑转换的瞬间在此刻发生:
正常情况下,暴露标准的 openid, profile 无可厚非,但暴露如此底层的内部业务 Scope,相当于给攻击者画了一张“藏宝图”。它明白无误地告诉我们:“嘿,我们后台有这些旧接口和提权向量,快来打!”
0x03 漏洞利用
拿到“藏宝图”后,我的攻击思路彻底打开了。既然业务域(target-realm)的配置毫无保留,那作为 Keycloak 最高权限的统帅——master 域,是不是也赤裸裸地暴露在公网上?
步骤 1:越权试探 Master 域
我立刻构造了对 master 域的探测请求:
GET /auth/realms/master/.well-known/openid-configuration HTTP/1.1
Host: auth.redacted.com
200 OK!
约 6,593 字节的 Master 域 OIDC 配置文档被完整下载。这意味着任何未授权的攻击者,都可以完整映射该企业核心认证基础设施的拓扑、端点甚至联合 IdP 链路。

步骤 2:提取密钥信息(JWKS)
紧接着,我顺着发现文档里的 jwks_uri,向证书端点发起了请求:
GET /auth/realms/target-realm/protocol/openid-connect/certs HTTP/1.1
Host: auth.redacted.com

响应体直接吐出了包含 RSA 公钥的模数(n)和指数(e),以及签名算法信息。当时作为一名在校生,看到“密钥”两个字,血压都上来了,心想:“好家伙,签名密钥都漏了,这不得伪造 JWT 令牌满天飞?”(剧透:这里我犯了一个典型的新手认知偏差,后文会讲到)。
步骤 3:挖掘客户端注册端点
我还发现 registration_endpoint 端点依然存活。虽然尝试注册客户端时被 {“error”:”insufficient_scope”,”error_description”:”Policy ‘Trusted Hosts’ rejected request”} 拦截,说明有一层 WAF/策略保护,但端点本身的暴露依然增加了攻击面。
0x04 漏洞影响
在漏洞报告中,我将这个组合拳评定为 中危 (CVSS: 5.3),并描绘了如下攻击场景:
- 基础设施测绘: 攻击者无需认证,即可获取包括端点、授权类型、自定义作用域、客户端 ID(如 account-app-of-et-moi, deleg-sipa 等)的全貌。
- 高价值目标锁定: 自定义 Scope 中的 api_keycloak_legacy 和 autologin 为定向打击提供了绝佳的入口。
- IdP 供应链链路: 通过暴露的客户端,识别出了到 auth.infoconnect.fr 的代理链路,这为后续的联合身份攻击(Federated Identity Attacks)提供了前置情报。
0x05 峰回路转:来自厂商的“降维打击”与反思
满怀期待地提交报告后,厂商的安全团队很快给出了详尽的回复。出乎意料的是,他们将漏洞状态改为 Informative(信息级)。
在看完他们的回复后,我犹如被浇了一盆冷水,但也犹如醍醐灌顶,这就是实战带来的成长。厂商是这么给我“上课”的:
- 关于 OIDC 发现文档: 根据 OIDC Discovery 1.0 规范,.well-known/openid-configuration 必须在未认证的情况下公开访问,否则单页应用(SPA)根本无法动态发现服务端点。
- 关于 JWKS 签名密钥提取: 根据 RFC 7517 规范,JWKS 端点暴露的仅仅是公钥(用于资源服务器验证 Token 签名),而私钥从未泄露。我把“获取公钥”误判为了“泄露签名密钥”。
- 关于 Client ID 泄露: 在 OAuth2.0 中,Client ID 本就不是机密信息,只有 Client Secret 才是。而 Secret 并没有泄露。
但是(转折来了)!
厂商非常有格局地表示,尽管核心点符合协议预期,但我指出的部分边缘风险是完全成立的:
- Master 域不应暴露: 他们承认 master 域的发现文档对公网开放是不必要的风险,并承诺将在 WAF 层面进行拦截封堵。
- 内部 Scope 命名泄露: 他们认可 api_keycloak_legacy 等内部敏感命名暴露在公网,构成了低级别的机密性影响(契合我给的 CVSS 评分)。他们解释道,Keycloak 默认机制无法从发现文档中单独剔除某个 Scope,这需要二开 SPI,但他们会评估“修改内部 Scope 命名规范”来缓解这个问题。
因为这份报告的细致程度和找出的盲点,厂商最终不仅发了声望积分,还额外发放了一笔现金赏金

网安之路是一场永无止境的修行。作为一名大学生白帽,这次经历让我明白:安全不仅是找各种奇技淫巧的 Payload,更是对底层协议规范(RFC)的深度理解。 只有懂标准,才能知道什么是“特性”,什么是真正的“配置缺陷”。