Teams Webhook钓鱼攻击:滥用Microsoft Teams功能进行内部钓鱼

本文详细介绍了攻击者如何滥用Microsoft Teams的Webhook和频道邮件功能进行内部钓鱼攻击,包括Webhook枚举、创建和利用方法,以及如何绕过安全防护发送恶意消息到Teams频道。

Wishing: Teams中的Webhook钓鱼攻击

在网络安全不断演变的背景下,常见到为便利性设计的功能导致负面安全后果。Microsoft Teams作为企业通信的重要工具,包含易被滥用的功能和配置。以下项目特别容易被滥用:

  • 默认情况下,用户可以在其有权访问的任何频道中配置传入Webhook。
  • 默认情况下,用户可以查看其有权访问的任何频道中其他用户创建的独特Webhook URL。
  • 在撰写本文时,Webhook无法配置使用身份验证。
  • Teams有一个晦涩的功能,可以为给定频道创建电子邮件地址。此电子邮件可被滥用以向组织的Teams频道发送钓鱼消息。

攻击者的最终结果是多种滥用Teams进行后期利用的方式,主要是发送钓鱼消息。本文将详细探讨这些漏洞,讨论其对组织的影响。

什么是Microsoft连接器?

首先,提供一些关于Teams连接器功能的背景。Microsoft Teams连接器允许用户将外部服务工具直接集成到其Teams频道和对话中。它们旨在通过从各种外部应用程序、服务或平台引入内容和更新来增强团队协作,使其对频道的所有成员可见。一些常见的连接器示例包括Azure、GitHub、Jira、Trello、RSS源等。这些更新作为消息发布在团队的频道内,允许团队成员实时了解信息并作出反应,而无需离开Teams界面。

任何频道的用户都可以通过转到给定频道的设置并单击“连接器”下的编辑按钮来轻松创建这些连接器。这将提供Microsoft批准的连接器列表以供配置。需要注意的是,有数百种可能的连接器。

图1 – 示例连接器目录

一旦选择了连接器,创建过程就像命名连接器、填写所需选项然后创建它一样简单。

图2 – 创建连接器

所有这些连接器都使用Webhook连接到Microsoft Teams。

图3 – 连接器的Webhook URL

什么是Webhook?

Webhook是由特定事件触发的HTTP回调。当此类事件发生时,源站点向为Webhook配置的URL发出HTTP请求。此HTTP请求执行的操作可能有所不同——可能是发送数据(如在POST请求中)、数据修改或任何数量的不同操作。发送的数据通常采用JSON或XML有效负载的形式,然后由接收应用程序处理。

Webhook在接收数据的方式上与API不同。使用API时,数据通过轮询获得,其中应用程序定期向API服务器发出请求以检查新数据。相比之下,Webhook允许服务器在事件发生时立即将数据推送到您的应用程序,因此有时称为“反向API”或“推送API”。这使得Webhook与API相比资源密集度较低,因为它们消除了持续轮询的需要。

Webhook对于自动化任务和集成不同软件应用程序特别有用。它们设置快速,可以自动化数据传输,节省时间和资源。Microsoft允许Webhook连接器作为一种简单的方式将应用程序连接到Microsoft Teams内的Microsoft Teams频道。传入Webhook允许您的用户将文本消息从频道发送到您的Web服务。基于连接器的Webhook允许用户接收来自您的Web服务的通知和消息。

那么,我们为什么关心Webhook?因为我们可以使用它们向配置了连接器的频道发送消息。

这对于钓鱼来说非常棒,因为Teams消息避开了电子邮件过滤器。当通过Webhook进行钓鱼时,消息将显示为是由合法的连接器应用程序发送的,这可能导致目标用户的怀疑减少。

我们如何获取这些Webhook?

我们在GraphRunner中创建了一个模块来枚举受损用户有权访问的已配置Webhook。如果您想尝试,请运行Get-Webhooks以获取更多信息。现在,让我们逐步了解构建此模块所需的逆向工程过程。首先,让我们使用Get-GraphTokens获取目标用户的承载令牌。如果您还不熟悉GraphRunner,请在此处查看。

图4 – GraphRunner – 获取GraphToken

一旦我们有了这个令牌,我们可以将其传递给https://login.microsoftonline.com/,作用域为outlook.office365.com/connectors。使用Burp Suite,我们可以看到POST请求,并在其中收到一个与api.spaces.skype.com绑定的access_token。这种类型的令牌将授予我们访问skype(即Microsoft Teams)的权限。

图5 – api.spaces.skype.com的access_token

接下来,我们将该access_token的内容重命名为单点登录令牌(“Sstoken”),用于在Office及其所有插件中进行无缝身份验证。然后将此请求传递给“outlook.office.com/connectors/Manage/AuthorizeUsingToken?client=SkypeSpaces”。此URI允许用户使用令牌对连接器的管理界面进行身份验证。我们将客户端指定为“SkypeSpaces”,因为这是Microsoft Teams的引用。

图6 – 对Connectors/Manage/AuthorizationUsingToken的GET请求

我们得到的响应为我们提供了大量通过office.com授予访问Microsoft Teams资源的cookie。这些cookie是:

  • BearerTokenFromWorkload
  • SkypeSpaceTokens
  • __RequestionVerificationToken_L2Nvbm51Y3RvCnM1(是的,这个随机数在不同用户之间是相同的)
  • .AspNet.ApplicationCookie
  • X-XSRF-Token

图7 – 来自Connectors/Manage/AuthorizationUsingToken的响应

重要的cookie是BearerTokenFromWorkload和SkypeSpaceTokens。这些提供对某些资源的访问权限(在这种情况下,因为我们将其指向“连接器”,所以所有内容都将专注于连接器应用程序)。

免责声明:我们通过逆向工程推断出此信息,没有Microsoft的官方文档。它可能是错误的,或者随着API的发展而发生变化。

BearerTokenFromWorkload令牌提供对资源的访问权限,而SkypeSpacesTokens提供对由BearerTokenFromWorkload令牌定义的资源的访问类型。SkypeSpaceTokens没有很好的文档记录,但允许Microsoft Teams内的某些功能或集成。与Graphtokens非常相似,SkypeSpaceTokens允许在Microsoft Teams生态空间内进行身份验证和授权,特别是在集成或启用某些功能、外部应用程序和连接器时。

图8 – 解码的BearerTokenFromWorkload和SkypeSpaceToken

现在我们有了适当的cookie和令牌值,我们可以开始枚举与频道中Webhook相关的所有信息。首先我们需要的是连接器配置信息。这可以通过查询“connectors/Manage/Configuration”URI以及租户、团队ID和频道ID轻松完成。然而,当我们发送请求时,我们收到错误消息。

图9 – 错误响应

这不应该发生,因此为了理解问题所在,我们可以使用Burp Suite拦截来自基于Web浏览器的Microsoft Teams版本的合法流量。我们可以看到所有cookie都是相同的,但是,在进一步检查时,我们发现SkypeSpaceToken的大小略有不同。

图10 – 来自Web浏览器的Microsoft Teams的有效请求

通过查看我们的Burp历史记录,我们可以看到这个较小版本的SkypeSpaceToken是从对API https://teams.microsoft.com/api/authsvc/v1.0/authz的POST请求设置的,但是,此令牌值实际上标记为SkypeToken。此API只需要承载令牌进行身份验证,但需要注意的是,此API和令牌仅用于teams.microsoft.com,而不是office.com。

图11 – SkypeToken的创建

这个SkypeToken与之前创建的原始SkypeSpaceToken完全不同。如前所述,关于这些cookie的文档不多,但是,解码JWT值显示此cookie与我们之前收到的cookie非常不同。如下所示,此令牌的内容指示OrganizationID和用户ID,这表明此cookie用于标识那些请求有关频道信息的人,这些频道在多个具有相同频道内权限但不一定是所有者的个人之间共享。

图12 – 解码的SkypeToken

一旦我们有了这个SkypeToken,我们就可以取这个SkypeToken的值并用它替换SkypeSpacesToken的值。我们可以使用PowerShell的“System.Net.CookieContainer”来创建一组临时cookie。一旦我们有了这组新cookie,我们通过搜索SkypeSpacesToken来解析所有存储的cookie。一旦找到,它将该值替换为SkypeToken值。这样做,我们取一个为teams.microsoft.com创建的cookie并将其与office.com一起使用。

图13 – 修改Cookie

通过此修改,我们可以再次运行请求,并且这次我们收到HTTP响应200。这使我们能够查询manage/configuration URI。此页面包含频道中所有已安装Webhook的所有配置信息。

图14 – 解码的Webhook信息

从上面的屏幕截图中可以看到,此请求提供了与频道中Webhook相关的所有信息,但是,它不提供与每个Webhook关联的URL。这就是配置ID值发挥作用的地方。此值是频道中每个Webhook的唯一标识符,用于知道具体与哪个Webhook交互。使用相同的cookie以及以下内容——

  • TeamID
  • TenantID
  • ChannelID
  • Configuration ID

——我们可以向https://outlook.office.com/connectors/IncomingWebhook/Manage/Show发送GET请求,并解析响应以找到Webhook的URL。

图15 – Webhook的URL

使用Get-Webhooks模块,我们可以通过循环遍历用户当前所属的所有Teams频道来请求用户有权访问的所有频道中的所有Webhook。

图16 – Webhook的URL

我们可以用这些Webhook做什么?

让我们制作一个HTTP POST请求,该请求将向配置了连接器的频道发送消息。这可以从互联网完成,无需除了Webhook URL本身之外的任何身份验证。这是因为默认情况下,所有Webhook连接器都没有附加任何身份验证机制(即NTLM哈希、承载令牌)。

这使得连接器非常适合持久性,或通过钓鱼方式重新进入。如果您失去对目标环境的访问权限(凭据更改、会话撤销),但您有有效的Webhook URL,您可以发送内部Teams消息,并额外的好处是显示为配置的连接器应用程序。

这些消息必须遵循特定格式,称为“主要消息卡JSON”。这些消息卡允许用户嵌入图像、链接,甚至操作按钮,当按下时触发某些操作(例如转到特定URL)。Microsoft有一篇关于如何构建此类JSON代码的精彩文章。

图17 – 示例主要消息卡JSON

图18 – Webhook消息示例

枚举频道

了解您的目标并创建真实的借口在这种攻击的成功中起着至关重要的作用。这意味着我们需要一些关于我们使用Webhook钓鱼(“WISHING”)目标的频道的信息。

幸运的是,Microsoft在Graph API中为我们提供了提取此信息的功能。使用Get-ChannelUsersEnum模块,我们可以枚举以下内容:

  • 频道描述
  • 频道成员数量
  • 频道成员列表(包括电子邮件地址)
  • 频道所有者

图19 – GraphRunner的Get-ChannelUsersEnum模块示例

创建您自己的Webhook

如果您有权访问的任何频道中都没有Webhook怎么办?没问题,感谢Microsoft对这些连接器的配置,任何频道的用户都可以创建带有Webhook的连接器。这包括低权限用户。要以编程方式创建Webhook,我们再次使用Burp观察发送到“connectors/IncomingWebhook/Manage/Create”API的POST请求。此API基于webkit表单提交的内容在频道内创建具有特定设置的Webhook。此webkit表单需要几个我们可以自己设置的值,但是,有几个需要由Microsoft生成。这些包括:

  • _RequestVerificationToken
  • AlternativeID值
  • ForwardToEmail值

通过Burp查看创建过程,我们可以看到在webkit表单提交POST请求之前发生对另一个API(“connectors/IncomingWebhook/Manage/New”)的请求。此API很重要,因为这是为webkit生成上述值的地方,但是,就像我们之前枚举Webhook的方式一样,我们首先需要生成SkypeToken cookie来请求它们。一旦我们有了该值并将其替换为我们的SkypeSpaceToken值,我们就可以向此API发送GET请求以生成那些缺失的值。

图20 – 获取创建Webhook所需值的请求

图21 – Webkit表单示例

但这还不是我们需要的全部;如果我们只是向API发送请求,我们会看到收到错误响应。这是因为一个奇怪的要求(我还没有找到原因),我们必须将主或“常规”频道ID指定为“SkypeSpacesTeamId”值,而不是我们想要放置Webhook的频道。这样做,Webhook仍然在正确的频道中创建,因为真实的ChannelId也存储在SSThread参数中。

图22 – Webkit请求 – 响应错误

图23 – Webkit请求,SkypeSpacesTeamID设置为常规频道的ID – 响应成功

虽然这个过程可能看起来很广泛,但模块Create-Webhooks为您完成了所有这些。

图24 – Webkit请求,SkypeSpacesTeamID设置为常规频道的ID – 响应成功

图25 – 频道中创建的Webhook

频道电子邮件枚举

虽然Webhook是在Microsoft Teams内部通信的一种方式,但还有另一种获取访问权限的方式。Microsoft Teams通过“获取电子邮件地址”功能为您提供了覆盖。Microsoft Teams频道中的“获取电子邮件地址”功能允许每个频道拥有自己独特的电子邮件地址。

图26 – Teams频道电子邮件地址创建

此功能使成员或外部用户能够直接将电子邮件发送到特定频道。此功能对于通过电子邮件与客户端或第三方交互的组织特别有用。这些电子邮件地址是一组随机生成的字符,使得未经身份验证难以枚举。此外,默认情况下,每个频道都没有关联的电子邮件地址。相反,当用户通过“获取电子邮件地址”功能或Microsoft Teams频道电子邮件API请求时,它们会被分配。Microsoft声称此功能需要由管理员启用,但是,通过测试,我们可以看到情况并非如此。

图27 – 关于此功能的Microsoft文档

此API查询起来相当简单,我们只需要我们的承载令牌和X-SkypeToken。您会注意到X-SkypeToken看起来像是由https://teams.microsoft.com/api/authsvc/v1.0/authz API创建的SkypeToken——那是因为它是相同的令牌,只是名称不同。一旦我们有了这个X-SkypeToken,我们唯一需要的另一件事是频道ID值。有了这些值,我们可以执行一系列HTTP请求来做不同的事情:

  • GET请求 – 检查频道是否设置了电子邮件地址。如果有,它将响应电子邮件地址和为该电子邮件地址设置的权限。如果没有电子邮件地址,它将响应状态代码“NotFound”。

图28 – 获取频道电子邮件地址信息的GET请求

图29 – 如果没有帐户已创建的响应

  • POST请求 – 此发送请求以创建电子邮件地址。如前所述,此地址是随机生成的,因此我们无法指定设置为什么。但是,我们可以定义“allowedSenderType”。在我们的案例中,我们可以将其设置为“anyone”。这意味着外部用户可以毫无问题地向此地址发送消息。

图30 – 创建频道电子邮件地址的POST请求

  • PUT请求 – 允许我们更改“allowedSenderType”的当前值(如果电子邮件地址已存在)。

图31 – 更新频道电子邮件地址的AllowedSenderType的PUT请求

图32 – 验证更新的频道电子邮件地址的AllowedSenderType的GET请求

模块Get-ChannelEmail可以自动化此过程,首先查找电子邮件地址,如果有,则检查确保其设置为“anyone”。如果没有频道电子邮件地址,此模块然后创建具有“anyone”权限的电子邮件地址。

图33 – GraphRunner的Get-ChannelEmail模块示例

发送频道电子邮件

将频道电子邮件地址设置为anyone后,我们可以从组织外部的任何电子邮件直接向该频道发送电子邮件。虽然这听起来很容易,但Exchange Online Protection(EOP)仍然适用于电子邮件过滤。要向频道电子邮件发送电子邮件,我们有几个选项。如果我们有外部电子邮件地址可以发送,并且有频道电子邮件地址可以发送到,我们可以使用任何邮件客户端从我们的电子邮件地址发送邮件(提示:https://developer.microsoft.com/en-us/microsoft-365/dev-program)创建一个您可以从中进行钓鱼的租户。

否则,我们可以使用像https://github.com/rvrsh3ll/FindIngresEmail这样的工具来查找可能允许通过EOP的域。要发送消息,我们可以使用Send-MailMessage PowerShell命令。有关该命令的更多信息,请查看Microsoft文档:https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage?view=powershell-7.3 我们建议在Azure控制台中使用该命令,因为您的连接将来自Microsoft IP地址,并且不会像从住宅IP地址发送那样被Spamhaus阻止。

1
Send-MailMessage -Body '<h3><b>Required Enrollment: <a href="https://www.google.com">Benefits Enrollment</a>' -To "TestShared - MSFT_Derp_Devs <7d685121.derpdevs.onmicrosoft.com@amer.teams.ms>" -From "noreply@eircom.net" -Subject "Benefits Enrollment" -SmtpServer "amer-teams-ms.mail.protection.outlook.com" -BodyAsHtml

图34 – 成功的Teams电子邮件消息

如您所见,电子邮件成功落地在Teams频道中。由于传入Teams频道的电子邮件通过EOP,来自某些域的地址可能会被阻止,或内容被阻止,或任何其他反垃圾邮件、欺骗或其他过滤器可能适用。有关如何测试此功能的更多信息,请查看BHIS博客此处:https://www.blackhillsinfosec.com/spamming-microsoft-365-like-its-1995/。

此外,频道显示“下载原始电子邮件”链接。一旦用户单击链接,将下载一个.eml文件。打开.eml文件将在用户的默认邮件应用程序(如Outlook)中加载电子邮件。那里存在几种恶意可能性,可以帮助促进初始访问。

Defender洞察

BHIS在2024年1月披露了此问题。在撰写本文时,这些发现已提交给MSRC,并由Microsoft关闭,没有修复。本文的目的是强调Microsoft Teams中这些功能的危险,并帮助防御者防御它们。不幸的是,在Microsoft对这些连接器使用的Webhook强制执行身份验证之前,无法防止外部消息进入。

那么,管理员可以做什么?嗯,Microsoft并没有让它变得容易。截至目前,Microsoft要么允许任何用户安装连接器应用程序,要么不允许任何人。没有中间地带。由于这些对于组织连接和集成来自各种外部应用程序、服务或平台的信息至关重要, outright禁用这些应用程序可能对大多数组织来说不是选项。

图35 – Microsoft Teams管理员组织范围的Teams应用程序设置

在这些问题得到Microsoft解决之前,蓝队需要依赖Microsoft Teams基于消息的检测规则来查找异常消息以检测此类滥用。同样重要的是要注意,如果用户向团队添加连接器,然后离开团队或被禁用或从组织中删除,该连接器(和Webhook)继续工作。

Microsoft确实有一些安全控制来限制用户向频道发送消息的能力。

图36 – Microsoft Teams管理员组织范围的Teams电子邮件集成设置

但是,对此选项的进一步调查显示,当此选项关闭时,仅从Teams UI(Web客户端和桌面客户端)中删除请求或查看它的选项。但是,API仍然允许请求和更新。

图37 – Microsoft Teams客户端缺少频道获取电子邮件地址

图38 – GraphRunner的Get-ChannelEmail模块仍然工作的示例

请务必在下周四,3/21,美国东部时间下午2:05收听Matthew的网络广播: Microsoft Teams滥用与Matthew Eidelberg 您可以在此处注册!

准备好了解更多信息吗? 通过Antisyphon的实惠课程

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计