# Azure Pentesting Notes

By
Published 2025-11-04

# ⚠️ Rules of Engagement

Microsoft Rules of Engagements for pentesting: pentest-rules-of-engagement

The goal of this program is to enable customers to test their services hosted in Microsoft Cloud services without causing harm to any other Microsoft customers.

Even with these prohibitions, Microsoft reserves the right to respond to any actions on its networks that appear to be malicious. Many automated mitigation mechanisms are employed across the Microsoft Cloud. These will not be disabled to facilitate a penetration test.

# 🔧 Technical Requirements

Make sure to have these PowerShell modules installed in your Windows or Linux machine (Using the pwsh command for linux)

Install-Module AzureAD
Install-Module Az
Install-Module AADIternals
Install-Module MSOnline

# 🔍 Outsider - Recon

The goal is to map the target attack surface. Looking for Tenant informations, exposed applications and storages, any credentials stored inside public repositories.

# User Enumeration

The goal here is to create a user list of maybe valid email accounts.

Source & Data

# Tenant ID

An Azure tenant ID is a 128-bit GUID that uniquely identifies a Microsoft Entra ID (formerly Azure AD) directory, typically shown in 8-4-4-4-12 hex format like 00000000-0000-0000-0000-000000000000.

This Tenant ID is often used by services and tools to scope authentication and API access, making it valuable during post-exploitation and lateral movement planning. While it’s not a secret, it’s highly useful for reconnaissance and chaining attacks, so record it carefully for later use in your engagement notes. Microsoft does not treat tenant IDs as confidential and they are commonly exposed in endpoints, tokens, and metadata, so adversaries can often discover them with basic OSINT or unauthenticated queries.

# OpenID Configuration

You have multiple ways to get this tenant ID, the first one is to use the .well-known url of a target domaine name like so:

https://login.microsoftonline.com/TAREGT-DOMAIN-NAME.COM/v2.0/.well-known/openid-configuration

You can then find it inside the token_endpoint URL.

Azure OpenID Configuration
Azure OpenID Configuration

# AADInternals Outsider

The second option is to use AADInternals with the Outsider enumeration module.

Install-Module AADInternals
Invoke-AADIntReconAsOutsider -Domain "company.com" | Format-Table

Invoke-AADIntReconAsOutsider Output
Invoke-AADIntReconAsOutsider Output

# Blob Enumeration

Tool Name: MicroBurst

This tool doesn't require an account. It's going to enumerate public accessible Azure storage (Blobs) using a pattern variation of the target name.

Import-Module .\MicroBurst.psm1
Invoke-EnumerateAzureBlobs –Base targetname

Azure Blobs
Azure Blobs

Once you find a public exposed blob you can start to enumerate the content using Storage Explorer

Predictable URL’s for core.windows.net:

  • storage-name.blob.core.windows.net
  • storage-name.queue.core.windows.net
  • storage-name.table.core.windows.net
  • storage-name.file.core.windows.net

# Multi-Cloud Enumeration

Multi-cloud OSINT tool. Enumerate public resources in AWS, Azure, and Google Cloud.

Tool Name: cloud_enum

pip3 install -r ./requirements.txt
./cloud_enum.py -k targetname -t 10

# Public Secrets

The goal here is to search for secrets and password that may grant us an Initial Access.

  • Google Dorking
  • Look for sensitive documents
  • Code Repositories

# Public Code Repo

Tools:

#Scan a repo
trufflehog git https://github.com/trufflesecurity/test_keys --results=verified

#Scan an organization
trufflehog github --org=trufflesecurity --results=verified

# Password Attacks

Password Spaying

Tool Name: MSOLSpray

A password spraying tool for Microsoft Online accounts (Azure/O365). The script logs if a user cred is valid, if MFA is enabled on the account, if a tenant doesn't exist, if a user doesn't exist, if the account is locked, or if the account is disabled.

Import-Module MSOLSpray.ps1
Invoke-MSOLSpray -UserList .\userlist.txt -Password Winter2020

# Phishing Device Code

Device code is an OAuth 2.0 flow for devices or CLIs that can’t open a browser: the app shows a short user code and a URL (e.g., microsoft.com/devicelogin), you complete sign-in and MFA in a normal browser, and the app polls until it exchanges its device_code for tokens.

  • How it works: Client requests a device code set, displays user_code and verification_uri, user authenticates at the URL, client polls at the specified interval until it receives access/refresh/ID tokens.​

  • Typical cues: Prompts like az login --use-device-code instruct to visit microsoft.com/devicelogin and enter a short code.​

  • Security notes: It’s MFA-compatible but phishable; attackers can social-engineer victims to enter the code, granting tokens to the waiting client. Monitor and restrict this flow via Conditional Access if it’s not needed.

Tool Name: GraphRunner

The goal is to generate a Device Code and Social-Engineer a target.

. ./GraphRunner
Get-GraphTokens

Once the target enter the code and authenticate to the microsoft portal, you'll receive an access_token and a refresh_token. They are both stored inside the $tokens global variable.

You can now start to enumerate the tenant with your initial access.

# 👹 Initial Access - Recon

You are trying to authenticate into the Tenant of the target.

# Validate Conditional Access

Tool Name: MFASweep

This command will use the provided credentials and attempt to authenticate to the Microsoft Graph API, Azure Service Management API, Microsoft 365 Exchange Web Services, Microsoft 365 Web Portal with both a desktop browser and mobile, and Microsoft 365 Active Sync. If any authentication methods result in success, tokens and/or cookies will be written to AccessTokens.json.

Invoke-MFASweep -Username targetuser@targetdomain.com -Password Winter2024 -WriteTokens 

# Token CheatSheets

Access tokens prove what the client can do right now, while Refresh tokens let the client get new access tokens later without re-prompting the user.

Access tokens

  • Purpose: Bearer token presented to an API (e.g., Microsoft Graph) to authorize requests; contains granted scopes/roles and expires quickly.​
  • Scope/audience: Issued for a specific resource (e.g., https://graph.microsoft.com) and a set of permissions listed in the token response’s scope claim.​
  • Lifetime: Short-lived, typically around an hour; apps must replace them when expires_in elapses.​
  • Usage: Sent as Authorization: Bearer <access_token> on each API call; do not reuse across different resources.​

Refresh tokens

  • Purpose: Used by the client to obtain new access token/refresh token pairs after the access token expires, avoiding user interaction.​
  • Scope/audience behavior: Not tied to a single resource; with appropriate grants, the same refresh token can mint access tokens for other resources or tenants the client is permitted to access.​
  • Lifetime and rotation: Much longer-lived (commonly up to 90 days for most apps; 24 hours for SPA redirect URIs), and they rotate-store only the latest token you receive.​
  • Prerequisite: Returned only if offline_access was requested in the scope during authorization.

# Find Tokens in Local Machine

# Azure TokenCache

If you gain access to a target machine remotely, you may need to look for tokens stored inside the machine.

Where tokens live now:

  • Azure CLI: Stores account and token cache under %USERPROFILE%\.azure on Windows, but sensitive material is DPAPI-protected and structured across JSON/db files rather than a simple reusable TokenCache.dat. Even when you locate "accessTokens.json" or similar artifacts, they are not reliably plain, reusable bearer tokens.
  • Microsoft Graph PowerShell SDK / MSAL-based apps: Token caches are managed by MSAL and, on Windows, encrypted via DPAPI tied to the current user profile. The SDK exposes Connect-MgGraph and related commands but does not provide plaintext tokens on disk.

# PowerShell History

PowerShell history is a great place to recover pasted tokens, client secrets, device codes, or one‑off commands that fetched tokens, because PSReadLine persists commands to a plain text file by default.

#Show HIstory Path
(Get-PSReadLineOption).HistorySavePath

#Read an Search
Get-Content (Get-PSReadLineOption).HistorySavePath | Select-String -Pattern 'token|asplaintext|password|secret|apikey'

#Default Path
%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Powershell\PSReadLine\ConsoleHost_history.txt

# More Location

  • Azure Publish Files .publishettings extension
  • web.config
  • Visual Studio Azure Cloud Service Packages .cspkg extension

# 🧙♂️ Post Exploitation

You manage to get your Initial Access, your goal is to enumerate and elevate your privileges.

# Get new tokens with different scopes

Request a new token for a new resource audience (scope). In this example you'll change your Microsoft Graph scope into an Azure Resource Manager (Management).

Keep your refresh token handy and mint separate access tokens for each API (e.g., Microsoft Graph vs. https://management.azure.com).

Use GraphRunner to refresh your Microsoft Graph tokens, ensuring your access token isn’t expired.

# Refresh MS Graph tokens in $tokens (access/refresh/id)
Invoke-RefreshGraphTokens

Connect to Microsoft Graph with an access token

Recent Microsoft Graph PowerShell versions accept -AccessToken as a SecureString; convert accordingly.

# Convert the MS Graph access token for Connect-MgGraph
$secureGraphToken = ($tokens.access_token | ConvertTo-SecureString -AsPlainText -Force)
Connect-MgGraph -AccessToken $secureGraphToken

Obtain an Azure Resource Manager (Management) token

Request a separate token for the ARM audience and use Az modules or direct REST calls.

# Interactive or device code as needed
Connect-AzAccount
# Get ARM token for Management API
$arm = Get-AzAccessToken -ResourceUrl https://management.azure.com
$armToken = $arm.Token

# Compliance & Audit

This part isn't really focused into pentesting but it could give you some highlight about the configuration of the Azure Tenant.

# Prowler

Prowler is like ScoutSuite and can generate a compliance check with a nice and shiny template, but the scan ca take few hours to perform. It's using azure cli

Tool Name: Prowler

#Running Scans from the terminal and generate differents reports format
prowler azure -M csv json-asff json-ocsf html


# List the available checks and services
prowler azure --list-checks
prowler azure --list-services

#Running an HTML Dashboard
prowler dashboard