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
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
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).
Basic Sign-In
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.
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!
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)
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:
- 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.
- 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:
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:
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
andMicrosoft.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.