Mastering Azure Enumeration: Techniques for Unauthenticated and Authenticated Hacking

n00🔑
11 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
If you are using a custom domain. Exists = 0 -> Means User Exists
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.
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

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

Authenticated-

Using MG Module-

Install-Module -Name Az -AllowClobber -Force; 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

$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)
#Whoami
Get-MgContext
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
#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 *

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

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

Listing logged-in accounts-

 Get-AzContext -ListAvailable

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

Get-AzContext

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*
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

Get-AzResource

Get-AzWebApp

Get-AzStorageAccount

Get-AzKeyVault

Get-AzSqlServer

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

#To upload the zip/json
http://localhost:8080/ui/administration/file-ingest

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

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

No responses yet