Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
title: Elevate to User Access Administrator at Root Scope
---

# Elevate to User Access Administrator at Root Scope




Platform: Azure

## Mappings

- MITRE ATT&CK
- Privilege Escalation



## Description


Elevates the current principal to the User Access Administrator role at root scope (/),
by abusing the "Access management for Azure resources" capability available to Global Administrators in Entra ID.

This technique allows a Global Administrator to gain control over all Azure subscriptions and management groups
in the tenant, enabling arbitrary role assignments across all Azure resources.

<span style="font-variant: small-caps;">Warm-up</span>: None

<span style="font-variant: small-caps;">Detonation</span>:

- Call the elevateAccess REST API endpoint, which assigns the User Access Administrator role at root scope (/) to the current principal

Revert:

- Remove the User Access Administrator role assignment at root scope (/)

References:

- https://learn.microsoft.com/en-us/azure/role-based-access-control/elevate-access-global-admin
- https://microsoft.github.io/Azure-Threat-Research-Matrix/PrivilegeEscalation/AZT402/AZT402/
- https://www.mandiant.com/resources/blog/detecting-microsoft-365-azure-active-directory-backdoors


## Instructions

```bash title="Detonate with Stratus Red Team"
stratus detonate azure.privilege-escalation.root-user-access-administrator
```
## Detection


Identify when the <code>elevateAccess</code> action is called through either the Entra ID Audit Logs or the Azure Activity Log.

Sample Entra ID Audit Log entry (sensitive fields redacted), filtering on service <code>Azure RBAC (Elevated Access)</code>:

```json hl_lines="1 3"
{
"activityDisplayName": "User has elevated their access to User Access Administrator for their Azure Resources",
"category": "AzureRBACRoleManagementElevateAccess",
"loggedByService": "Azure RBAC (Elevated Access)",
"result": "success",
"additionalDetails": [
{ "key": "OperationName", "value": "Microsoft.Authorization/elevateAccess/action" },
{ "key": "Resource", "value": "/providers/Microsoft.Authorization" }
],
"initiatedBy": {
"user": {
"id": "00000000-0000-0000-0000-000000000000",
"userPrincipalName": "user@example.com",
"ipAddress": "1.2.3.4"
}
}
}
```

Sample Azure Activity Log entry (sensitive fields redacted):

```json hl_lines="2 8"
{
"authorization": {
"action": "Microsoft.Authorization/elevateAccess/action",
"scope": "/providers/Microsoft.Authorization"
},
"caller": "user@example.com",
"operationName": {
"value": "Microsoft.Authorization/elevateAccess/action",
"localizedValue": "Assigns the caller to User Access Administrator role"
},
"status": {
"value": "Succeeded"
}
}
```

Microsoft Sentinel provides a built-in analytics rule <b>Azure RBAC (Elevate Access)</b> to detect this behavior using the <code>AuditLogs</code> table.


5 changes: 5 additions & 0 deletions docs/attack-techniques/azure/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ Note that some Stratus attack techniques may correspond to more than a single AT
- [Create Azure VM Bastion shareable link](./azure.persistence.create-bastion-shareable-link.md)


## Privilege Escalation

- [Elevate to User Access Administrator at Root Scope](./azure.privilege-escalation.root-user-access-administrator.md)


## Exfiltration

- [Export Disk Through SAS URL](./azure.exfiltration.disk-export.md)
Expand Down
1 change: 1 addition & 0 deletions docs/attack-techniques/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ This page contains the list of all Stratus Attack Techniques.
| [Azure ransomware via Storage Account Blob deletion](./azure/azure.impact.blob-ransomware-individual-file-deletion.md) | [Azure](./azure/index.md) | Impact |
| [Delete Azure resource lock](./azure/azure.impact.resource-lock.md) | [Azure](./azure/index.md) | Impact |
| [Create Azure VM Bastion shareable link](./azure/azure.persistence.create-bastion-shareable-link.md) | [Azure](./azure/index.md) | Persistence |
| [Elevate to User Access Administrator at Root Scope](./azure/azure.privilege-escalation.root-user-access-administrator.md) | [Azure](./azure/index.md) | Privilege Escalation |
| [Create Admin EKS Access Entry](./EKS/eks.lateral-movement.create-access-entry.md) | [EKS](./EKS/index.md) | Lateral Movement |
| [Backdoor aws-auth EKS ConfigMap](./EKS/eks.persistence.backdoor-aws-auth-configmap.md) | [EKS](./EKS/index.md) | Persistence, Privilege Escalation |
| [Backdoor Entra ID application through service principal](./entra-id/entra-id.persistence.backdoor-application-sp.md) | [Entra ID](./entra-id/index.md) | Persistence, Privilege Escalation |
Expand Down
8 changes: 4 additions & 4 deletions docs/attack-techniques/mitre-attack-coverage-matrices.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ This provides coverage matrices of MITRE ATT&CK tactics and techniques currently
</div>
<h2>Azure</h2>
<div class="table-container"><table>
<thead><tr><th>Execution</th><th>Persistence</th><th>Exfiltration</th><th>Impact</th></tr></thead>
<thead><tr><th>Execution</th><th>Persistence</th><th>Privilege Escalation</th><th>Exfiltration</th><th>Impact</th></tr></thead>
<tbody>
<tr><td><a href="../Azure/azure.execution.vm-custom-script-extension">Execute Command on Virtual Machine using Custom Script Extension</a></td><td><a href="../Azure/azure.persistence.create-bastion-shareable-link">Create Azure VM Bastion shareable link</a></td><td><a href="../Azure/azure.exfiltration.disk-export">Export Disk Through SAS URL</a></td><td><a href="../Azure/azure.impact.blob-ransomware-client-encryption-scope">Azure Blob Storage ransomware through Encryption Scope using client-managed Key Vault key</a></td></tr>
<tr><td><a href="../Azure/azure.execution.vm-run-command">Execute Commands on Virtual Machine using Run Command</a></td><td></td><td><a href="../Azure/azure.exfiltration.storage-public-access">Exfiltrate Azure Storage via public access</a></td><td><a href="../Azure/azure.impact.blob-ransomware-individual-file-deletion">Azure ransomware via Storage Account Blob deletion</a></td></tr>
<tr><td></td><td></td><td><a href="../Azure/azure.exfiltration.storage-sas-export">Exfiltrate Azure Storage through SAS URL</a></td><td><a href="../Azure/azure.impact.resource-lock">Delete Azure resource lock</a></td></tr>
<tr><td><a href="../Azure/azure.execution.vm-custom-script-extension">Execute Command on Virtual Machine using Custom Script Extension</a></td><td><a href="../Azure/azure.persistence.create-bastion-shareable-link">Create Azure VM Bastion shareable link</a></td><td><a href="../Azure/azure.privilege-escalation.root-user-access-administrator">Elevate to User Access Administrator at Root Scope</a></td><td><a href="../Azure/azure.exfiltration.disk-export">Export Disk Through SAS URL</a></td><td><a href="../Azure/azure.impact.blob-ransomware-client-encryption-scope">Azure Blob Storage ransomware through Encryption Scope using client-managed Key Vault key</a></td></tr>
<tr><td><a href="../Azure/azure.execution.vm-run-command">Execute Commands on Virtual Machine using Run Command</a></td><td></td><td></td><td><a href="../Azure/azure.exfiltration.storage-public-access">Exfiltrate Azure Storage via public access</a></td><td><a href="../Azure/azure.impact.blob-ransomware-individual-file-deletion">Azure ransomware via Storage Account Blob deletion</a></td></tr>
<tr><td></td><td></td><td></td><td><a href="../Azure/azure.exfiltration.storage-sas-export">Exfiltrate Azure Storage through SAS URL</a></td><td><a href="../Azure/azure.impact.resource-lock">Delete Azure resource lock</a></td></tr>
</tbody>
</table>
</div>
Expand Down
8 changes: 8 additions & 0 deletions docs/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,14 @@ Azure:
- Persistence
platform: Azure
isIdempotent: false
Privilege Escalation:
- id: azure.privilege-escalation.root-user-access-administrator
name: Elevate to User Access Administrator at Root Scope
isSlow: false
mitreAttackTactics:
- Privilege Escalation
platform: Azure
isIdempotent: false
Entra ID:
Persistence:
- id: entra-id.persistence.backdoor-application-sp
Expand Down
1 change: 1 addition & 0 deletions v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ require (
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/iam v1.3.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions v2/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 h1:Hp+EScFOu9HeCbeW8WU2yQPJd4gGwhMgKxWe+G6jNzw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0/go.mod h1:/pz8dyNQe+Ey3yBp/XuYz7oqX8YDNWVpPB0hH3XWfbc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E=
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package azure

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"strings"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2"
"github.com/datadog/stratus-red-team/v2/pkg/stratus"
"github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
)

// Built-in User Access Administrator role definition ID (constant across all tenants)
const userAccessAdminRoleDefinitionID = "18d7d88d-d35e-4fb5-a5c3-7773c20a72d9" //TODO: double check this is constant across all tenants

func init() {
const codeBlock = "```"
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: "azure.privilege-escalation.root-user-access-administrator",
FriendlyName: "Elevate to User Access Administrator at Root Scope",
Platform: stratus.Azure,
MitreAttackTactics: []mitreattack.Tactic{
mitreattack.PrivilegeEscalation,
},
Description: `
Elevates the current principal to the User Access Administrator role at root scope (/),
by abusing the "Access management for Azure resources" capability available to Global Administrators in Entra ID.

This technique allows a Global Administrator to gain control over all Azure subscriptions and management groups
in the tenant, enabling arbitrary role assignments across all Azure resources.

Warm-up: None

Detonation:

- Call the elevateAccess REST API endpoint, which assigns the User Access Administrator role at root scope (/) to the current principal

Revert:

- Remove the User Access Administrator role assignment at root scope (/)
If you are getting a 403 error when attempting to revert, you may need to refresh your credentials with <code>az login</code>

References:

- https://learn.microsoft.com/en-us/azure/role-based-access-control/elevate-access-global-admin
- https://microsoft.github.io/Azure-Threat-Research-Matrix/PrivilegeEscalation/AZT402/AZT402/
`,
Detection: `
Identify when the <code>elevateAccess</code> action is called through either the Entra ID Audit Logs or the Azure Activity Log.

Sample Entra ID Audit Log entry (sensitive fields redacted), filtering on service <code>Azure RBAC (Elevated Access)</code>:

` + codeBlock + `json hl_lines="1 3"
{
"operationName": "User has elevated their access to User Access Administrator for their Azure Resources",
"category": "AuditLogs",
"properties": {
"category": "AzureRBACRoleManagementElevateAccess",
"activityDisplayName": "User has elevated their access to User Access Administrator for their Azure Resources",
"loggedByService": "Azure RBAC (Elevated Access)",
"result": "success",
"initiatedBy": {
"user": {
"id": "00000000-0000-0000-0000-000000000000",
"userPrincipalName": "user@example.com",
"ipAddress": "1.2.3.4"
}
}
}
}

` + codeBlock + `

Sample Azure Activity Log entry (sensitive fields redacted):

` + codeBlock + `json hl_lines="2 8"
{
"authorization": {
"action": "Microsoft.Authorization/elevateAccess/action",
"scope": "/providers/Microsoft.Authorization"
},
"caller": "user@example.com",
"operationName": {
"value": "Microsoft.Authorization/elevateAccess/action",
"localizedValue": "Assigns the caller to User Access Administrator role"
},
"properties": {
"statusCode": "OK",
"eventCategory": "Administrative",
"entity": "/providers/Microsoft.Authorization",
"message": "Microsoft.Authorization/elevateAccess/action",
}
"status": {
"value": "Succeeded"
}
}
` + codeBlock + `

Microsoft Sentinel provides a built-in analytics rule <b>Azure RBAC (Elevate Access)</b> to detect this behavior using the <code>AuditLogs</code> table.
`,
IsIdempotent: false,
Detonate: detonate,
Revert: revert,
})
}

func detonate(_ map[string]string, providers stratus.CloudProviders) error {
ctx := context.Background()
cred := providers.Azure().GetCredentials()
clientOptions := providers.Azure().ClientOptions

globalAdminClient, err := armauthorization.NewGlobalAdministratorClient(cred, clientOptions)
if err != nil {
return fmt.Errorf("failed to create global administrator client: %w", err)
}

log.Println("Elevating access to User Access Administrator at root scope (/)")
_, err = globalAdminClient.ElevateAccess(ctx, nil)
if err != nil {
return fmt.Errorf("failed to call elevateAccess: %w", err)
}

log.Println("Successfully elevated access: User Access Administrator role assigned at root scope (/)")
return nil
}

func revert(_ map[string]string, providers stratus.CloudProviders) error {
ctx := context.Background()
cred := providers.Azure().GetCredentials()
clientOptions := providers.Azure().ClientOptions

// Get a management token to extract the current principal's object ID
token, err := cred.GetToken(ctx, policy.TokenRequestOptions{
Scopes: []string{"https://management.azure.com/.default"},
})
if err != nil {
return fmt.Errorf("failed to get access token: %w", err)
}
principalID, err := getOIDFromToken(token.Token)
if err != nil {
return fmt.Errorf("failed to extract principal object ID from access token: %w", err)
}
log.Println("Current principal object ID: " + principalID)

roleAssignmentsClient, err := armauthorization.NewRoleAssignmentsClient("", cred, clientOptions)
if err != nil {
return fmt.Errorf("failed to create role assignments client: %w", err)
}

// List all role assignments at root scope (principalId filter is unsupported at root scope)
// and filter by principal ID and role definition ID in Go
log.Println("Listing role assignments at root scope (/) for principal " + principalID)
pager := roleAssignmentsClient.NewListForScopePager("/", &armauthorization.RoleAssignmentsClientListForScopeOptions{
Filter: to.Ptr("atScope()"),
})

for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return fmt.Errorf("failed to list role assignments at root scope: %w", err)
Comment thread
jbfeldman-dd marked this conversation as resolved.
}
for _, ra := range page.Value {
if *ra.Properties.PrincipalID != principalID {
continue
}
if !strings.HasSuffix(*ra.Properties.RoleDefinitionID, userAccessAdminRoleDefinitionID) {
continue
}
log.Println("Found User Access Administrator role assignment at root scope, deleting it")
_, err := roleAssignmentsClient.DeleteByID(ctx, *ra.ID, nil)
if err != nil {
return fmt.Errorf("failed to delete role assignment %s: %w", *ra.ID, err)
}
log.Println("Successfully removed User Access Administrator role assignment at root scope")
return nil
}
}

return fmt.Errorf("user access administrator role assignment at root scope not found")
}

// getOIDFromToken extracts the object ID (oid claim) from an Azure AD JWT access token
// without validating the signature, since we already obtained the token from Azure AD ourselves.
func getOIDFromToken(tokenString string) (string, error) {
parts := strings.Split(tokenString, ".")
if len(parts) != 3 {
return "", fmt.Errorf("invalid JWT token format: expected 3 parts, got %d", len(parts))
}

// JWT uses base64url encoding without padding - add padding as needed
payload := parts[1]
switch len(payload) % 4 {
case 2:
payload += "=="
case 3:
payload += "="
}

decoded, err := base64.URLEncoding.DecodeString(payload)
if err != nil {
return "", fmt.Errorf("failed to decode JWT payload: %w", err)
}

var claims struct {
OID string `json:"oid"`
}
if err := json.Unmarshal(decoded, &claims); err != nil {
return "", fmt.Errorf("failed to parse JWT claims: %w", err)
}

if claims.OID == "" {
return "", fmt.Errorf("oid claim not found in JWT token")
}

return claims.OID, nil
}
1 change: 1 addition & 0 deletions v2/internal/attacktechniques/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import (
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/impact/blob-ransomware-individual-file-deletion"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/impact/resource-lock"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/privilege-escalation/root-user-access-administrator"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/entra-id/persistence/backdoor-application"
Expand Down