介绍GraphRunner:针对Microsoft 365的后渗透工具集
作者:Beau Bullock & Steve Borosh
摘要
我们构建了一个名为GraphRunner的后渗透工具集,用于与Microsoft Graph API交互。该工具集提供了多种功能,包括对Microsoft Entra ID(Azure AD)账户进行侦察、持久化和数据窃取。以下是主要功能概述,文末还提供了潜在攻击路径场景,对攻防安全团队成员都颇具参考价值。
主要功能
- 搜索并导出电子邮件
- 搜索并导出用户可访问的SharePoint和OneDrive文件
- 搜索用户可见的所有Teams聊天和频道,并导出完整对话
- 部署恶意应用
- 发现配置不当的暴露邮箱
- 克隆安全组以实施水坑攻击
- 查找可由当前用户直接修改的组,或滥用成员规则获取访问权限的组
- 在所有用户属性中搜索特定术语
- 利用基于Graph API构建的GUI窃取用户账户数据
- 导出条件访问策略
- 导出应用注册和外部应用,包括同意和范围信息,以识别潜在恶意应用
- 在同意授予攻击期间完成OAuth流的工具
- 持续刷新令牌包
- GraphRunner不依赖任何第三方库或模块
- 支持Windows和Linux
工具下载地址:https://github.com/dafthack/GraphRunner/
Graph API
Microsoft Graph API是支持Microsoft云服务运行的核心基础设施之一。从Outlook到SharePoint、Teams再到Entra ID,都依赖此API。尽管用户通常不会直接看到Graph API,但它一直在后台运行。PowerShell模块(如MSOnline或AzureAD模块)和AZ命令行工具(az cli)都使用它。对管理员而言,Graph API是在Azure中执行任务的强大工具。
普通用户的视角
在渗透测试、红队演练、云评估和其他攻击性安全评估中,我们经常获得M365用户账户的访问权限,这可能源于密码喷洒或钓鱼攻击的成功。通过浏览器,我们可以访问电子邮件和文件共享服务等资源,但除了邮件和文件,Microsoft租户中还有许多其他数据点可供收集。Azure(Entra)门户是一个起点,但它可能被锁定,仅允许管理用户使用。
幸运的是,Microsoft 365的大部分功能都需要访问Graph API。即使Azure门户被封锁,通过API仍可访问大部分相同数据,有时还能进行交互。在实际攻防演练中,我们多次遇到这种情况。通过实战演练和研发会议,BHIS内部开始开发这套工具集。
本文详细介绍了工具集的每个部分,并提供了多个攻击路径场景,以展示其应用价值。其中一些路径可能为人熟知,另一些则可能较为新颖。发布此工具集的主要目的是为攻击操作员提供快速识别Microsoft云环境中安全问题的工具,但防御者也可利用它主动识别和缓解安全问题。
GraphRunner的三大组件
- GraphRunner.ps1:一个PowerShell脚本,包含多个模块,用于后渗透侦察、持久化和账户数据窃取。
- GraphRunnerGUI.html:一个HTML图形用户界面,需配合访问令牌使用,提供枚举和从Outlook、SharePoint、OneDrive和Teams等服务窃取数据的多种模块。
- PHPRedirector:一个基本的PHP脚本,用于在OAuth同意流中捕获授权代码,以及一个Python脚本来自动完成流以获取访问令牌。
GraphRunner PowerShell
GraphRunner包含一套PowerShell工具,协助在Microsoft Entra ID(Azure AD)租户后渗透阶段执行各种攻击。大多数模块需要已认证的访问令牌。为此,提供了多个模块来获取和处理用户及应用程序(服务主体)令牌。大多数模块不需要特权账户。
入门指南
将GraphRunner导入新的PowerShell会话:
|
|
模块概述
认证模块
Get-GraphTokens
:以用户身份认证到Microsoft GraphInvoke-RefreshGraphTokens
:使用刷新令牌获取新的访问令牌Get-AzureAppTokens
:以应用身份完成OAuth流以获取访问令牌Invoke-RefreshAzureAppTokens
:使用刷新令牌和应用凭据刷新令牌Invoke-AutoTokenRefresh
:按间隔刷新令牌
侦察与枚举模块
Invoke-GraphRecon
:执行一般侦察,获取组织信息、用户设置、目录同步设置等Invoke-DumpCAPS
:获取条件访问策略Invoke-DumpApps
:获取应用注册和外部企业应用,以及同意和范围信息Get-AzureADUsers
:获取用户目录Get-SecurityGroups
:获取安全组及其成员Get-UpdatableGroups
:获取当前用户可能可修改的组Get-DynamicGroups
:查找动态组并显示成员规则Get-SharePointSiteURLs
:获取当前用户可见的SharePoint站点URL列表Invoke-GraphOpenInboxFinder
:检查列表中每个用户的收件箱是否可读Get-TenantID
:从域名检索租户GUID
持久化模块
Invoke-InjectOAuthApp
:将应用注册注入租户Invoke-SecurityGroupCloner
:克隆安全组,使用相同名称和成员列表,并可注入其他用户Invoke-InviteGuest
:邀请访客用户到租户Invoke-AddGroupMember
:向组添加成员
数据窃取模块
Invoke-SearchSharePointAndOneDrive
:搜索用户可见的所有SharePoint站点和OneDrive驱动器Invoke-ImmersiveFileReader
:使用沉浸式阅读器打开受限文件Invoke-SearchMailbox
:深度搜索用户邮箱,并可导出邮件Invoke-SearchTeams
:搜索当前用户可读的所有Teams消息Invoke-SearchUserAttributes
:在目录中的所有用户属性中搜索术语Get-Inbox
:从邮箱获取最新收件箱项目,可用于读取其他用户邮箱(共享)Get-TeamsChat
:下载完整的Teams聊天对话
Invoke-GraphRunner模块
Invoke-GraphRunner
:运行Invoke-GraphRecon
、Get-AzureADUsers
、Get-SecurityGroups
、Invoke-DumpCAPS
、Invoke-DumpApps
,然后使用default_detectors.json
文件通过Invoke-SearchMailbox
、Invoke-SearchSharePointAndOneDrive
和Invoke-SearchTeams
进行搜索。
补充模块
Invoke-AutoOAuthFlow
:在用户同意应用注册时自动完成OAuth流以获取访问和刷新密钥Invoke-DeleteOAuthApp
:删除OAuth应用Invoke-DeleteGroup
:删除组Invoke-RemoveGroupMember
:从组中移除用户/成员的模块Invoke-DriveFileDownload
:以当前用户身份从SharePoint和OneDrive下载单个文件Invoke-CheckAccess
:检查令牌是否有效Invoke-HTTPServer
:一个基本的Web服务器,用于访问从Invoke-SearchMailbox
输出的emailviewerInvoke-BruteClientIDAccess
:针对MSGraph测试不同的client_id以确定权限Invoke-ImportTokens
:从其他工具导入令牌以供GraphRunner使用Get-UserObjectID
:检索用户的Object ID
认证
从Get-GraphTokens
模块开始认证。该模块将启动设备代码登录,允许您从浏览器会话认证PowerShell会话。访问和刷新令牌将写入全局变量$tokens
,租户ID将写入变量$tenantid
。要与其他GraphRunner模块一起使用,请使用Tokens标志(例如:Invoke-DumpApps -Tokens $tokens
)。
访问令牌通常有一小时的过期时间,因此需要偶尔刷新。如果您已运行Get-GraphTokens
命令,当您运行Invoke-RefreshGraphTokens
以获取新令牌集时,您的刷新令牌将自动从$tokens
变量中使用。
GraphRunner还包括以服务主体身份认证的模块。这对于利用应用注册(如本文后续持久化部分所述)非常有用。Get-AzureAppTokens
模块可协助完成OAuth流以获取Azure应用注册的访问令牌。获取授权代码后,可将其与一组应用注册凭据(客户端ID和密钥)一起使用以完成流。
侦察与枚举
GraphRunner包含多个侦察模块,用于确定配置设置、列出对象并识别租户中的攻击路径。Invoke-GraphRecon
模块收集租户的一般信息,包括主要联系信息、目录同步设置和用户设置,例如用户是否能够创建应用、创建组或同意应用。显示租户的主要联系信息以及目录同步设置和用户设置。
授权策略部分包括配置设置,例如用户是否可以读取自己的Bitlocker密钥、谁可以邀请外部用户、是否阻止MSOL PowerShell等。
Invoke-GraphRecon
模块还有一个名为“PermissionEnum”的开关。如果设置此开关,它将使用未文档化的“Estimate Access”API暴力破解近400个操作(权限参考:https://learn.microsoft.com/en-us/azure/active-directory/roles/permissions-reference)以确定当前用户允许执行哪些操作。这对于发现用户在租户中能够执行的独特操作非常有用。此外,当我们在本文后续的组编辑部分深入时,此方法有助于确定可能已更改的访问权限。
Invoke-DumpCAPS
模块从租户导出条件访问策略。此模块使用传统的Azure Active Directory Graph API(graph.windows.net)拉取策略。
本文后续关于注入应用注册(Invoke-InjectOAuthApp
)的模块促成了Invoke-DumpApps
模块的创建。此模块可协助识别恶意应用注册。它将从租户导出Azure应用注册列表,包括权限范围和已同意应用的用户。此外,它将列出不属于当前租户或Microsoft主应用租户的外部应用。这是查找用户可能已同意的第三方外部应用的一种方式。
Get-AzureADUsers
和Get-SecurityGroups
模块可用于从租户导出用户和组。
基于组的攻击是GraphRunner功能中较为有趣的领域之一。我们攻击M365组的第一个用例涉及更改某些组的成员身份,即使作为非管理用户也是如此。例如,GraphRunner具有模块,可帮助利用Microsoft 365组的默认行为,即组织中的任何人都可以加入它们。每当创建团队时,也会创建Microsoft 365组。随之自动创建SharePoint站点、邮箱、Teams频道等。
如Microsoft文档所述(下图),Microsoft 365组的默认行为使其对所有人开放。此外,在某些情况下,安全组也可以配置为可加入。
这就是Get-UpdatableGroups
模块的用武之地。此模块还利用“Estimate Access”API确定当前用户是否有能力更新租户中的组。它将收集租户中的所有组并逐一检查以确定它们是否可修改。
如果您找到可修改的组,这意味着当前用户有能力向该组添加成员,包括您自己、其他租户成员甚至访客。这可能导致权限提升场景,如我们在后文的攻击路径部分所示。
类似地,“动态组”是Microsoft 365中另一个有趣的攻击路径。动态组是使用动态组成员规则创建的组。创建时,动态组配置有一组规则,自动将具有某些属性的对象处理到组中。这些组可以包括各种参数,例如用户的电子邮件、位置、职务、设备等。它们可以帮助自动将用户添加到组中,但配置不当可能被攻击者滥用。
在上面的示例中,此动态组配置为添加其用户主体名称包含“admin”一词的任何用户。通过简单地邀请一个电子邮件地址中包含“admin”的访客用户到租户,就可以利用此场景。在作为访客添加到租户后,名称中包含“admin”的账户将自动添加到动态组。
GraphRunner通过Get-DynamicGroups
模块帮助查找动态组。在列出租户中的动态组后,需要分析成员规则以确定可利用的潜力。
Get-SharePointSiteURLs
模块与前面提到的组模块相辅相成。它使用Graph Search API尝试定位用户有权访问的所有唯一站点。在执行任何基于组的滥用之前和之后运行它,以确定您获得了哪些新站点的访问权限,非常有用。
2017年,我(Beau)写了一篇关于滥用Exchange邮箱权限的文章。当时,我为MailSniper编写了一个名为Invoke-OpenInboxFinder
的模块,协助查找配置为允许组织中其他用户访问的邮箱。该模块利用Exchange Web Services和Outlook Web Access。事实证明,可以通过Microsoft Graph API执行相同类型的邮箱枚举。GraphRunner具有Invoke-GraphOpenInboxFinder
模块来执行此任务。
为此,您需要一个范围为Mail.Read.Shared权限或Mail.ReadWrite.Shared权限的令牌。这可以通过同意具有此范围权限的应用程序来实现。一种快速简单的方法是利用Graph Explorer。它是一个用于测试Graph API调用的知名应用程序,您可以在此处同意特定权限:https://developer.microsoft.com/en-us/graph/graph-explorer。同意后,您可以单击“Access token”选项卡查看您的令牌,然后将其设置为GraphRunner会话中的$tokens.access_token
变量。
现在对用户列表运行Invoke-GraphOpenInboxFinder
模块将尝试访问提供列表中的每个收件箱。如果用户将其收件箱权限设置得太宽,当前用户可能能够读取其收件箱中的消息。
持久化
在维持访问方面,GraphRunner有一些模块可以帮助在租户中建立不同级别的持久化。将应用程序部署到租户在多种场景中都很有趣。默认情况下,用户可以创建应用程序。但是,默认情况下,他们不能添加管理权限,例如Directory.ReadWrite.All。然而,他们可以添加许多默认不需要管理员同意的委托权限。这些大多数不需要管理员同意的权限用于执行常见任务,例如读取电子邮件(Mail.Read)、列出目录中的用户(User.ReadBasic.All)、导航SharePoint和OneDrive(Files.ReadWrite.All和Sites.ReadWrite.All)等。
通过部署具有这些权限的应用,然后以我们已入侵的用户身份同意它,我们可以利用与应用程序关联的服务主体凭据来访问用户的账户。如果受入侵用户更改其密码,应用仍保留对其账户的访问权限。如果受入侵用户的所有会话被终止,我们仍然有权访问,直到访问令牌过期(默认为1小时)以操作用户。
Invoke-InjectOAuthApp
模块是一个用于自动化将应用注册部署到Microsoft租户的工具。如果Azure门户被锁定,这可能提供额外的应用部署机制,前提是允许用户在租户中注册应用。
此模块有一些硬编码的范围设置,用于快速部署某些类型的应用,但也可以输入自定义值。例如,当将-scope
参数设置为“op backdoor”时,工具将创建一个应用并向其添加大量常见权限,包括对Mail、Files、Teams等的访问。这些权限都不需要管理员同意。
应用部署后,同意URL会自动生成并显示在终端中(上图为绿色)。此URL是自定义的,与特定的应用注册绑定,包括所有请求的范围项。当用户访问此URL时,他们将