Azure Attack Paths: Common Findings and Fixes (Part 1)

Image taken at Scotrail Arches next to SWG3, Glasgow, Scotland

Learning about cloud penetration testing is nothing new, but it is undoubtedly an area where a lot of people lack knowledge on the defence and offence side of the house. It is also an area where many folks have written up some excellent techniques, but there is still a lack of structured learning around how to hunt for security risks within Azure and some key findings that can be called out.

This post will walk through various services within the Azure catalogue and look at potential attack paths; originally it was going to be all one post however, I have decided to split it into several parts as there are so many services and the service lines are forever expanding. Therefore, the attack surface is equally expanding.

A Brief overview of Azure Roles

Azure has two different management areas of roles within an environment, they are split up into Azure Active Directory (AzureAD) and Azure Role-Based Access Control (RBAC).  Specifically, AzureAD is used for authentication to different areas of Azure Active Directory, such as applications and other AD objects. Whereas RBAC manages access to Azure resources such as storage containers, functions, databases, virtual machines, and other services.

  • Reader - The reader allows a user to view all resources where the role is applied, it can be applied at different levels within a subscription from as high as the tenant all the way down to granular resources. It does not allow any changes to be made.
  • Contributor - Similar to the reader role, it can be applied at all levels in the RBAC model, and it grants full access to manage all resources. Still, it does not allow you to assign roles in Azure RBAC, manage assignments in Azure Blueprints, or share image galleries.
  • Owner - Owner has all the functionality of Contributor but with the addition of being able to assign roles to other users within AzureRBAC.

AzureAD on the other hand has many different roles available, I’m not going to dive into all of them as it shares a lot of similarities with on-prem Active Directory in that different roles and groups grant access to different resources within AzureAD. For more information on the different roles in both AzureAD and Azure, see the following references:

Setup an Environment

Before diving into all the available tools, available hunt paths, and interesting things, it is essential to have a robust working environment setup.

The following commands can be leveraged to install the modules required to access Azure via PowerShell. Note that conditional access may block standard Azure users from accessing PowerShell for Azure. The following modules are required as a minimum to interact with Azure:

  • Azure
  • AzureAD
  • MSOnline
  • Microsoft Graph
Install-Module Az -Force -Confirm -AllowClobber -Scope CurrentUser
Install-Module AzureAD -Force -Confirm -AllowClobber -Scope CurrentUser
Install-Module Microsoft.Graph -Force -Confirm -AllowClobber -Scope CurrentUser
Install-Module MSOnline -Force -Confirm -AllowClobber -Scope CurrentUser  
Install-Module AzureADPreview -Force -Confirm -AllowClobber -Scope CurrentUser 

The commands above can be run from any Windows-based system, the –Scope CurrentUser flag notates that a standard user can be used to install the modules however it is recommended to install them as a local administrator to ensure system-wide compatibility.

There are several other tools written in python for graphing and identifying other issues within an Azure environment however, for the purposes of these tutorial sections PowerShell for Azure, and Azure CLI will be used.

To connect to Azure via the various exposed APIs the following three commands can be used. If your environment restricts the use of PowerShell for Azure then you will need to use the Azure CLI instead or CloudShell, see the section on CloudShell below as there are limitations to using it for enumeration.

Connect-AzAccount
Connect-AzureAD
Connect-MSolService

Each of the Connect commands will prompt for authentication to Azure, therefore login with an account with reader access and at least one subscription assigned to review.

Available PowerShell Tools

Once your PowerShell environment is set up you can begin exploring the various tools available to you, useful ones that are available for enumeration via Powershell and Cloudshell are the following. There are many other tools available, but the following will be used throughout this blog post to show and explore the various products Azure offers.

  • PowerZure + CloudShell: PowerZure has several functions for use with PowerShell for Azure however, the linked pull request allows for use within CloudShell which can evade conditional access restrictions in some cases where a user is permitted CloudShell access but not PowerShell for Azure.
  • AzureRT is a PowerShell module that allows for the enumeration of Azure resources, it incorporates several modules and allows for authentication to different Azure APIs.
  • MicroBurst provides a lot of PowerShell functions geared towards the RestAPI and Powershell for Azure modules and can be leveraged to extract information from an Azure environment provided the account has a subscription tied to it. Unlike other tools in this list that can operate from an unauthenticated, guest, or non-assigned subscription account, Microburst requires the use of a subscription as a bare minimum.
  • AADInternals provides a lot of PowerShell modules that can be leveraged to enumerate an Azure environment, there is a lot of cross-over in AADInternals with AzureRT and some of the PowerZure modules but it also gives lots of useful information and enumeration functions.
  • AzureHound is an Azure ingestor for BloodHound, which uses a lot of PowerZure functionality under the hood however, there are a lot better tools for enumerating information about an environment; AzureHound at least gives a visual representation of Azure Active Directory relations and slots in nicely with on-prem objects graphing out paths from on-prem to the cloud and vice versa.

There are many other tools written in python for graphing and identifying other issues within an Azure environment however, for the purposes of this tutorial, PowerShell for Azure and Azure CLI(AzCLI) will be used.

Once all the modules and tools are downloaded, the following code can be run to import the modules and connect to Azure and AzureAD:

Import-Module Az
Import-Module AzureAD
Import-Module .\MicroBurst-master\MicroBurst.psm1
Import-Module .\AzureRT-master\AzureRT.ps1
Import-Module .\AzureHound\AzureHound.ps1
Import-Module AADInternals

CloudShell

CloudShell is an excellent resource for attacking an environment, it does have a few pre-requisites however can be leveraged in some cases to bypass conditional access restrictions where PowerShell for Azure may not work.

The limitations of CloudShell are that a session can only run for 20 minutes or less, and the user needs to have access to a storage account to launch a CloudShell instance. If possible, CloudShell enables access to the Azure tenant via a Kubernetes instance of bash or PowerShell to allow for the execution of commands within the environment. If PowerShell is selected, the setup commands within Setup Environment can be leveraged to execute PowerShell tooling. The fork of PowerZure above has been altered to work with CloudShell and provides some insight into an environment via CloudShell, due to the limitations of the 20-minute time limit, tools like AzureHound do not work as well.

Initial Enumeration from an Unauthenticated/Anonymous perspective

Using AADInternals, there are several functions that can be leveraged to perform analysis of an environment even before valid credentials are obtained, these functions lend themselves to enumerate information about connected domains to an environment or synced information. The following Recon functions can be executed at varying degrees of authentication or access:

  • Invoke-AADIntReconAsOutsider
  • Invoke-AADIntReconAsGuest
  • Invoke-AADIntReconAsInsider

Using Invoke-AADIntReconAsOutsider we can feed it a domain name and the PowerShell function Format-Table to view information about a domain.

Invoke-AADIntReconAsOutsider -DomainName domain.com | Format-Table

The function Invoke-AADIntReconAsOutsider can be run from an unauthenticated perspective meaning you do not need to connect it to Azure Powershell at all and will allow you to identify additional domains that are synced via Azure, if they are managed and if Microsoft 365 is in use. The other functions enable you to enumerate more information about an Azure environment based on the privileges it is run as.

The Invoke-AADIntReconAsGuest function, on the other hand, will prompt for a tenant and run a number of enumeration and recon queries to identify the number of Azure AD objects and quota, and the number of domains (both verified and unverified). Using the documentation for the function, the following command can be run to enumerate an environment:

# Get access token and save to cache
Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache

# Invoke tenant recon as guest
$results = Invoke-AADIntReconAsGuest

# This will display all the available options within the results object, gm is short for Get-Member
$results | gm

# Display output in table format
$results | Format-Table

Finally the Invoke-AADIntReconAsInsider gives a little bit more information about an environment as an authenticated user with a full subscription associated, it can be run in the exact same manner as the Guest module:

# Get access token and save to cache
Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache

# Invoke tenant recon as guest
$results = Invoke-AADIntReconAsGuest

# This will display all the available options within the results object, gm is short for Get-Member
$results | gm

An example of the output given from the module is as follows:

Tenant brand:                company.com
Tenant name:                 company.onmicrosoft.com
Tenant id:                   d4e225d6-8877-4bc6-b68c-52c44011ba81
Azure AD objects:            147960/300000
Domains:                     5 (5 verified)
Non-admin users restricted?  True
Users can register apps?     True
Directory access restricted? False
Directory sync enabled?      true
Global admins                10


Azure Runbooks

What Are They?

Azure Runbooks are essentially scripts that run in the context of an automation account and perform specific tasks, they are typically written in PowerShell. From a usability perspective, they can be incredibly useful for automating otherwise manual tasks or improving commonly mundane functionality.

What is a Common Risk?

Like many functions and resources within Azure, the common risk of runbooks is hard-coding secrets and credentials, while Microsoft has several documented best practices that state using functions such as Keyvaults, developers still follow insecure instructions and may hardcoded credentials.

How to Find Them?

Using PowerZure both within CloudShell and PowerShell for Azure it is possible to pull all the available runbooks within the enabled subscriptions for searching offline. The following for loop will grab the enabled subscriptions and run the PowerZure function Get-AzureRunbookContent against every subscription accessible.

$enabledSubs = Get-AzSubscription |  Where-Object{$_.State -eq "Enabled"} | select Id
foreach ($SubName in $enabledSubs) {
$IDOut = $SubName.id
Set-AzContext -Subscription "$IDOut"
Get-AzureRunbookContent -All
}

The output will pull all available scripts to the current working directory that can then be searched using tools like grep and Visual Studio Code for strings or regular expressions.

Searching for strings such as password, clientsecret, mycred and credentials can yield a lot of results across extracted information.  In addition to pulling out credentials, often it is possible to extract client secret values and leverage automation accounts and managed service principles to further enumerate environments. The template below can be leveraged to connect with either Powershell for Azure or Az CLI and access an environment:

$tenant_ID = "<INSET TENANT ID>"
$client_ID = "<INSERT CLIENT ID/USERNAME>"
$client_secret = "<INSERT CLIENT SECRET/PASSWORD"
$subscription_name = "<INSERT SUBCRIPTION>"
$mycreds = New-Object System.Management.Automation.PSCredential($client_ID,(ConvertTo-SecureString $client_secret -AsPlainText -Force))
Connect-AzAccount -Credential $mycreds -Tenant $tenant_ID -ServicePrincipal -Subscription $subscription_name
# Below is if you need to also authenticate to Az as well
az login --service-principal -u $client_ID -p $client_secret --tenant $tenant_ID

Upon filling in the relevant fields the above will enable you to authenticate to an identified account and leverage this for further enumeration of an environment.


What's the Fix?

There are several things that can be done to combat credentials in runbooks, the main is educating developers and users of runbooks about the best practices to employ safe handling of credentials.

Mainly the use of Get-AzAutomationCredential will allow a script to pull an automation credential rather than hard-coding specific credentials into a script, it will also enable for dynamic updating of credentials which makes for more dynamic coding practices.

In addition, using the above technique to hunt out vulnerabilities in your environment will help identify the amount of exposed(if any) credentials and secrets and help uncover additional risks.


Storage Accounts

Like runbooks, storage containers and storage accounts contain a wealth of information and can be vital in furthering the traversal of an environment. Using data gathered from Runbooks, often it is possible to access storage accounts via connection strings. Azure Storage Explorer(https://azure.microsoft.com/en-gb/features/storage-explorer/) is an excellent tool provided by Microsoft that enables you to browse storage containers using a GUI. It takes a storage account name and storage account key, providing anonymous authentication without the use of a username and password.

Information held within storage accounts can be searched or browsed manually and files can be downloaded, it should be noted that while using storage explorer it is advised if large files are being downloaded to turn off “Check MD5” which is located under settings as shown below:

Turning off the MD5 check reduces the risk of files being corrupt upon download or even failing to download due to checksum failings or connection failings. It does however introduce a lack of checksum so therefore if data integrity is paramount, keep this option enabled.

Using a storage browser allows you as an attacker to browse various storage accounts en masse.  It also allows for browsing with a lack of logging due to the methods of authentication being purely a key and account name rather than traditional authentication which would be the username and a password that is identifiable and somewhat unique.

Storage accounts are like the cloud equivalent of SMB shares on a windows estate, often permissions are misconfigured and they hold a wealth of information.


Restricting Access to Storage Accounts

Using the Azure RBAC model it is possible to configure granular permissions to restrict access and apply conditional access policies that restrict access to storage accounts within only the portal or other access methods, equally using conditional access it is possible to restrict access to an azure environment by region, IP ranges and specific accounts too making for a much easier way to manage permissions and access logging.

Conclusion

This post has been part 1, looking at a few common issues within Azure and how to fix them and potentially avoid them, allowing us as attackers and defenders to better understand the functionality and how to correctly identify flaws. In the next parts, I'll cover things like hybrid workers, logic apps,  Azure container registry, and some other services too.

Andy Gill

Andy Gill