Pentest Copilot's agents ran 89 supported Azure attack-path checks from a VM managed identity. We analyzed each run across control, data, directory, runtime, rollback, and retest.

Azure Resource Manager is the control-plane API used to configure Azure resources. Consider a VM that can use it to update a Key Vault. The same VM receives 403 Forbidden when it asks the vault for a secret. Those responses prove different kinds of access. Reporting the first as secret access sends defenders to the wrong permission.
Pentest Copilot is organized into assessment modules. We used its Internal Assessment module for these cloud security checks.
Its agents began inside an Azure VM and used its attached managed identity.
Before each run, we approved one subscription and resource group. We also set the safety rules. The agents stayed inside those boundaries. When a resource met a check's requirements, they ran it without waiting for an operator to choose each action.
One check attached a stronger user-managed identity to the VM. From inside the guest, the agent requested a token for that identity. It used the token for an action denied to the starting identity.
After the check, the agent restored the VM's original identity set. A second token request confirmed that the temporary identity was no longer available.
We reviewed each run from its first request through cleanup. For every response, we identified the endpoint and whether its authorization plane accepted or denied the action. That distinction separated management access from data, directory, and guest access. Because 89 checks met the evidence rule, we grouped them into 32 categories.
| Field | Result |
|---|---|
| Research question | When does one Azure VM managed identity produce a verified path across resource, data, directory, or private-network boundaries? |
| Starting condition | Code execution on an Azure VM using its attached managed identity |
| Unit of analysis | One principal, one Azure scope, one target, and one observed transition |
| Evidence threshold | The destination plane accepts the tested operation as the intended identity |
| Recovery threshold | Identity sets, application state, grants, and network state return to baseline |
| Coverage | 89 attack paths across 32 vulnerability categories |
The agents read the VM, subscription, resource group, region, and attached identities from the machine context. They request a token for the intended managed identity. When several user-assigned identities exist, the identity is selected explicitly rather than relying on an ambiguous default.
The detected subscription and resource group must match the approved scope. A subscription can contain unrelated teams and environments; the ability to list them does not expand the experiment.
| Measurement | Required result | Why it is necessary |
|---|---|---|
| VM context | Exact VM and resource group | Connects later execution evidence to the starting machine |
| Principal | Token resolves to the intended managed identity | Prevents an operator session from substituting for workload authority |
| Subscription | Detected and approved identifiers agree | Stops cross-environment drift |
| Target | Specific resource identifier is retained | Prevents a broad role from selecting an unintended object |
Assigning a user-managed identity to an existing VM requires control of both the VM configuration and the selected identity. The path exists only when the source can perform both operations.
Azure's control plane manages resources through Azure Resource Manager. Data-plane requests go to resource-specific endpoints. Creating a storage account is a control-plane operation; reading its objects is a separate data-plane operation.
| Plane | Example question | A successful answer does not prove |
|---|---|---|
| Resource Manager | Can the identity update this VM or vault? | Guest execution or secret access |
| Service data plane | Can it read this blob, secret, queue, or database row? | Permission to reconfigure the resource |
| Microsoft Graph | Can it enumerate or change this directory object? | Azure subscription authority |
| Guest or private service | Can the selected host accept SSH, Kerberos, SMB, or SQL? | That the Azure API call reached the runtime |
An absent object, a 403 from a data endpoint, and a failed collection request remain three different results. This prevents incomplete visibility from appearing as an empty, safe environment.
The representative identity path begins with a user-assigned identity that has more authority than the VM's starting principal but is not attached to the VM.
The source principal can update the VM and assign the selected identity. The agent records the VM's complete identity set before attaching the target identity. From inside the guest, it requests that identity's token, confirms the object identifier, and performs one approved Resource Manager read.
| Stage | Observation |
|---|---|
| Baseline | Guest cannot request a token for the target identity |
| Control | Source can update the VM and assign that identity |
| Transition | Metadata returns a token after attachment |
| Identity | Token resolves to the intended service principal |
| Consequence | Target completes an action unavailable to the source |
| Recovery | Original system-assigned and user-assigned identity set is restored |
If the token resolves to the wrong object, the action fails, or the original identity set cannot be restored, the path fails. The role assignment alone is not compromise.
Azure offers several control-plane routes into a VM. Their runtime consequence is similar, but proof and cleanup differ.
| Route | Required proof | Recovery |
|---|---|---|
| SSH-key update | Selected VM accepts the key and returns its OS identity | Remove temporary user and key |
| Run Command | Output contains a unique marker and intended OS identity | Leave no durable command or artifact |
| Custom Script | Extension succeeds and returns guest output | Remove extension and temporary script |
| Disk export | A known marker is read through a short-lived grant | Revoke grant and restore VM power state |
| Snapshot path | Selected disk state is present in the temporary snapshot | Remove snapshot and export grant |
A successful Resource Manager request proves that Azure accepted the request. Guest output proves that the selected machine executed it.
A Key Vault can be visible through Resource Manager while its secret endpoint returns 403 Forbidden. The split result is expected because Resource Manager and the vault endpoint enforce different authorization planes.
To test a vault-policy path, the agent first confirms that the identity lacks secret-read access. It records the original policy and adds one temporary get grant. The agent then reads one secret version without returning its plaintext and restores the policy.
Read-only checks do not need restoration. For keys, storage, queues, or disks, the agent selects one target and retains only enough output to prove access.
| Target | Functional result |
|---|---|
| Key Vault secret | Exact version is read; plaintext is withheld |
| Managed key | Non-sensitive ciphertext is decrypted or one reversible setting changes |
| Blob or file | One selected object operation succeeds |
| Queue | One message fingerprint is observed without unrelated modification |
| Disk | A small byte range or known marker is recovered through a temporary grant |
The finding identifies principal, object, operation, and restoration status. It does not publish secret values, keys, connection strings, or row contents.
Azure Functions and Logic Apps combine mutable definitions with a runtime identity. Managed connectors add access to an external service, while Automation and container definitions create other execution routes. A package or workflow change becomes privilege only when the selected runtime processes a marker and performs an approved downstream action.
Private connectivity creates a second class of path. An Azure VM can be both a cloud resource and a host on a network that reaches Active Directory, file services, or databases. The agents retain the originating managed identity, private route, destination service, and accepted action as one end-to-end record.
| Hybrid transition | Required evidence |
|---|---|
| Vault secret to AD service account | Exact directory service accepts the authentication material |
| Constrained delegation | Kerberos issues the intended tickets and named service accepts them |
| Object-control abuse | Changed identity authenticates or performs the selected action |
| Domain trust | Referral and destination tickets lead to an accepted host action |
| AD CS | Certificate authenticates as the selected stronger identity |
| Golden Ticket class control | Forged ticket is accepted for one controlled host action |
These results are not inferred from the presence of a domain controller, trust, template, or synchronization configuration. The destination must accept the authority.
Across the Azure runs we reviewed, 89 checks crossed Resource Manager, service data planes, Microsoft Graph, workload runtimes, or private services. Each check had to reach the same boundary as the claimed impact.
An Azure Function can expose a stronger managed identity to anyone who can replace its package. Before deployment, the agent records the function resource, hostname, managed identity, package digest, and ETag. The ETag prevents restoration from overwriting a newer deployment.
The temporary package requests its own managed-identity token, checks the token's object and managed-resource claims, and performs one Resource Manager read through that token. The HTTP response returns only fingerprints and status, never the token. The function must serve the expected marker before the path is accepted.
Both deployment and restoration use conditional updates. If the function's ETag or package digest no longer matches the state recorded by the agent, rollback stops. This prevents the agent from overwriting a deployment made by another operator while the check was running.
Logic Apps create a similar path through workflow definitions. A caller able to change a workflow can add a managed-identity action that reaches Azure Resource Manager or a connected service.
The agent hashes the complete workflow definition, installs one temporary recurrence and action, triggers the workflow, and inspects that specific run. Both the run and action must succeed, and the destination must return the expected HTTP status. A successful PUT to the workflow resource is not enough.
Before the change, the agent also records a rollback ownership marker separately from the definition digest. It restores the workflow only if the live definition and ownership marker still identify the agent's temporary change. If either value has changed, the agent reports concurrent drift instead of replacing the workflow.
Azure Storage has several distinct routes from management authority to data: account shared keys, service or account SAS tokens, file-share permissions, object versions, public access, and encryption-scope configuration.
A shared-key path must prove more than the ability to list keys. The selected key authenticates one controlled request to the intended storage account. Blob reads use a byte range and a fixed object version where available. A disk or snapshot grant is short-lived and tied to the specific resource. Evidence retains identifiers, byte counts, response status, and fingerprints rather than object contents.
This detail matters because Resource Manager can reveal or change storage configuration while the storage endpoint still denies data. Reporting “Storage Account access” without naming the plane hides the remediation boundary.
An eligible Entra role is only a candidate path. The agent records the eligible assignment, activates the role for a limited period, and repeats a Microsoft Graph action that was denied before activation. The role and directory action must resolve to the intended principal.
After the proof, the agent ends the activation and repeats the same Graph request. The path is recovered only when the temporary authority is gone. Application ownership, app-role grants, federation, and cross-tenant delegation remain separate checks in the coverage matrix because they cross different consent boundaries.
Several Azure control planes ultimately create guest or workload access:
| Route | What completes the path |
|---|---|
| Bastion shareable link | The link reaches the selected VM through the intended Bastion host |
| Azure Arc Run Command | The connected machine returns the expected operating-system identity and marker |
| Container App identity | A selected revision runs and resolves the intended managed identity |
| VM Run Command | The intended VM returns limited command output |
| SSH-key update | The new key authenticates to the intended guest and is then removed |
These mechanisms should not be collapsed into one “compute admin” finding. Bastion is a network-mediated access path, Arc uses a connected-machine control channel, and Container Apps prove authority in an ephemeral revision. They have different preventive controls and audit trails.
The management-lock check begins with an operation that Azure denies. The agent records the lock, removes it temporarily, and repeats the same operation against the protected resource. The path is established only when the previously blocked action succeeds.
The agent then restores the lock and retries the operation a third time. Azure must deny it again. Telemetry checks use the same before-change-after pattern for diagnostic settings and flow logs. Security, backup, encryption, and quota checks remain separate categories because they affect detection, recovery, or availability.
| Observation | Not inferred |
|---|---|
| Resource Manager can describe a vault or storage account | Secret, key, blob, queue, or file access |
| Graph can enumerate an application | Ability to authenticate as it |
| A managed identity is assigned to a resource | Ability to obtain and use its token |
| A function or workflow update is accepted | The stronger runtime identity executed |
| A private endpoint or peering exists | The target service is reachable and accepts authentication |
| A role is eligible in PIM | It can be activated and used under current policy |
| Cleanup request returned success | The original state has been restored |
We kept these as separate categories because each crosses a different authorization boundary and requires a different proof.
| Category | Paths | Measured result |
|---|---|---|
| Classic service authentication | 5 | FTP/SFTP, LDAP, MSSQL, MySQL, and SSH acceptance |
| Classic share exposure | 1 | Controlled FTP/SFTP read or write |
| Compute execution | 4 | SSH key, identity attachment, Run Command, and Custom Script |
| Delegation and trust | 5 | Trusted role, workload identity, application identity, service principal, and federation |
| Discovery | 7 | Resource, permission, scope, secret, storage, metadata, and foothold context |
| Identity validation | 1 | Caller identity resolution |
| Managed secrets and keys | 4 | Key administration, decrypt, vault-policy write, and secret read |
| Metadata identity access | 3 | Metadata token, multiple identities, and overprivileged VM identity |
| Permission enumeration | 1 | Effective reachable actions |
| Permission escalation | 4 | Application authentication material, resource-to-admin, managed identity, and resource-policy path |
| Cloud-hosted AD | 5 | ACL, delegation, trust, AD CS, and Golden Ticket |
| Network and exposure | 3 | Broad egress, backend control, and DNS control |
| Storage exposure | 6 | SAS, file share, old versions, public object, shared key, and signed URL |
| Disk and image exfiltration | 3 | Disk export, snapshot export, and snapshot creation |
| Guardrail bypass | 1 | Policy or management-guardrail modification |
| Hybrid identity bridge | 2 | Private-service pivot and federation |
| Serverless and event execution | 5 | Function, Logic App, publishing profile, connector, and event path |
| Audit and telemetry access | 2 | Audit read and sensitive log values |
| Defense evasion | 3 | Lock removal, management bypass, and network protection |
| Impact | 5 | Backup, storage, compute, key, and quota outcomes |
| Persistence | 8 | SSH, role, application, Bastion, access, storage, network, and bootstrap paths |
| Cloud identity initial access | 1 | Application identity is accepted |
| Cross-scope trust | 1 | Cross-tenant action |
| File share persistence | 1 | Azure Files Kerberos application path |
| Application foothold | 1 | Public application to managed identity |
| Configuration secret exposure | 1 | Bootstrap or extension data |
| Kubernetes and containers | 1 | Workload definition with a stronger identity |
| Deployment history exposure | 1 | Deployment data disclosure |
| Software supply chain | 1 | Writable package, bundle, artifact, or mutable image |
| Automation data mining | 1 | Automation variable, connection, certificate, or runbook data |
| Messaging data access | 1 | Queue or topic message |
| Serverless data mining | 1 | Function or App Service deployment package |
| Total | 89 | 32 categories |
Before changing an Azure resource, the responsible agent records a complete baseline and the service's concurrency value. The baseline includes the subscription, resource group, resource identifier, identity set, configuration document, and any ETag or definition digest needed to detect a competing change. Temporary state also carries an ownership marker wherever the service supports one.
| Stage | Azure evidence |
|---|---|
| Baseline | Source identity is denied, the original runtime marker is observed, or the protected control blocks the action |
| Owned mutation | One identity assignment, role, application grant, package, workflow, network rule, lock, or telemetry setting changes |
| Functional proof | The intended Resource Manager, data-plane, Graph, guest, workflow, or private-service action succeeds |
| Conditional rollback | The recorded identity set or configuration is restored only while the live entity tag, fingerprint, and ownership marker match |
| Retest | Temporary authority is denied, the baseline marker returns, and the original control is enforced again |
A VM identity path records the complete set of system-managed and user-managed identities. After the stronger identity is removed, the guest requests its token again and must receive a denial. The original identities are then requested to confirm that rollback did not break legitimate workload access.
Function restoration uses the ETag and package digest captured before deployment. The original package is invoked after restoration and must return its baseline marker. Logic Apps use a normalized definition fingerprint plus a separate rollback owner. The workflow is restored only when the live definition still matches the temporary one, after which a new run must demonstrate baseline behavior.
Directory checks remove the temporary grant, role, federation entry, ownership change, or authentication material. The agent then repeats the same Graph action and requires the introduced identity to lose access.
For storage checks, the agent invalidates the access method used for the proof. Depending on the method, it rotates the selected account key, revokes the delegation or stored access policy, or waits for a short-lived SAS to expire. It then repeats the same data request.
Disk checks remove their temporary grants and resources before repeating the read. For locks, telemetry, security controls, and network rules, the agent reads back the restored configuration before retrying the action that was originally blocked.
A successful Azure cleanup request is not sufficient. Recovery is complete only when the exact resource configuration matches the baseline, the attack transaction no longer succeeds, and the pre-existing workload or control still operates.
Azure findings should name the originating managed identity, the authorization plane, the destination, and the action that succeeded. Separating workload administration from identity assignment, and management rights from data access, gives defenders the control boundary they need to fix.
After the subscription, resource-group, and safety boundaries were approved, Pentest Copilot's agents executed the checks without step-by-step operator control. We analyzed the complete chain for each result: source identity, authorization plane, target resource, observed action, restoration, and post-cleanup denial. That review prevents a Resource Manager permission, Graph relationship, or accepted deployment from being reported as impact before the destination confirms it.