Alt text

CHAPTER TWO

  • Enumeration of Azure Services
    • Storage Accounts
    • Key vaults
    • Blobs
    • Automation Accounts
    • Deployment History
  • Privilege Escalation
    • RBAC roles
    • Azure AD Roles
    • Across subscriptions
    • Custom Roles
    • Resource Ownership
    • Dynamic Groups

Enumeration

Azure Portal

The Azure Portal is a web console that can be used to manage Azure account and its resources.

  • It is a GUI alternative to tools like PowerShell modules and Azure CLI.

Default User Permissions

Microsoft Fundamentals

A normal user can:

- Read all users, Groups, Applications, Devices, Roles, Subscriptions, and their public properties
- Invite Guests
- Create Security groups
- Read non-hidden Group memberships
- Add guests to Owned groups
- Create new application
- Add up to 50 devices to Azure

MSGraph Module

[NOTE] It will replace AzureAD Module

Mostly it changed the commands from AzureAD to Mg. As we will see in the examples. So Get-AzureADUser = Get-MgUser

To install MSGraph:

Install-Module Microsoft.Graph

Connect to MSGraph:

Connect-MgGraph

We can connect with Access Token (obtained using AZ Powershell module or other method):

$Token = eyJ0...
Connect-MgGraph AccessToken ($Token | ConvertTo-SecureString -AsPlainText -Force)

Get the current session state:

Get-MgContext

Get details of the current tenant (Available in Beta version)

Get-MgOrganization | fl *

Users

Enumerate all users

Get-MgUser -All

Enumerate a specific user

Get-MgUser -UserId test@tenant.onmicrosoft.com

Search for a user based on string in first characters of DisplayName or userPrincipalName (wildcard not supported)

Get-MgUser -Filter "startsWith(DisplayName, 'a')" -ConsistencyLevel eventual

Search for users who contain the word “admin” in their Display name:

Get-MgUser -All | ?{ $_.Displayname -match "admin" }
Get-MgUser -Search '"DisplayName:admin"' -ConsistencyLevel eventual

List all the attributes for a user

Get-MgUser -UserId test@tenant.onmicrosoft.com | fl *
Get-MgUser -UserId test@tenant.onmicrosoft.com | % { $_.PSObject.Properties.Name }

Search attributes for all users that contain the string “password”:

Get-MgUser -All | % { $Properties = $_; $Properties.PSObject.Properties.Name | % { if ($Properties.$_ -match 'password') { "$($Properties.UserPrincipalName) - $_ - $($Properties.$_)" } } }

All users who are synced from on-prem

Get-MgUser -All | ? { $_.OnPremisesSecurityIdentifier -ne $null }

All users who are from Azure AD

Get-MgUser -All | ? { $_.OnPremisesSecurityIdentifier -eq $null }

Objects created by any user (use -ObjectId for a specific user)

Get-MgUserCreatedObject -UserId test@tenant.onmicrosoft.com | fl *

Objects owned by a specific user

Get-MgUserOwnedObject -UserId test@tenant.onmicrosoft.com | fl *

Groups

List all Groups

Get-MgGroup -All

Enumerate a specific group

Get-MgGroup -GroupId 783a312d-0de2-4490-92e4-539b0e4ee03e

Search for a group based on string in first characters of DisplayName (wildcard not supported)

Get-MgGroup -ConsistencyLevel eventual -Search '"DisplayName:A"'

To search for groups which contain the word “admin” in their name:

Get-MgGroup -ConsistencyLevel eventual -Search '"DisplayName:Admin"'

Get Groups that allow Dynamic membership

Get-MgGroup | ? { $_.GroupTypes -eq 'DynamicMembership' }

All groups that are synced from on-prem (note that security groups are not synced)

Get-MgGroup -All | ? { $_.OnPremisesSecurityIdentifier -ne $null }

All groups that are from Azure AD

Get-MgGroup -All | ? { $_.OnPremisesSecurityIdentifier -eq $null }

Default security groups in AD: Active Directory Default Security Groups by Operating System Version

Get members of a group

Get-MgGroupMember -GroupId 783a312d-0de2-4490-92e4-539b0e4ee03e

Get groups and roles where the specified user is a member

(Get-MgUserMemberOf -UserId test@tenant.onmicrosoft.com).AdditionalProperties

Roles

Get all available role templates

Get-MgDirectoryRoleTemplate

Get all enabled roles (a built-in role must be enabled before usage)

Get-MgDirectoryRole

Enumerate users to whom roles are assigned

$RoleId = (Get-MgDirectoryRole -Filter "DisplayName eq 'Global Administrator'").Id
(Get-MgDirectoryRoleMember -DirectoryRoleId $RoleId).AdditionalProperties

Devices

Get all Azure joined and registered devices

Get-MgDevice All | fl *

List all the active devices (and not the stale devices)

Get-MgDevice All | ? { $_.ApproximateLastSignInDateTime -ne $null }

List Registered owners of all the devices

$Ids = (Get-MgDevice All).Id; foreach($i in $Ids) { (Get-MgDeviceRegisteredOwner -DeviceId $i).AdditionalProperties }
$Ids = (Get-MgDevice All).Id; foreach($i in $Ids) { (Get-MgDeviceRegisteredOwner -DeviceId $i).AdditionalProperties.userPrincipalName }

List Registered users of all the devices

$Ids = (Get-MgDevice All).Id; foreach($i in $Ids) { (Get-MgDeviceRegisteredUser -DeviceId $i).AdditionalProperties }
$Ids = (Get-MgDevice All).Id; foreach($i in $Ids) { (Get-MgDeviceRegisteredUser -DeviceId $i).AdditionalProperties.userPrincipalName }

List devices owned by a user

(Get-MgUserOwnedDevice -UserId user@tenant.onmicrosoft.com).AdditionalProperties

List devices registered by a user

(Get-MgUserRegisteredDevice -UserId user@tenant.onmicrosoft.com).AdditionalProperties

List devices managed using Intune

Get-MgDevice -All | ? { $_.IsCompliant -eq "True" } | fl *

Apps

Get all the application objects registered with the current tenant (visible in App Registrations in Azure portal). An application object is the global representation of an app.

Get-MgApplication -All

Get all details about an application

Get-MgApplicationByAppId -AppId f072c4a6-b440-40de-983f-a7f3bd317d8f | fl *

Get an application based on the display name

Get-MgApplication -All | ? { $_.DisplayName -match "app" }

The Get-MgApplication will show all the applications details including password but password value is not shown. List all the apps with an application password

Get-MgApplication -All | ? { $_.PasswordCredentials -ne $null }

Get owner of an application

(Get-MgApplicationOwner -ApplicationId 35589758-714e-43a9-be9e-94d22fdd34f6).AdditionalProperties.userPrincipalName

Get Apps where a User has a role (exact role is not shown)

Get-MgUserAppRoleAssignment -UserId user@tenant.onmicrosoft.com | fl *

Get Apps where a Group has a role (exact role is not shown)

Get-MgGroupAppRoleAssignment -GroupId 57ada729-a581-4d6f-9f16-3fe0961ada82 | fl *

Service Principals

Enumerate Service Principals (visible as Enterprise Applications in Azure Portal).

  • Service principal is local representation for an app in a specific tenant and it is the security object that has privileges.

This is the ‘service account’!

Get-MgServicePrincipal -All

Get all details about a service principal

Get-MgServicePrincipal -ServicePrincipalId fd518680-b290-4db2-b92a-5dbd025c6791 | fl *

Get a service principal based on the display name

Get-MgServicePrincipal All | ? { $_.DisplayName -match "app" }

List all the service principals with an application password

Get-MgServicePrincipal All | ? { $_.KeyCredentials -ne $null }

Get owner of a service principal

(Get-MgServicePrincipalOwner -ServicePrincipalId fd518680-b290-4db2-b92a-5dbd025c6791).AdditionalProperties.userPrincipalName

Get objects owned by a service principal

Get-MgServicePrincipalOwnedObject -ServicePrincipalId fd518680-b290-4db2-b92a-5dbd025c6791

Get objects created by a service principal

Get-MgServicePrincipalCreatedObject -ServicePrincipalId fd518680-b290-4db2-b92a-5dbd025c6791

Get group and role memberships of a service principal

Get-MgServicePrincipalMemberOf -ServicePrincipalId fd518680-b290-4db2-b92a-5dbd025c6791 | fl *

AzureAD Module

[NOTE] Its available until june 2025. It will be replaced by MSGraph module.

AzureAD is a PowerShell module from Microsoft for managing Azure AD.

  • Does not show all the properties of Azure AD objects and the documentation is not good. Still useful to some extent!
  • Can be used only to interact with Azure AD, no access to Azure resources.

Please note that if the module is not present on your machine, you can use Install-Module AzureAD command or:

  • Download it from PowerShell Gallery - AzureAD Module
  • Rename the .nukpkg to .zip and extract it
  • Then import
Import-Module C:\AzAD\Tools\AzureAD\AzureAD.psd1

To be able to use the PowerShell module, we must connect to Azure AD first:

Connect-AzureAD

Using credentials from the command line (a PSCredential object can be used too):

$creds = Get-Credential
Connect-AzureAD -Credential $creds
$passwd = ConvertTo-SecureString "password123" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential("user@tenant.onmicrosoft.com", $passwd)
Connect-AzureAD -Credential $creds

Get the current session state:

Get-AzureADCurrentSessionInfo

Get details of the current tenant:

Get-AzureADTenantDetail

Users

Enumerate all users:

Get-AzureADUser -All $true

Enumerate a specific user:

Get-AzureADUser -ObjectId test@tenant.onmicrosoft.com

Search for a user based on string in first characters of DisplayName or userPrincipalName (wildcard not supported):

Get-AzureADUser -SearchString "admin"

Search for users who contain the word “admin” in their Display name:

Get-AzureADUser -All $true | ?{ $_.Displayname -match "admin" }

List all the attributes for a user:

Get-AzureADUser -ObjectId test@tenant.onmicrosoft.com | Format-List *

List all the attributes’ names for a user:

Get-AzureADUser -ObjectId test@tenant.onmicrosoft.com | ForEach-Object { $_.PSObject.Properties.Name }

Search attributes for all users that contain the string “password”:

Get-AzureADUser -All $true | ForEach-Object {
    $Properties = $_
    $Properties.PSObject.Properties.Name | ForEach-Object {
        if ($Properties.$_ -match 'password') {
            "$($Properties.UserPrincipalName) - $_ - $($Properties.$_)"
        }
    }
}

All users who are synced from on-prem:

Get-AzureADUser -All $true | Where-Object { $_.OnPremisesSecurityIdentifier -ne $null }

All users who are from Azure AD:

Get-AzureADUser -All $true | Where-Object { $_.OnPremisesSecurityIdentifier -eq $null }

Objects created by any user (use -ObjectId for a specific user):

Get-AzureADUser | Get-AzureADUserCreatedObject

Objects owned by a specific user:

Get-AzureADUserOwnedObject -ObjectId test@tenant.onmicrosoft.com

Groups

List all Groups:

Get-AzureADGroup -All $true

Enumerate a specific group:

Get-AzureADGroup -ObjectId 783a312d-0de2-4490-92e4-539b0e4ee03e

Search for a group based on string in first characters of DisplayName (wildcard not supported):

Get-AzureADGroup -SearchString "admin" | fl *

To search for groups which contain the word admin in their name:

Get-AzureADGroup -All $true |?{$_.Displayname -match "admin"}

Get Groups that allow Dynamic membership (Note the cmdlet name):

Get-AzureADMSGroup | ?{$_.GroupTypes -eq 'DynamicMembership'}

All groups that are synced from on-prem (note that security groups are not synced):

Get-AzureADGroup -All $true | ?{$_.OnPremisesSecurityIdentifier -ne $null}

All groups that are from Azure AD:

Get-AzureADGroup -All $true | ?{$_.OnPremisesSecurityIdentifier -eq $null}

Get members of a group:

Get-AzureADGroupMember -ObjectId 783a312d-0de2-4490-92e4-539b0e4ee03e

Get groups and roles where the specified user is a member:

Get-AzureADUser -SearchString 'test' | Get-AzureADUserMembership
Get-AzureADUserMembership -ObjectId test@tenant.onmicrosoft.com

Roles

Get all available role templates:

Get-AzureADDirectoryroleTemplate

Get all enabled roles (a built-in role must be enabled before usage):

Get-AzureADDirectoryRole

Enumerate users to whom roles are assigned:

Get-AzureADDirectoryRole -Filter "DisplayName eq 'Global Administrator'" | Get-AzureADDirectoryRoleMember

Devices

Get all Azure joined and registered devices:

Get-AzureADDevice -All $true | fl *

Get the device configuration object (note the RegistrationQuota in the output):

Get-AzureADDeviceConfiguration | fl *

List all the active devices (and not the stale devices):

Get-AzureADDevice -All $true | ?{$_.ApproximateLastLogonTimeStamp -ne $null}

List Registered owners of all the devices:

Get-AzureADDevice -All $true | Get-AzureADDeviceRegisteredOwner
Get-AzureADDevice -All $true | %{if($user=Get-AzureADDeviceRegisteredOwner -ObjectId $_.ObjectID){$_;$user.UserPrincipalName;"`n"}}

List Registered users of all the devices:

Get-AzureADDevice -All $true | Get-AzureADDeviceRegisteredUser
Get-AzureADDevice -All $true | %{if($user=Get-AzureADDeviceRegisteredUser -ObjectId $_.ObjectID){$_;$user.UserPrincipalName;"`n"}}

List devices owned by a user:

Get-AzureADUserOwnedDevice -ObjectId user@tenant.onmicrosoft.com

List devices registered by a user:

Get-AzureADUserRegisteredDevice -ObjectId user@tenant.onmicrosoft.com

List devices managed using Intune:

Get-AzureADDevice -All $true | ?{$_.IsCompliant -eq "True"}

Apps

Get all the application objects registered with the current tenant (visible in App Registrations in Azure portal). An application object is the global representation of an app:

Get-AzureADApplication -All $true

Get all details about an application:

Get-AzureADApplication -ObjectId a1333e88-1278-41bf-8145-155a069ebed0 | fl *

Get an application based on the display name:

Get-AzureADApplication -All $true | ?{$_.DisplayName -match "app"}

The Get-AzureADApplicationPasswordCredential cmdlet will show the applications with an application password but the password value is not shown. List all the apps with an application password:

Get-AzureADApplication -All $true | %{if(Get-AzureADApplicationPasswordCredential -ObjectID $_.ObjectID){$_}}

Get Owner of an Application:

Get-AzureADApplication -ObjectId a1333e88-1278-41bf-8145-155a069ebed0 | Get-AzureADApplicationOwner | fl *

Get Apps Where a User Has a Role (Exact Role Not Shown):

Get-AzureADUser -ObjectId user@tenant.onmicrosoft.com | Get-AzureADUserAppRoleAssignment | fl *

Get Apps Where a Group Has a Role (Exact Role Not Shown):

Get-AzureADGroup -ObjectId 57ada729-a581-4d6f-9f16-3fe0961ada82 | Get-AzureADGroupAppRoleAssignment | fl *

Service Principals

Enumerate Service Principals (visible as Enterprise Applications in Azure Portal). A service principal is the local representation of an app in a specific tenant and is the security object that has privileges.

This is the service account!

Service Principals can be assigned Azure roles.

Get all service principals:

Get-AzureADServicePrincipal -All $true

Get all details about a service principal:

Get-AzureADServicePrincipal -ObjectId cdddd16e-2611-4442-8f45-053e7c37a264 | fl *

Get a service principal based on the display name:

Get-AzureADServicePrincipal -All $true | ?{$_.DisplayName -match "app"}

List all the service principals with an application password:

Get-AzureADServicePrincipal -All $true | %{if(Get-AzureADServicePrincipalKeyCredential -ObjectID $_.ObjectID){$_}}

Get owner of a service principal:

Get-AzureADServicePrincipal -ObjectId cdddd16e-2611-4442-8f45-053e7c37a264 | Get-AzureADServicePrincipalOwner | fl *

Get objects owned by a service principal:

Get-AzureADServicePrincipal -ObjectId cdddd16e-2611-4442-8f45-053e7c37a264 | Get-AzureADServicePrincipalOwnedObject

Get objects created by a service principal:

Get-AzureADServicePrincipal -ObjectId cdddd16e-2611-4442-8f45-053e7c37a264 | Get-AzureADServicePrincipalCreatedObject

Get group and role memberships of a service principal:

Get-AzureADServicePrincipal -ObjectId cdddd16e-2611-4442-8f45-053e7c37a264 | Get-AzureADServicePrincipalMembership | fl *

Az PowerShell

Az PowerShell is a module from Microsoft for managing Azure resources

  • Please note that if the module is not present on your machine, you can use Install-Module Az command
  • The Azure Az PowerShell module is a rollup module. Installing it downloads the generally available Az PowerShell modules, and makes their cmdlets available for use

Connect to Azure AD:

Connect-AzAccount

Using credentials from command line (PSCredential object and access tokens can be used too):

$creds = Get-Credential
Connect-AzAccount -Credential $creds
$passwd = ConvertTo-SecureString "password" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("user@tenant.onmicrosoft.com", $passwd)
Connect-AzAccount -Credential $creds

Az PowerShell can enumerate both Azure AD and Azure Resources

All the Azure AD cmdlets have the format -AzAD:

Get-Command *azad*
Get-AzADUser

Cmdlets for other Azure resources have the format Az:

Get-Command *az*
Get-AzResource

Find cmdlets for a particular resource. For example, VMs:

Get-Command *azvm*
Get-Command -Noun *vm* -Verb Get
Get-Command *vm*

Get the information about the current context (Account, Tenant, Subscription etc.):

Get-AzContext

List all available contexts:

Get-AzContext -ListAvailable

Enumerate subscriptions accessible by the current user:

Get-AzSubscription

Enumerate all resources visible to the current user:

Get-AzResource

Enumerate all Azure RBAC role assignments:

Get-AzRoleAssignment

AAD Users

Enumerate all users:

Get-AzADUser

Enumerate a specific user:

Get-AzADUser -UserPrincipalName test@tenant.onmicrosoft.com

Search for a user based on string in first characters of DisplayName (wildcard not supported):

Get-AzADUser -SearchString "admin"

Search for users who contain the word admin in their Display name:

Get-AzADUser | ?{$_.Displayname -match "admin"}

AAD Groups

List all groups:

Get-AzADGroup

Enumerate a specific group:

Get-AzADGroup -ObjectId 783a312d-0de2-4490-92e4-539b0e4ee03e

Search for a group based on string in first characters of DisplayName (wildcard not supported):

Get-AzADGroup -SearchString "admin" | fl *

To search for groups which contain the word “admin” in their name:

Get-AzADGroup | ?{$_.Displayname -match "admin"}

Get members of a group:

Get-AzADGroupMember -ObjectId 783a312d-0de2-4490-92e4-539b0e4ee03e

AAD Apps

Get all the application objects registered with the current tenant (visible in App Registrations in Azure portal). An application object is the global representation of an app:

Get-AzADApplication

Get all details about an application:

Get-AzADApplication -ObjectId a1333e88-1278-41bf-8145-155a069ebed0

Get an application based on the display name:

Get-AzADApplication | ?{$_.DisplayName -match "app"}

The Get-AzADAppCredential will show the applications with an application password but password value is not shown. List all the apps with an application password:

Get-AzADApplication | %{if(Get-AzADAppCredential -ObjectID $_.ID){$_}}

AAD Service Principals

Enumerate Service Principals (visible as Enterprise Applications in Azure Portal). Service principal is local representation for an app in a specific tenant and it is the security object that has privileges.

This is the service account!

Service Principals can be assigned Azure roles.

Get all service principals:

Get-AzADServicePrincipal

Get all details about a service principal:

Get-AzADServicePrincipal -ObjectId cdddd16e-2611-4442-8f45-053e7c37a264

Get a service principal based on the display name:

Get-AzADServicePrincipal | ?{$_.DisplayName -match "app"}

Azure CLI (az cli)

A set of commands used to create and manage Azure resources

  • Can be installed on multiple platforms and can be used with multiple clouds.
  • Available in Cloud Shell too.

Install using MSI - Azure CLI

To be able to use az cli, we must connect to Azure AD first (opens up a login page using Default browser):

az login

Using credentials from command line (service principals and managed identity for VMs is also supported):

az login -u test@tenant.onmicrosoft.com -p SuperVeryEasytoGuessPassword@1234

If the user has no permissions on the subscription:

az login -u test@tenant.onmicrosoft.com -p SuperVeryEasytoGuessPassword@1234 --allow-no-subscriptions

You can configure az cli to set some default behaviour (output type, location, resource group etc.):

az configure

We can search for popular commands (based on user telemetry) on a particular topic!

To find popular commands for VMs:

az find "vm"

To find popular commands within az vm:

az find "az vm"

To find popular subcommands and parameters within az vm list:

az find "az vm list"

We can format output using the –output parameter. The default format is JSON. You can change the default if u want.

List all the users in Azure AD and format output in table:

az ad user list --output table

(JMES PATH QUERY)

List only the userPrincipalName and givenName (case sensitive) for all the users in Azure AD and format output in table. Az cli uses JMESPath (pronounced James path) query:

az ad user list --query "[].[userPrincipalName,displayName]" --output table

List only the userPrincipalName and givenName (case sensitive) for all the users in Azure AD, rename the properties and format output in table:

az ad user list --query "[].{UPN:userPrincipalName, Name:displayName}" --output table

We can use JMESPath query on the results of JSON output. Add –query-examples at the end of any command to see examples:

az ad user show list --query-examples

Get details of the current tenant (uses the account extension):

az account tenant list

Get details of the current subscription (uses the account extension):

az account subscription list

List the current signed-in user:

az ad signed-in-user show

AAD Users

Enumerate all users:

az ad user list
az ad user list --query "[].[displayName]" -o table

Enumerate a specific user (lists all attributes):

az ad user show --id test@tenant.onmicrosoft.com

Search for users who contain the word admin in their Display name (case sensitive):

az ad user list --query "[?contains(displayName,'admin')].displayName"

When using PowerShell, search for users who contain the word admin in their Display name. This is NOT case-sensitive:

az ad user list | ConvertFrom-Json | %{$_.displayName -match "admin"}

All users who are synced from on-prem:

az ad user list --query "[?onPremisesSecurityIdentifier!=null].displayName"

All users who are from Azure AD:

az ad user list --query "[?onPremisesSecurityIdentifier==null].displayName"

AAD Groups

List all Groups:

az ad group list
az ad group list --query "[].[displayName]" -o table

Enumerate a specific group using display name or object id:

az ad group show -g "VM Admins"
az ad group show -g 783a312d-0de2-4490-92e4-539b0e4ee03e

Search for groups that contain the word “admin” in their Display name (case sensitive) - run from cmd:

az ad group list --query "[?contains(displayName,'admin')].displayName"

When using PowerShell, search for groups that contain the word admin in their Display name. This is NOT case-sensitive:

az ad group list | ConvertFrom-Json | %{$_.displayName -match "admin"}

All groups that are synced from on-prem:

az ad group list --query "[?onPremisesSecurityIdentifier!=null].displayName"

All groups that are from Azure AD:

az ad group list --query "[?onPremisesSecurityIdentifier==null].displayName"

Get members of a group:

az ad group member list -g "VM Admins" --query "[].[displayName]" -o table

Check if a user is a member of the specified group:

az ad group member check --group "VM Admins" --member-id b71d21f6-8e09-4a9d-932a-cb73df519787

Get the object IDs of the groups of which the specified group is a member:

az ad group get-member-groups -g "VM Admins"

AAD Apps

Get all the application objects registered with the current tenant (visible in App Registrations in Azure portal). An application object is the global representation of an app:

az ad app list
az ad app list --query "[].[displayName]" -o table

Get all details about an application using identifier uri, application id, or object id:

az ad app show --id a1333e88-1278-41bf-8145-155a069ebed0

Get an application based on the display name (Run from cmd):

az ad app list --query "[?contains(displayName,'app')].displayName"

When using PowerShell, search for apps that contain the word slack in their Display name. This is NOT case-sensitive:

az ad app list | ConvertFrom-Json | %{$_.displayName -match "slack"}

Get owner of an application:

az ad app owner list --id a1333e88-1278-41bf-8145-155a069ebed0 --query "[].[displayName]" -o table

List apps that have password credentials:

az ad app list --query "[?passwordCredentials != null].displayName"

List apps that have key credentials (use of certificate authentication):

az ad app list --query "[?keyCredentials != null].displayName"

AAD Service Principals

Enumerate Service Principals (visible as Enterprise Applications in Azure Portal). Service principal is local representation for an app in a specific tenant and it is the security object that has privileges.

This is the service account!

Service Principals can be assigned Azure roles

Get all service principals:

az ad sp list --all
az ad sp list --all --query "[].[displayName]" -o table

Get all details about a service principal using service principal id or object id:

az ad sp show --id cdddd16e-2611-4442-8f45-053e7c37a264

Get a service principal based on the display name:

az ad sp list --all --query "[?contains(displayName,'app')].displayName"

When using PowerShell, search for service principals that contain the word slack in their Display name. This is NOT case-sensitive:

az ad sp list --all | ConvertFrom-Json | %{$_.displayName -match "slack"}

Get owner of a service principal:

az ad sp owner list --id cdddd16e-2611-4442-8f45-053e7c37a264 --query "[].[displayName]" -o table

Get service principals owned by the current user:

az ad sp list --show-mine

List apps that have password credentials:

az ad sp list --all --query "[?passwordCredentials != null].displayName"

List apps that have key credentials (use of certificate authentication):

az ad sp list -all --query "[?keyCredentials != null].displayName"

Authentication and APIs

Microsoft identity platform uses OpenID Connect (OIDC) for authentication and OAuth 2.0 for authorization.

  • Azure AD supports multiple types of authentication like SAML 2.0, OIDC, OAuth 2.0, and Legacy authentication protocols for synchronization like -Header-based, LDAP, Kerberos Constrained Delegation, etc.

An application (OAuth Client - web app, mobile apps, cli apps) can sign in to the Authorization server, get bearer tokens to access Resource server (Microsoft Graph and other APIs).

Alt text

Basic Sign-In

Alt text

Basic Sign-In with Access Token Acquisition

In this case the Web Server/Web App needs to access a Web API on behalf of the user.

  • This is achieved by acquisition of tokens using the OAuth authorization code flow.

Alt text

Tokens

OAuth 2.0 and OIDC use bearer tokens which are JSON Web Tokens.

  • A bearer token, as the name suggests, grants the bearer access to a protected resource.

There are three types of tokens used in OIDC:

  • Access Tokens: The client presents this token to the resource server to access resources. It can be used only for a specific combination of user, client, and resource and cannot be revoked until expiry - that is 1 hour by default.
  • ID Tokens: The client receives this token from the authorization server. It contains basic information about the user. It is bound to a specific combination of user and client.
  • Refresh Tokens: Provided to the client with access token. Used to get new access and ID tokens. It is bound to a specific combination of user and client and can be revoked. Default expiry is 90 days for inactive refresh tokens and no expiry for active tokens.

Az Powershell

  • Both Az PowerShell and AzureAD modules allow the use of Access tokens for authentication.

Usually tokens contain all the claims (including that for MFA and Conditional Access etc.) so they are useful in bypassing such security controls.

If you are already connected to a tenant, request an access token for resource manager (ARM):

Get-AzAccessToken
(Get-AzAccessToken).Token

Request an access token for AAD Graph to access Azure AD. Supported tokens include AadGraph, AnalysisServices, Arm, Attestation, Batch, DataLake, KeyVault, MSGraph, OperationalInsights, ResourceManager, Storage, Synapse:

Get-AzAccessToken -ResourceTypeName MSGraph

From older versions of Az PowerShell, get a token for Microsoft Graph:

(Get-AzAccessToken -Resource "https://graph.microsoft.com").Token

Use the access token:

Connect-AzAccount -AccountId test@tenant.onmicrosoft.com -AccessToken eyJ0eXA...

Use other access tokens. In the below command, use the one for MSGraph (access token is still required) for accessing Azure AD:

Connect-AzAccount -AccountId test@tenant.onmicrosoft.com -AccessToken eyJ0eXA... -MicrosoftGraphAccessToken eyJ0eXA...

Az CLI

Az CLI can request a token but cannot use it!

  • Request an access token (ARM):
    az account get-access-token
    
  • Request an access token for aad-graph. Supported tokens include aad-graph, arm, batch, data-lake, media, ms-graph, oss-rdbms:
    az account get-access-token --resource-type ms-graph
    

Stealing Tokens - Az CLI

Az CLI (before 2.30.0 – January 2022) stores access tokens in clear text in accessTokens.json in the directory:

C:\Users\[username]\.Azure
  • We can read tokens from the file, use them, and request new ones too!
  • azureProfile.json in the same directory contains information about subscriptions.
  • You can modify accessTokens.json to use access tokens with Az CLI, but it’s better to use with Az PowerShell or the Azure AD module.
  • To clear the access tokens, always use az logout.

Stealing Tokens - Az Powershell

Az PowerShell (older versions) stores access tokens in clear text in TokenCache.dat in the directory:

C:\Users\[username]\.Azure
  • It also stores ServicePrincipalSecret in clear text in AzureRmContext.json if a service principal secret is used to authenticate.
  • Another interesting method is to take a process dump of PowerShell and look for tokens in it!
  • Users can save tokens using Save-AzContext, so look out for them! Search for Save-AzContext in PowerShell console history!
  • Always use Disconnect-AzAccount!

Hunting Azure Admins

AzureAD Module

AzureAD module cannot request a token but can use one for AADGraph or Microsoft Graph!

Use the AAD Graph token:

Connect-AzureAD -AccountId test@tenant.onmicrosoft.com -AadAccessToken eyJ0eXA...

APIs

Management

The two REST API endpoints that are most widely used are:

  • Azure Resource Manager: management.azure.com
  • Microsoft Graph: graph.microsoft.com (Azure AD Graph, which is deprecated, is graph.windows.net)

ARM

Get an access token and use it with ARM API. For example, list all the subscriptions:

$Token = 'eyJ0eXAi..'
$URI = 'https://management.azure.com/subscriptions?api-version=2020-01-01'
$RequestParams = @{
    Method = 'GET'
    Uri = $URI
    Headers = @{
        'Authorization' = "Bearer $Token"
    }
}
(Invoke-RestMethod @RequestParams).value

MS Graph

Get an access token for MS Graph. For example, list all the users:

$Token = 'eyJ0eXAi..'
$URI = 'https://graph.microsoft.com/v1.0/users'
$RequestParams = @{
    Method = 'GET'
    Uri = $URI
    Headers = @{
        'Authorization' = "Bearer $Token"
    }
}
(Invoke-RestMethod @RequestParams).value

Continuous Access Evaluation (CAE)

Concept CAE - Microsoft Learn

Continuous Access Evaluation (CAE) can help in invalidating access tokens before their expiry time (default 1 hour).

Useful in cases like:

  • User termination or password change enforced in near real time.
  • Blocking use of access tokens outside trusted locations when location-based conditional access is present.

In CAE sessions, the access token lifetime is increased up to 28 hours

Claims Challenge

Claims Challenge - Microsoft learn

CAE needs the client (Browser/App/CLI) to be CAE-capable – the client must understand that the token has not expired but cannot be used.

  • Outlook, Teams, Office (other than web), and OneDrive web apps and apps support CAE.
  • “The xms_cc claim with a value of CP1 in the access token is the authoritative way to identify a client application is capable of handling a claims challenge.”
  • In case the client is not CAE-capable, a normal access token with 1-hour expiry is issued.
  • We can find the xms_cc claim in the MSGraph token for testuser that was requested using Az PowerShell.
  • Access tokens issued for managed identities by Azure IMDS are not CAE-enabled.

Critical Event Evaluation

CAE works in two scenarios:

  1. Critical event evaluation
    • User account is deleted or disabled.
    • Password change or reset for a user.
    • MFA enabled for a user.
    • Refresh token is revoked.
    • High user risk detected by Azure AD Identity Protection (not supported by SharePoint Online).
    • Only Exchange Online, SharePoint Online, and Teams are supported.
  2. Conditional Access policy evaluation
    • Only IP-based (both IPv4 and IPv6) named locations are supported. Other location conditions like MFA trusted IPs or country-based locations are not supported.
    • Exchange Online, SharePoint Online, Teams, and MS Graph are supported.

Tools

ROADTools

RoadRecon is a tool for enumerating Azure AD environments!

RoadRecon uses a different version 1.61-internal of Azure AD Graph API that provides more information.

Enumeration using RoadRecon includes three steps:

  • Authentication
  • Data Gathering
  • Data Exploration

RoadRecon supports username/password, access and refresh tokens, device code flow (sign-in from another device), and PRT cookie.

cd C:\AzAD\Tools\ROADTools
.\venv\Scripts\activate
roadrecon auth -u test@tenant.onmicrosoft.com -p SuperVeryEasytoGuessPassword@1234

Once authentication is done, use the below command to gather data (ignore the errors):

roadrecon gather

Use RoadRecon GUI to analyze the gathered information (starts a web server on port 5000):

roadrecon gui

StormSpotter

StormSpotter is a tool from Microsoft for creating attack graphs of Azure resources.

It uses the Neo4j graph database to create graphs for relationships in Azure and Azure AD!

It has the following modules:

  • Backend: This is used for ingesting the data into the Neo4j database.
  • Frontend (WebApp): This is the UI used for visualizing the data.
  • Collector: This is used to collect the data from Azure.

Start the backend service:

cd C:\AzAD\Tools\stormspotter\backend\
pipenv shell
python ssbackend.pyz

In a new process, start the frontend webserver:

cd C:\AzAD\Tools\stormspotter\frontend\dist\spa\
quasar.cmd serve -p 9091 --history

Use Stormcollector to collect the data:

cd C:\AzAD\Tools\stormspotter\stormcollector\
pipenv shell
az login -u test@tenant.onmicrosoft.com -p SuperVeryEasytoGuessPassword@1234
python C:\AzAD\Tools\stormspotter\stormcollector\sscollector.pyz cli

Log on to the web server at http://localhost:9091 using the following:

Username: neo4j
Password: BloodHound
Server: bolt://localhost:7687
  • After login, upload the ZIP archive created by the collector
  • Use the built-in queries to visualize the data

BloodHound

BloodHound’s AzureHound supports Azure and Azure AD too to map attack paths!

It uses Azure AD and Az PowerShell modules for gathering the data through its collectors.

There are two free versions of BloodHound:

  1. BloodHound Legacy
  2. BloodHound CE (Community Edition)

Run the collector to gather data:

$passwd = ConvertTo-SecureString "password" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential("user@tenant.onmicrosoft.com", $passwd)

Connect-AzAccount -Credential $creds
Connect-AzureAD -Credential $creds

. C:\AzAD\Tools\AzureHound\AzureHound.ps1
Invoke-AzureHound -Verbose

The gathered data can be uploaded to the BloodHound application (both Legacy and Community Edition).

Open the BloodHound application and login using the following details:

- Server: bolt://localhost:7687
- Username: neo4j
- Password: BloodHound
  • Upload the ZIP archive to the BloodHound UI (drag and drop) and use built-in or custom Cypher queries to query the data.

Some examples of Cypher queries are below:

Cypher Cheatsheet

Custom Bloodhound Queries

Find all users who have the Global Administrator role:

MATCH p =(n)-[r:AZGlobalAdmin*1..]->(m) RETURN p

Find all paths to an Azure VM:

MATCH p = (n)-[r]->(g:AZVM) RETURN p

Find all paths to an Azure KeyVault:

MATCH p = (n)-[r]->(g:AZKeyVault) RETURN p

Find all paths to an Azure Resource Group:

MATCH p = (n)-[r]->(g:AZResourceGroup) RETURN p

Find Owners of Azure AD Groups:

MATCH p = (n)-[r:AZOwns]->(g:AZGroup) RETURN p

Privilege Escalation

Automation Account

Some common scenarios for automation as per Microsoft:

– Deploy VMs across a hybrid environment using run books
– Identify configuration changes
– Configure VMs
– Retrieve Inventory

Managed Identity is used to provide authentication for managing Azure resources

The Managed Identity can only be used from inside a Runbook, so Contributor on a Runbook = profit

Runbook contains the automation logic and code that you want to execute

Always checkout Runbooks! They often have credentials that are not stored in the shared resources.

  • By default, only signed script can be run on a VM.
  • Runbooks can run in Azure Sandbox or a Hybrid Runbook Worker.

Hybrid Worker

This is used when a Runbook is to be run on a non-azure machine

The hybrid worker jobs run as SYSTEM on Windows and nxautomation account on Linux

Automation Account comes very handy in privilege escalation:

  • Usually, high privileges for the Managed Identity.
  • Often, clear-text credentials can be found in Runbooks. For example, a PowerShell runbook may have admin credentials for a VM to use PSRemoting.
  • Access to connections, key vaults from a runbook.
  • Ability to run commands on on-prem VMs if hybrid workers are in use.
  • Ability to run commands on VMs using DSC in configuration management.

Abusing Azure DSC Remote Code Execution and Privilege Escalation

Key Vault

Azure service for storing secrets like passwords, connection strings, certificates, private keys etc

Object types available with a key vault:

– Cryptographic Keys - RSA, EC etc.
– Secrets - Passwords, connection strings
– Certificates - Life cycle management
– Storage account keys - Key vault can manage and rotate access keys for
storage accounts

Identifier

The base URL is of the format: https://{vault-name}.vault.azure.net/{object-type}/{object-name}/{object-version}

  • vault-name is the globally unique name of the key vault
  • object-type can be “keys”, “secrets” or “certificates”
  • object-name is unique name of the object within the key vault
  • object version is system generated and optionally used to address a unique version of an object.

Access Management

Access to a vault is controlled though two planes:

– Management plane: To manage the key vault and access policies.
# Only Azure role based access control (RBAC) is supported.

– Data plane: To manage the data (keys, secrets and certificates) in the key vault. 
# This supports key vault access policies or Azure RBAC.

If we can compromise an azure resource whose managed identity can read secrets from a key vault (due to an access policy or assigned one of the capable roles or a custom role), it may be possible to gain access to more resources.

  • Note that each secret has its own IAM inherited from the KeyVault.
  • Overly permissive access policies may result in access to data stored in a vault
Built-in Role Description Can access secrets?
Key Vault Contributor Can manage key vaults No
Key Vault Administrator Perform all data plane operations. Cannot manage role assignment. Yes
Key Vault Certificates Officer Perform any action on certificates. Cannot manage permissions. Yes (Certificates)
Key Vault Crypto Officer Perform any action on keys. Cannot manage permissions. Yes (Keys)
Key Vault Secrets Officer Perform any action on secrets. Cannot manage permissions. Yes (Secrets)
Key Vault Secrets User Read secret contents. Yes (Secrets)
Key Vault Crypto Service Encryption User Read metadata and perform wrap/unwrap operations on keys No
Key Vault Crypto User Perform cryptographic operations using keys No
Key Vault Reader Read metadata of key vaults and its certificates, keys, and secrets. No

Azure Resource Manager (ARM) Templates

The infrastructure as code service for Azure to deploy resources using code

  • ARM templates are JSON files containing deployment configuration for Azure resources.
  • ARM templates also support a language called Bicep

History

Each resource group maintains a deployment history for up to 800 deployments. The history is automatically deleted when the count exceeds 775 deployments.

  • Deployment history is a rich source of information!
  • Any user with permissions Microsoft.Resources/deployments/read and Microsoft.Resources/subscriptions/resourceGroups/read can read the deployment history.

Useful information can be extracted from deployment history.

  • It gives us the ability to have information about the resources that are not presently deployed but may be deployed again in future!
  • A deployment parameter that contains sensitive information like passwords should have SecureString type. In case of a poorly written deployment template - that uses String for such a parameter - we can get password in clear-text!

Function App - Continuous Deployment

Functions Apps (Azure Functions) support continuous deployment.

In case continuous deployment is used, a source code update triggers a deployment to Azure.

Following source code locations are supported:

  • Azure Repos
  • GitHub
  • Bitbucket

Deployment slots are supported so that deployments are first done in slots like staging (to avoid deploying directly in the default production slot)

  • A misconfigured function app that deploys directly in production can be abused.
  • In this case, if the source of truth/code location is compromised, it will be possible to assume the identity of the function app.
  • For example, if GitHub is used as the provider, compromise of a GitHub account that can commit code will lead to compromise of the function app.