OAuth 2.0 默认凭证导致未授权访问漏洞一个典型的前端硬编码 → OAuth 2.0 Client Credentials 提权 → 越权访问业务 API 的攻击链0x00 前言某品牌渠道数字化平台前后端分离架构前端 Vue3 打包后端使用 OAuth 2.0 JWT 做身份认证。打开首页 F12发现 JS 文件里躺着一对硬编码凭证。顺着这条线索一路走到了越权查组织用户数据的终点。本文还原完整过程并分析涉及的 OAuth 2.0 协议机制。0x01 信息收集主页面访问http://target.com/查看源代码scripttypemodulecrossoriginsrc/assets/~.js/script这个 JS 文件就是前端的全部逻辑直接下载下来搜关键字。发现硬编码凭证同时 JS 中还存在完整的 OAuth 2.0 Token 请求逻辑说明开发阶段写的测试代码打包上线时忘了清理。发现 Token 端点JS 中同时暴露了 Token 获取接口和业务 API 路径/api/authorization-server/oauth/token ← OAuth 2.0 Token Endpoint /api/organization/user/conditions ← 组织用户查询接口0x02 漏洞利用第一步获取 Access Token直接走 OAuth 2.0 Client Credentials 模式换 TokenPOST /api/authorization-server/oauth/token HTTP/1.1 Host: target.com Authorization: Basic Content-Type: application/x-www-form-urlencoded grant_typeclient_credentialsscoperead参数说明参数值含义AuthorizationBasic base64client_id:client_secret 用 Base64 编码提交grant_typeclient_credentialsOAuth 2.0 的机器对机器授权模式scoperead申请只读权限服务端返回了合法 JWTeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9~解码 JWT Payload{reference:null,roleIds:null,user_id:null,user_name:null,scope:[read],group_ids:null,dept_id:null,is_manager:null,position_codes:null,client_id:test_client,exp:1780646963}关键发现全部用户身份字段为nullToken 不绑定任何真实用户。但签名有效、未过期服务器认可其合法性。第二步越权调用业务 APIPOST /api/organization/user/conditions HTTP/1.1 Host: target.com Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: application/json {}请求体传空对象{}等于把能查的都给我。一个user_idnull、没有任何角色的空壳 Token成功调通了组织用户查询接口。0x03 涉及的 OAuth 2.0 协议机制分析本次攻击没有利用 OAuth 2.0 的漏洞而是利用了协议设计的合法规则。三层叠加┌──────────────────────────────────────────────────────┐ │ RFC 6749 §4.4 → Client Credentials 不需要人类用户 │ │ RFC 6749 §2.3.1 → HTTP Basic Auth 传递 client 凭证 │ │ RFC 6750 → Bearer Token 持有即通过原则 │ └──────────────────────────────────────────────────────┘ ↓ 三层叠加 凭证泄露即全线沦陷规则一Client Credentials GrantRFC 6749 §4.4OAuth 2.0 定义了 4 种授权模式client_credentials是唯一不需要人类用户参与的模式其他模式用户 → 登录 → 授权 → 发 Token 本模式 客户端 → 凭 client_id secret 证明身份 → 直接发 Token协议原文RFC 6749 §4.4.1The client can request an access token using only its client credentials when the client is requesting access to the protected resources under its control.攻击利用的点协议不要求请求者是一个人。test_client:test_secret被服务端当成合法的机器客户端直接签发了 Token。JWT 中user_idnull是 client_credentials 模式的正常结果——因为这个模式的 Token 本来就不绑定人类用户。规则二HTTP Basic Auth 传递凭证RFC 6749 §2.3.1协议规定客户端通过 HTTP Basic Auth 向 Token 端点证明身份Clients in possession of a client password MAY use the HTTP Basic authentication scheme.client_id:client_secret → Base64 编码 → Authorization: Basic 编码值攻击利用的点Base64 是编码不是加密任何人拿到 JS 中的字符串都能直接解码还原明文。规则三Bearer Token 持票即过RFC 6750协议规定持有 Token 即可访问受保护资源The access token is a string representing an access authorization issued to the client.攻击利用的点协议没有要求资源服务器二次校验 Token 中的user_id是否为 null。网关/拦截器只做了两步校验Token 过期了吗 → 没过期 ✓ 签名有效吗 → 有效 ✓ ↓ 放行user_idnull不在校验逻辑里。Scope 参数RFC 6749 §3.3客户端在请求体中声明scoperead服务端直接照搬签发。协议虽允许服务端缩小 scope但不强制。这意味如果服务端限制不严攻击者可以尝试换更大的 scope如read write提权。0x04 攻击链路总结前端 JS 泄露 test_client:test_secret ↓ POST /oauth/token grant_typeclient_credentials ↓ 拿到合法 JWTuser_idnull, scope[read] ↓ POST /api/organization/user/conditions Bearer Token ↓ 空身份 Token 成功查询组织用户数据0x05 漏洞根因漏洞不在 OAuth 2.0 协议本身而是三层人为失误层次问题前端测试凭证硬编码在 JS 中打包上线未清理认证服务测试 client 在生产环境未禁用且未区分环境业务接口Token 校验只管有没有效不管是谁空 identity 的 Token 不应访问业务 APIOAuth 2.0 把机器认证和用户认证拆成了两件事这个系统只完成了第一件——给了机器一张票但每个刷票的门口都没人查身份证。本文仅用于安全研究与學習交流请勿对未授权系统进行测试。
OAuth2.0默认凭证导致未授权访问
发布时间:2026/5/30 1:30:08
OAuth 2.0 默认凭证导致未授权访问漏洞一个典型的前端硬编码 → OAuth 2.0 Client Credentials 提权 → 越权访问业务 API 的攻击链0x00 前言某品牌渠道数字化平台前后端分离架构前端 Vue3 打包后端使用 OAuth 2.0 JWT 做身份认证。打开首页 F12发现 JS 文件里躺着一对硬编码凭证。顺着这条线索一路走到了越权查组织用户数据的终点。本文还原完整过程并分析涉及的 OAuth 2.0 协议机制。0x01 信息收集主页面访问http://target.com/查看源代码scripttypemodulecrossoriginsrc/assets/~.js/script这个 JS 文件就是前端的全部逻辑直接下载下来搜关键字。发现硬编码凭证同时 JS 中还存在完整的 OAuth 2.0 Token 请求逻辑说明开发阶段写的测试代码打包上线时忘了清理。发现 Token 端点JS 中同时暴露了 Token 获取接口和业务 API 路径/api/authorization-server/oauth/token ← OAuth 2.0 Token Endpoint /api/organization/user/conditions ← 组织用户查询接口0x02 漏洞利用第一步获取 Access Token直接走 OAuth 2.0 Client Credentials 模式换 TokenPOST /api/authorization-server/oauth/token HTTP/1.1 Host: target.com Authorization: Basic Content-Type: application/x-www-form-urlencoded grant_typeclient_credentialsscoperead参数说明参数值含义AuthorizationBasic base64client_id:client_secret 用 Base64 编码提交grant_typeclient_credentialsOAuth 2.0 的机器对机器授权模式scoperead申请只读权限服务端返回了合法 JWTeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9~解码 JWT Payload{reference:null,roleIds:null,user_id:null,user_name:null,scope:[read],group_ids:null,dept_id:null,is_manager:null,position_codes:null,client_id:test_client,exp:1780646963}关键发现全部用户身份字段为nullToken 不绑定任何真实用户。但签名有效、未过期服务器认可其合法性。第二步越权调用业务 APIPOST /api/organization/user/conditions HTTP/1.1 Host: target.com Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: application/json {}请求体传空对象{}等于把能查的都给我。一个user_idnull、没有任何角色的空壳 Token成功调通了组织用户查询接口。0x03 涉及的 OAuth 2.0 协议机制分析本次攻击没有利用 OAuth 2.0 的漏洞而是利用了协议设计的合法规则。三层叠加┌──────────────────────────────────────────────────────┐ │ RFC 6749 §4.4 → Client Credentials 不需要人类用户 │ │ RFC 6749 §2.3.1 → HTTP Basic Auth 传递 client 凭证 │ │ RFC 6750 → Bearer Token 持有即通过原则 │ └──────────────────────────────────────────────────────┘ ↓ 三层叠加 凭证泄露即全线沦陷规则一Client Credentials GrantRFC 6749 §4.4OAuth 2.0 定义了 4 种授权模式client_credentials是唯一不需要人类用户参与的模式其他模式用户 → 登录 → 授权 → 发 Token 本模式 客户端 → 凭 client_id secret 证明身份 → 直接发 Token协议原文RFC 6749 §4.4.1The client can request an access token using only its client credentials when the client is requesting access to the protected resources under its control.攻击利用的点协议不要求请求者是一个人。test_client:test_secret被服务端当成合法的机器客户端直接签发了 Token。JWT 中user_idnull是 client_credentials 模式的正常结果——因为这个模式的 Token 本来就不绑定人类用户。规则二HTTP Basic Auth 传递凭证RFC 6749 §2.3.1协议规定客户端通过 HTTP Basic Auth 向 Token 端点证明身份Clients in possession of a client password MAY use the HTTP Basic authentication scheme.client_id:client_secret → Base64 编码 → Authorization: Basic 编码值攻击利用的点Base64 是编码不是加密任何人拿到 JS 中的字符串都能直接解码还原明文。规则三Bearer Token 持票即过RFC 6750协议规定持有 Token 即可访问受保护资源The access token is a string representing an access authorization issued to the client.攻击利用的点协议没有要求资源服务器二次校验 Token 中的user_id是否为 null。网关/拦截器只做了两步校验Token 过期了吗 → 没过期 ✓ 签名有效吗 → 有效 ✓ ↓ 放行user_idnull不在校验逻辑里。Scope 参数RFC 6749 §3.3客户端在请求体中声明scoperead服务端直接照搬签发。协议虽允许服务端缩小 scope但不强制。这意味如果服务端限制不严攻击者可以尝试换更大的 scope如read write提权。0x04 攻击链路总结前端 JS 泄露 test_client:test_secret ↓ POST /oauth/token grant_typeclient_credentials ↓ 拿到合法 JWTuser_idnull, scope[read] ↓ POST /api/organization/user/conditions Bearer Token ↓ 空身份 Token 成功查询组织用户数据0x05 漏洞根因漏洞不在 OAuth 2.0 协议本身而是三层人为失误层次问题前端测试凭证硬编码在 JS 中打包上线未清理认证服务测试 client 在生产环境未禁用且未区分环境业务接口Token 校验只管有没有效不管是谁空 identity 的 Token 不应访问业务 APIOAuth 2.0 把机器认证和用户认证拆成了两件事这个系统只完成了第一件——给了机器一张票但每个刷票的门口都没人查身份证。本文仅用于安全研究与學習交流请勿对未授权系统进行测试。