Sitemap

Mastering Azure Enumeration: Techniques for Unauthenticated and Authenticated Hacking

12 min readOct 16, 2024

Terminology-

  • Tenant โ€” An instance of Azure AD and represents a single organization.
  • Azure AD Directory โ€” Each tenant has a dedicated Directory. This is used to perform identity and access management functions for resources.
  • Subscriptions โ€” It is used to pay for services. There can be multiple subscriptions in a Directory.
  • Core Domain โ€” The initial domain name <tenant>.onmicrosoft.com is the core domain. It is possible to define custom domain names too.

Managed Identity(Similar to roles in AWS)-

  • Azure provides the ability to assign Managed Identities to resources like app services, function apps, virtual machines, etc.
  • Managed Identity uses Azure AD tokens to access other resources (like key vaults, and storage accounts) that support Azure AD authentication.
  • It is a service principal of a special type that can be used with Azure resources.
  • Managed Identity can be system-assigned (tied to a resource and cannot be shared with other resources) or user-assigned (independent life cycle and can be shared across resources).

Unauthenticated-

Tenant Information

https://login.microsoftonline.com/getuserrealm.srf?login=pswalia2u.onmicrosoft.com

Set-ExecutionPolicy bypass
Import-Module .\AADInternals.psd1
Get-AADIntLoginInformation -UserName pswalia2u@pswalia2u.onmicrosoft.com
Press enter or click to view image in full size
If you are using a custom domain. Exists = 0 -> Means User Exists
Press enter or click to view image in full size
If you run using default domain(.onmicrosoft.com). In this case User exists, but still Exists = 1 because we have not used custom domain while querying.
Press enter or click to view image in full size
For Non existent user Exists=1

Another way using POST request to https://login.microsoftonline.com/common/GetCredentialType

$UserName = "pswalia2u@pdfmerge.work"
$URI = 'https://login.microsoftonline.com/common/GetCredentialType'
$RequestParams = @{
Method = 'POST'
Uri = $URI
Body = @{
'Username' = $UserName
} | ConvertTo-Json
}

$Result = Invoke-RestMethod @RequestParams
If($Result.IfExistsResult -eq 0){
Write-Output "$UserName is Valid"
}else{
Write-Output "$UserName is Invalid"
}

Oauth Endpoint-

https://login.microsoftonline.com/pswalia2u.onmicrosoft.com/.well-known/openid-configuration
Press enter or click to view image in full size

Retrieved info-

token_endpoint                        : https://login.microsoftonline.com/ab0d55f7-6101-4073-8c76-3cb36cd1a3ee/oauth2/token
#TenantID ab0d55f7-6101-4073-8c76-3cb36cd1a3ee

token_endpoint_auth_methods_supported : {client_secret_post, private_key_jwt, client_secret_basic}
jwks_uri : https://login.microsoftonline.com/common/discovery/keys
response_modes_supported : {query, fragment, form_post}
subject_types_supported : {pairwise}
id_token_signing_alg_values_supported : {RS256}
response_types_supported : {code, id_token, code id_token, token id_token...}
scopes_supported : {openid}
issuer : https://sts.windows.net/ab0d55f7-6101-4073-8c76-3cb36cd1a3ee/
microsoft_multi_refresh_token : True
authorization_endpoint : https://login.microsoftonline.com/ab0d55f7-6101-4073-8c76-3cb36cd1a3ee/oauth2/authorize
device_authorization_endpoint : https://login.microsoftonline.com/ab0d55f7-6101-4073-8c76-3cb36cd1a3ee/oauth2/devicecode
http_logout_supported : True
frontchannel_logout_supported : True
end_session_endpoint : https://login.microsoftonline.com/ab0d55f7-6101-4073-8c76-3cb36cd1a3ee/oauth2/logout
claims_supported : {sub, iss, cloud_instance_name, cloud_instance_host_name...}
check_session_iframe : https://login.microsoftonline.com/ab0d55f7-6101-4073-8c76-3cb36cd1a3ee/oauth2/checksession
userinfo_endpoint : https://login.microsoftonline.com/ab0d55f7-6101-4073-8c76-3cb36cd1a3ee/openid/userinfo
kerberos_endpoint : https://login.microsoftonline.com/ab0d55f7-6101-4073-8c76-3cb36cd1a3ee/kerberos
tenant_region_scope : EU
cloud_instance_name : microsoftonline.com
cloud_graph_host_name : graph.windows.net
msgraph_host : graph.microsoft.com
rbac_url : https://pas.windows.net
Press enter or click to view image in full size
Get-AADIntTenantID -Domain <your-org>.onmicrosoft.com

Get-AADIntTenantDomains -Domain pswalia2u.onmicrosoft.com

Get-AADIntTenantDomains -Domain microsoft.com

curl.exe "https://outlook.office365.com/autodiscover/autodiscover.json?Email=mariewilliams@pharmacorphq.onmicrosoft.com&Protocol=Autodiscoverv1"

Enum Domains-
https://github.com/RoseSecurity/Enum_AzureSubdomains

redis.cache.windows.net Databases-Redis
documents.azure.com Databases-Cosmos DB
database.windows.net Databases-MSSQL
vault.azure.net Key Vaults
onmicrosoft.com Microsoft Hosted Domain
mail.protection.outlook.com Email
sharepoint.com SharePoint
azureedge.net CDN
search.windows.net Search Appliance
azure-api.net API Services
portal.cloudappsecurity.com Microsoft Defender for Cloud Apps
azurewebsites.net App Services
scm.azurewebsites.net App Services - Management
p.azurewebsites.net App Services
cloudapp.net App Services
file.core.windows.net Storage Accounts-Files
blob.core.windows.net Storage Accounts-Blobs
queue.core.windows.net Storage Accounts-Queues
table.core.windows.net Storage Accounts-Tables

Import-Module .\MicroBurst.psm1 -Verbose
Invoke-EnumerateAzureSubDomains -Base microsoft -Verbose

https://github.com/dafthack/MFASweep.git
Invoke-MFASweep -Username user-email -Password "test@123"
contact@defcorphq.onmicrosoft.com
admin@defcorphq.onmicrosoft.com
root@defcorphq.onmicrosoft.com
test@defcorphq.onmicrosoft.com
hello@defcorphq.onmicrosoft.com
Press enter or click to view image in full size

Authenticated-

Using MG Module-

Install-Module -Name Az -AllowClobber -Force -Scope AllUsers; Import-Module Az
#Creating Pscredential object
$passwd = ConvertTo-SecureString "secret_password" -AsPlainText -Force `
; $creds = New-Object System.Management.Automation.PSCredential ("email", $passwd) `
; Connect-AzAccount -Credential $creds

#Note If MFA is present run only "Connect-AzAccount" and perform interactive login

Install-Module Microsoft.Graph -AllowClobber -Force -Scope AllUsers

$token=(Get-AzAccessToken -ResourceTypeName MSGraph).token
OR
$token = (az account get-access-token --resource https://graph.microsoft.com | ConvertFrom-Json).accessToken
OR
$token = (az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsv)

Connect-MgGraph -AccessToken ($Token | ConvertTo-SecureString -AsPlainText -Force)
OR
Connect-MgGraph -Scopes "Directory.ReadWrite.All, User.ReadWrite.All, Group.ReadWrite.All, Mail.ReadWrite, Files.ReadWrite.All, Sites.ReadWrite.All, Reports.Read.All, Device.ReadWrite.All, Policy.ReadWrite.ConditionalAccess, SecurityEvents.ReadWrite.All, IdentityRiskEvent.ReadWrite.All, PrivilegedAccess.ReadWrite.AzureAD, PrivilegedAccess.ReadWrite.AzureResources, Application.ReadWrite.All, RoleManagement.ReadWrite.Directory" -UseDeviceAuthentication
Press enter or click to view image in full size
#Whoami
Get-MgContext
OR
$object = Get-MgContext;$object.PSObject.Properties | ForEach-Object { $object | Select-Object -ExpandProperty $_.Name }

Get-MgOrganization | fl *
Get-Mguser -All | Measure

#Enumerate a specific user
Get-MgUser -UserId pswalia2u@pswalia2u.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

Get-MgUser -UserId <email> | fl *
Get-MgUser -UserId <email> | %{$_.PSObject.Properties.Name}

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}

#Retrieves objects created by any user, with an option to specify a particular user using -ObjectId
Get-MgUserCreatedObject -UserId <email> | fl *

#Retrieves objects owned by a specific user.
Get-MgUserOwnedObject -UserId test@defcorphq.onmicrosoft.com | fl *

Groups
#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}

#Get groups and roles where the specified user is a member
(Get-MgUserMemberof -UserId <email>).AdditionalProperties
Press enter or click to view image in full size
#Roles
Get all available role templates
Get-MgDirectoryRoleTemplate

#Roles which have been enabled atleast once
Get-MgDirectoryRole
Get-MgDirectoryRole | fl
#Enumerate users to whom roles are assigned
$RoleId = (Get-MgDirectoryRole -Filter "DisplayName eq 'Global Administrator'").Id `
; ((Get-MgDirectoryRoleMember -DirectoryRoleId $RoleId).AdditionalProperties).userPrincipalName

Domain Joined Devices:

Get all Azure joined and registered devices
Get-MgDevice -All | fl *

Get-MgDevice -All | Where-Object { $_.DisplayName -like "*honeypot*" } | fl *

List Registered Owners of all the devices
$ids = (Get-MgDevice -All).Id; foreach ($id in $ids) { (Get-MgDeviceRegisteredOwner -DeviceId $id).AdditionalProperties }

Registered Owner's email
$ids = (Get-MgDevice -All).Id; foreach ($id in $ids) { (Get-MgDeviceRegisteredOwner -DeviceId $id).AdditionalProperties.userPrincipalName }


List Registered Users of all the devices
$Ids = (Get-MgDevice -All).Id; foreach($i in $Ids){ (Get-MgDeviceRegisteredUser -DeviceId $i).AdditionalProperties}

List Registered User's email
$Ids = (Get-MgDevice -All).Id; foreach($i in $Ids){ (Get-MgDeviceRegisteredUser -DeviceId $i).AdditionalProperties.userPrincipalName}

Get owned devices by a user
(Get-MgUserOwnedDevice -userId <email>).AdditionalProperties

Get registered devices by user
(Get-MgUserRegisteredDevice -userId <email>).AdditionalProperties

List devices managed using Intune
Get-MgDevice -All | ?{$_.IsCompliant -eq "True"} | fl *
  • Registered User (Get-MgDeviceRegisteredUser): This refers to users who have signed into or registered with the device but are not necessarily the owners of the device.
  • Registered Owner (Get-MgDeviceRegisteredOwner): This refers to the user(s) who are officially considered the owner(s) of the device.

App Registrations-

#App registrations (All registered applications in an Azure Active Directory (AAD) tenant.)
Get-MgApplication -All

#Get information of a specific App
Get-MgApplicationByAppId -AppId 3c67156d-b5ff-4eed-a66f-e2b5462f7b70 | fl *

#Below cmd 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 }

Service Principals/Enterprise Applications-

Get-MgServicePrincipal -All

#Get all details about a service principal
Get-MgServicePrincipal -ServicePrincipalId fd518680-b290-4db2-b92a-5dbd025c6791 | fl *

#Get an service principal based on the display name
Get-MgServicePrincipal -All | ?{ $_.DisplayName -match "chatgpt" }

#List all the service principals with an application password
Get-MgServicePrincipal -All | ?{ $_.KeyCredentials -ne $null }
Get-MgServicePrincipal -All | ?{ $_.KeyCredentials -ne $null } | fl *

#Get owner of a service principal
(Get-MgServicePrincipalOwner -ServicePrincipalId 9d9c5d6e-a594-4148-a48e-9551fbb0de07).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 9d9c5d6e-a594-4148-a48e-9551fbb0de07 | fl *
Press enter or click to view image in full size

Using Az Powershell Module-

Authenticating-

#Opens browser for authentication
Connect-AzAccount

#Takes id/password in a prompt
$creds = Get-Credential
Connect-AzAccount -Credential $creds

#Uses
$passwd = ConvertTo-SecureString "SuperVeryEasytoGuesPassword@1234" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("Email", $passwd)
Connect-AzAccount -Credential $creds

Login

Press enter or click to view image in full size

We can use the Az PowerShell module to enumerate Azure and Azure EntraID resources.

Listing logged-in accounts-

 Get-AzContext -ListAvailable
Press enter or click to view image in full size

Checking current context(Account being used to run command):

Get-AzContext
Press enter or click to view image in full size

There are 51 Azure AD/Entra ID-related commands and overall 6300 commands related to Azure

#All AzureAD commands have 
Get-Command *azad*
Get-Command *azad* | measure
Get-Command -Verb Get -Noun *vm*
Press enter or click to view image in full size
Press enter or click to view image in full size
Get-AzADUser

#Get the information about the current context (Account, Tenant, Subscription etc.)
Get-AzContext

#lists all the contexts which were used on the device.
#We can use Set-Azcontext to use one of the availble context
#with active access token
Get-AzContext -ListAvailable

#Enumerate all resources visible to the current user
Get-AzSubscription

#get all the resources which are visible to current user
Get-Azresource

#Enumerate all Azure RBAC role assignments
Get-AzRoleAssignment

#List VMs
Get-AzVM | fl

Get-AzVM | Select-Object -ExpandProperty NetworkProfile | fl

#List Web APPs
Get-AzWebapp

#List Function Apps
Get-AzFunctionApp

#Storage accounts
(Get-AzStorageAccount | Select -ExpandProperty NetworkRuleSet).IpRules

Get-AzResourceGroup

Press enter or click to view image in full size

Get-AzResource

Press enter or click to view image in full size
Press enter or click to view image in full size
Press enter or click to view image in full size

Get-AzWebApp

Press enter or click to view image in full size
Press enter or click to view image in full size
Press enter or click to view image in full size

Get-AzStorageAccount

Press enter or click to view image in full size
Press enter or click to view image in full size

Get-AzKeyVault

Press enter or click to view image in full size
Press enter or click to view image in full size

Get-AzSqlServer

Press enter or click to view image in full size
Press enter or click to view image in full size

Azure cli-

az login

#If the user has no permissions on the subscription
az login -u test@defcorphq.onmicrosoft.com -p SuperVeryEasytoGuessPassword@1234 --allow-no-subscriptions

#Get details of the current tenant (uses the account extension):
az account tenant list
#Whoami
az account show

#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

#List available profiles
az account list

#Switching profiles
az account set --subscription 4034513d-bdad-429d-bc93-6b4d9f0b0c97

az configure

#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"

#Enumerate all users
az ad user list
az ad user list --query "[].displayName" -o table
az ad user list --output table

#Enumerate a specific user (lists all attributes)
az ad user show --id <emial>

#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"

#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 fd0beb15-718d-43a5-a1f2-b5ef9229afa7

#Get an application based on the display name(Run in cmd and it is case sensitive)
az ad app list --query "[?contains(displayName,'App')].displayName"

#Get an application based on the display name(run in powershell and not casesensitive)
az ad app list | ConvertFrom-Json | %{ $_.displayName -match "app" }

#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"

#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"
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 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"


#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 ffedb8fa-6b7c-4d18-946e-30d330c00645

#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 "app" }

#
az ad sp owner list --id fd51bc12-e707-4df0-b45f-af2e0c5aa4a9 --query "[].[displayName]" -o table

#Service Principals owned by current user
az ad sp list --show-mine

#List apps that has 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"

Location of Tokens and Auth info:

C:\Users\<User>\.Azure

#ServicePrincipalSecret is stored in clear text in AzureRmContext.json in C:\Users\<User>\.Azure\ directory

Azurehound-

(Get-AzAccessToken -ResourceTypeName MSGraph).Token
.\azurehound.exe -j <token> --tenant <example.onmicrosoft.com> list -o output.json
#Use old version of azurehound. Version: 2.2.1-test-ci. Latest Version gives error.
Press enter or click to view image in full size
#To upload the zip/json
http://localhost:8080/ui/administration/file-ingest

#Then click on folder in Explore->Cypher section

Setting up o365stealer

apt update -y && apt install python3 php-cli git python3-pip python3-venv sqlite3 vim -y
cd /opt
git clone https://github.com/AlteredSecurity/365-Stealer.git
cd /opt/365-Stealer
python3 -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
php -S 0.0.0.0:80

line number 961 remove comments extension=sqlite3

References:

https://github.com/compassSecurity/bloodhoundce-resources

https://raw.githubusercontent.com/synacktiv/Mindmaps/refs/heads/main/pentesting_azuread_entraid.png

https://github.com/Acceis/bqm

https://github.com/SpecterOps/AzureHound

https://github.com/dirkjanm/ROADtools

https://github.com/dafthack/GraphRunner

Thanks for Reading!!

--

--

n00๐Ÿ”‘
n00๐Ÿ”‘

Written by n00๐Ÿ”‘

Computer Security Enthusiast. Usually plays HTB (ID-23862). https://www.youtube.com/@pswalia2u https://www.linkedin.com/in/pswalia2u/ Instagram @pswalia4u

Responses (1)