From d4a2de273d29738ec3a363ff9f511a1d3ca6b8bb Mon Sep 17 00:00:00 2001 From: Katie Knowles Date: Thu, 10 Oct 2024 08:34:06 -0400 Subject: [PATCH 1/8] Add Azure Bastion shareable link technique --- .../azure.execution.bastion-shareable-link.md | 85 ++++++++ v2/go.mod | 3 +- v2/go.sum | 4 + .../execution/bastion-shareable-link/main.go | 184 ++++++++++++++++++ .../execution/bastion-shareable-link/main.tf | 171 ++++++++++++++++ v2/internal/attacktechniques/main.go | 1 + 6 files changed, 447 insertions(+), 1 deletion(-) create mode 100644 docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md create mode 100644 v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.go create mode 100644 v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.tf diff --git a/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md b/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md new file mode 100644 index 000000000..20f023196 --- /dev/null +++ b/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md @@ -0,0 +1,85 @@ +--- +title: Access Virtual Machine using Bastion shareable link +--- + +# Access Virtual Machine using Bastion shareable link + + slow + + +Platform: Azure + +## MITRE ATT&CK Tactics + + +- Execution + +## Description + + +By utilizing the 'shareable link' feature on Bastions where it is enabled, an attacker can create a link to allow access to a virtual machine (VM) from untrusted networks. Public links generated for an Azure Bastion can allow VM network access to anyone with the generated URL. + +References: + +- https://blog.karims.cloud/2022/11/26/yet-another-azure-vm-persistence.html +- https://learn.microsoft.com/en-us/azure/bastion/shareable-link +- https://microsoft.github.io/Azure-Threat-Research-Matrix/Persistence/AZT509/AZT509/ + +Warm-up: + +- Create a VM and VNet +- Create an Azure Bastion host with access to the VM, and shareable links enabled +NOTE: Warm-up and cleanup can each take 10-15 minutes to create and destroy the Azure Bastion instance + +Detonation: + +- Create an Azure Bastion shareable link with access to the VM + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate azure.execution.bastion-shareable-link +``` +## Detection + + +Identify Azure events of type Microsoft.Network/bastionHosts/createshareablelinks/action and Microsoft.Network/bastionHosts/getShareablelinks/action. A sample of createshareablelinks is shown below (redacted for clarity). + +```json hl_lines="7" + { + "category": { + "value": "Administrative", + "localizedValue": "Administrative" + }, + "level": "Informational", + "operationName": { + "value": "Microsoft.Network/bastionHosts/createshareablelinks/action", + "localizedValue": "Creates shareable urls for the VMs under a bastion and returns the urls" + }, + "resourceGroupName": "stratus-red-team-shareable-link-rg-tz6o", + "resourceProviderName": { + "value": "Microsoft.Network", + "localizedValue": "Microsoft.Network" + }, + "resourceType": { + "value": "Microsoft.Network/bastionHosts", + "localizedValue": "Microsoft.Network/bastionHosts" + }, + "resourceId": "[removed]/resourceGroups/stratus-red-team-shareable-link-rg-tz6o/providers/Microsoft.Network/bastionHosts/stratus-red-team-shareable-link-bastion-tz6o", + "status": { + "value": "Succeeded", + "localizedValue": "Succeeded" + }, + "subStatus": { + "value": "", + "localizedValue": "" + }, + "properties": { + "eventCategory": "Administrative", + "entity": "[removed]/resourceGroups/stratus-red-team-shareable-link-rg-tz6o/providers/Microsoft.Network/bastionHosts/stratus-red-team-shareable-link-bastion-tz6o", + "message": "Microsoft.Network/bastionHosts/createshareablelinks/action", + "hierarchy": "[removed]" + }, + } +``` \ No newline at end of file diff --git a/v2/go.mod b/v2/go.mod index d42fe99c8..8de077d6b 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -7,7 +7,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 github.com/aws/aws-sdk-go-v2 v1.30.3 github.com/aws/aws-sdk-go-v2/config v1.25.11 github.com/aws/aws-sdk-go-v2/credentials v1.16.9 @@ -47,6 +47,7 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.1.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect diff --git a/v2/go.sum b/v2/go.sum index 1363dd393..9f19fde65 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -12,8 +12,12 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0/go.mod h1:gM3K25LQlsET3QR+4V74zxCsFAy0r6xMNN9n80SZn+4= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0 h1:lMW1lD/17LUA5z1XTURo7LcVG2ICBPlyMHjIUrcFZNQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.0.0 h1:nBy98uKOIfun5z6wx6jwWLrULcM0+cjBalBFZlEZ7CA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.1.0 h1:Fd+iaEa+JBwzYo6OTWYSNqyvlPSLciMGsmsnYCKcXM0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.1.0/go.mod h1:ulHyBFJOI0ONiRL4vcJTmS7rx18jQQlEPmAgo80cRdM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= diff --git a/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.go b/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.go new file mode 100644 index 000000000..e547faddc --- /dev/null +++ b/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.go @@ -0,0 +1,184 @@ +package azure + +import ( + "context" + _ "embed" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "log" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6" + "fmt" +) + +//go:embed main.tf +var tf []byte + +func init() { + const codeBlock = "```" + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "azure.execution.bastion-shareable-link", + FriendlyName: "Access Virtual Machine using Bastion shareable link", + Description: ` +By utilizing the 'shareable link' feature on Bastions where it is enabled, an attacker can create a link to allow access to a virtual machine (VM) from untrusted networks. Public links generated for an Azure Bastion can allow VM network access to anyone with the generated URL. + +References: + +- https://blog.karims.cloud/2022/11/26/yet-another-azure-vm-persistence.html +- https://learn.microsoft.com/en-us/azure/bastion/shareable-link +- https://microsoft.github.io/Azure-Threat-Research-Matrix/Persistence/AZT509/AZT509/ + +Warm-up: + +- Create a VM and VNet +- Create an Azure Bastion host with access to the VM, and shareable links enabled +NOTE: Warm-up and cleanup can each take 10-15 minutes to create and destroy the Azure Bastion instance + +Detonation: + +- Create an Azure Bastion shareable link with access to the VM +`, + Detection: ` +Identify Azure events of type Microsoft.Network/bastionHosts/createshareablelinks/action and Microsoft.Network/bastionHosts/getShareablelinks/action. A sample of createshareablelinks is shown below (redacted for clarity). + +` + codeBlock + `json hl_lines="7" +{ + { + "category": { + "value": "Administrative", + "localizedValue": "Administrative" + }, + "level": "Informational", + "operationName": { + "value": "Microsoft.Network/bastionHosts/createshareablelinks/action", + "localizedValue": "Creates shareable urls for the VMs under a bastion and returns the urls" + }, + "resourceGroupName": "stratus-red-team-shareable-link-rg-tz6o", + "resourceProviderName": { + "value": "Microsoft.Network", + "localizedValue": "Microsoft.Network" + }, + "resourceType": { + "value": "Microsoft.Network/bastionHosts", + "localizedValue": "Microsoft.Network/bastionHosts" + }, + "resourceId": "[removed]/resourceGroups/stratus-red-team-shareable-link-rg-tz6o/providers/Microsoft.Network/bastionHosts/stratus-red-team-shareable-link-bastion-tz6o", + "status": { + "value": "Succeeded", + "localizedValue": "Succeeded" + }, + "subStatus": { + "value": "", + "localizedValue": "" + }, + "properties": { + "eventCategory": "Administrative", + "entity": "[removed]/resourceGroups/stratus-red-team-shareable-link-rg-tz6o/providers/Microsoft.Network/bastionHosts/stratus-red-team-shareable-link-bastion-tz6o", + "message": "Microsoft.Network/bastionHosts/createshareablelinks/action", + "hierarchy": "[removed]" + }, +} +` + codeBlock + ` +`, + Platform: stratus.Azure, + IsSlow: true, + IsIdempotent: false, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Execution}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + Revert: revert, + }) +} + +const ExtensionName = "CustomScriptExtension-StratusRedTeam-Example" + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + bastionName := params["bastion_name"] + resourceGroup := params["resource_group_name"] + vmId := params["vm_id"] + vmName := params["vm_name"] + tenantId := params["tenant_id"] + + ctx := context.Background() + cred := providers.Azure().GetCredentials() + subscriptionID := providers.Azure().SubscriptionID + clientOptions := providers.Azure().ClientOptions + + client, err := armnetwork.NewClientFactory(subscriptionID, cred, clientOptions) + if err != nil { + log.Fatalf("failed to create client: %v", err) + } + + // Create Bastion shareable link + // Reference method: https://learn.microsoft.com/en-us/rest/api/virtualnetwork/put-bastion-shareable-link/put-bastion-shareable-link + log.Println("Getting Bastion shareable link for VM " +vmName) + + poller, err := client.NewManagementClient().BeginPutBastionShareableLink(ctx, resourceGroup, bastionName, armnetwork.BastionShareableLinkListRequest{ + VMs: []*armnetwork.BastionShareableLink{ + { + VM: &armnetwork.VM{ + ID: to.Ptr(vmId), + }, + }, + }, + }, nil) + if err != nil { + log.Fatalf("failed to create shareable link: %v", err) + } + + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + log.Fatalf("failed to poll results: %v", err) + } + log.Println("Shareable link created") + + // Provide URL to access Bastion shareable link + // NOTE: Response via Go SDK methods does not return any page contents, so we'll supply a Portal URL to fetch the link for now. (The example cited in reference link above is not clear on how to resolve this.) + url := fmt.Sprintln("https://portal.azure.com/#@" + tenantId + "/resource/subscriptions/" + subscriptionID + "/resourceGroups/" + resourceGroup + "/providers/Microsoft.Network/bastionHosts/" + bastionName + "/shareablelinks") + + log.Println("You can view and fetch the shareable link URL here: " + url) + + return nil +} + +func revert(params map[string]string, providers stratus.CloudProviders) error { + // Reference method: https://learn.microsoft.com/en-us/rest/api/virtualnetwork/delete-bastion-shareable-link/delete-bastion-shareable-link?view=rest-virtualnetwork-2024-03-01&tabs=Go + bastionName := params["bastion_name"] + resourceGroup := params["resource_group_name"] + vmId := params["vm_id"] + vmName := params["vm_name"] + + ctx := context.Background() + cred := providers.Azure().GetCredentials() + subscriptionID := providers.Azure().SubscriptionID + clientOptions := providers.Azure().ClientOptions + + client, err := armnetwork.NewClientFactory(subscriptionID, cred, clientOptions) + if err != nil { + log.Fatalf("failed to create client: %v", err) + } + + // Delete shareable link that was previously created + log.Println("Deleting shareable Bastion link to VM " + vmName) + + poller, err := client.NewManagementClient().BeginDeleteBastionShareableLink(ctx, resourceGroup, bastionName, armnetwork.BastionShareableLinkListRequest{ + VMs: []*armnetwork.BastionShareableLink{ + { + VM: &armnetwork.VM{ + ID: to.Ptr(vmId), + }, + }, + }, + }, nil) + if err != nil { + log.Fatalf("failed to finish the request: %v", err) + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + log.Fatalf("failed to pull the result: %v", err) + } + + log.Println("Shareable link deleted") + + return nil +} \ No newline at end of file diff --git a/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.tf b/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.tf new file mode 100644 index 000000000..16ceff2f5 --- /dev/null +++ b/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.tf @@ -0,0 +1,171 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.8.0" + } + } +} + +provider "azurerm" { + features {} +} + +locals { + resource_prefix = "stratus-red-team-shareable-link" +} + +data "azurerm_client_config" "current" { +} + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Random +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +resource "random_string" "lab_name" { + length = 4 + special = false + upper = false +} + +resource "random_password" "password" { + length = 64 + special = true + override_special = "!#$%&*()-_=+[]{}<>:?" +} + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Resource Group +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +resource "azurerm_resource_group" "lab_environment" { + name = "${local.resource_prefix}-rg-${random_string.lab_name.result}" + location = "West US" +} + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Networking Resources +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +resource "azurerm_virtual_network" "lab_vnet" { + name = "${local.resource_prefix}-vnet-${random_string.lab_name.result}" + address_space = ["10.0.0.0/24"] + location = azurerm_resource_group.lab_environment.location + resource_group_name = azurerm_resource_group.lab_environment.name +} + +resource "azurerm_subnet" "bastion_subnet" { + # Required naming for deployment of Azure Bastion + name = "AzureBastionSubnet" + resource_group_name = azurerm_resource_group.lab_environment.name + virtual_network_name = azurerm_virtual_network.lab_vnet.name + address_prefixes = ["10.0.0.0/27"] +} + +resource "azurerm_subnet" "lab_subnet" { + name = "${local.resource_prefix}-subnet-${random_string.lab_name.result}" + resource_group_name = azurerm_resource_group.lab_environment.name + virtual_network_name = azurerm_virtual_network.lab_vnet.name + address_prefixes = ["10.0.0.32/27"] +} + +resource "azurerm_public_ip" "lab_pip" { + name = "${local.resource_prefix}-pip-${random_string.lab_name.result}" + location = azurerm_resource_group.lab_environment.location + resource_group_name = azurerm_resource_group.lab_environment.name + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_network_interface" "lab_nic" { + name = "${local.resource_prefix}-nic-${random_string.lab_name.result}" + location = azurerm_resource_group.lab_environment.location + resource_group_name = azurerm_resource_group.lab_environment.name + + ip_configuration { + name = "${local.resource_prefix}-ip-${random_string.lab_name.result}" + subnet_id = azurerm_subnet.lab_subnet.id + private_ip_address_allocation = "Dynamic" + } +} + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Virtual Machine Resources +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +resource "azurerm_windows_virtual_machine" "lab_windows_vm" { + name = "srt-vm-bsl" # 15 character limit: stratus red team - vm - bastion shareable link + resource_group_name = azurerm_resource_group.lab_environment.name + location = azurerm_resource_group.lab_environment.location + size = "Standard_F2" + admin_username = "local_admin_user" + admin_password = random_password.password.result + user_data = base64encode(random_string.lab_name.result) + + network_interface_ids = [ + azurerm_network_interface.lab_nic.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } +} + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Bastion Resource +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +# Note: Creation/destruction of a Bastion can take 10 minutes each, see https://learn.microsoft.com/en-us/azure/bastion/tutorial-create-host-portal +resource "azurerm_bastion_host" "bastion" { + name = "${local.resource_prefix}-bastion-${random_string.lab_name.result}" + location = azurerm_resource_group.lab_environment.location + resource_group_name = azurerm_resource_group.lab_environment.name + # Required for shareable link feature + sku = "Standard" + shareable_link_enabled = "true" + + ip_configuration { + name = "${local.resource_prefix}-ipconfig-${random_string.lab_name.result}" + subnet_id = azurerm_subnet.bastion_subnet.id + public_ip_address_id = azurerm_public_ip.lab_pip.id + } +} + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Outputs +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +output "resource_group_name" { + value = azurerm_resource_group.lab_environment.name +} + +output "bastion_name" { + value = azurerm_bastion_host.bastion.name +} + +output "vm_id"{ + value = azurerm_windows_virtual_machine.lab_windows_vm.id +} + +output "vm_name"{ + value = azurerm_windows_virtual_machine.lab_windows_vm.name +} + +output tenant_id{ + value = data.azurerm_client_config.current.tenant_id +} + +output "display" { + value = format( + "Bastion %s ready in resource group %s, with access to VM %s.", + azurerm_windows_virtual_machine.lab_windows_vm.name, + azurerm_resource_group.lab_environment.name, + azurerm_windows_virtual_machine.lab_windows_vm.name + ) +} \ No newline at end of file diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index 9c2f6f5aa..1e4e66b9c 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -42,6 +42,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-custom-script-extension" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-run-command" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/bastion-shareable-link" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/exfiltration/disk-export" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap" From f35d0688546e65cb581d427975147a2677c93e85 Mon Sep 17 00:00:00 2001 From: Katie Knowles Date: Thu, 10 Oct 2024 09:02:27 -0400 Subject: [PATCH 2/8] Add delay note for Bastion technique --- .../azure.execution.bastion-shareable-link.md | 76 +++++++------------ 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md b/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md index 20f023196..f4c35687f 100644 --- a/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md +++ b/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md @@ -1,10 +1,10 @@ --- -title: Access Virtual Machine using Bastion shareable link +title: Execute Command on Virtual Machine using Custom Script Extension --- -# Access Virtual Machine using Bastion shareable link +# Execute Command on Virtual Machine using Custom Script Extension - slow + slow Platform: Azure @@ -17,69 +17,49 @@ Platform: Azure ## Description -By utilizing the 'shareable link' feature on Bastions where it is enabled, an attacker can create a link to allow access to a virtual machine (VM) from untrusted networks. Public links generated for an Azure Bastion can allow VM network access to anyone with the generated URL. +By utilizing the 'CustomScriptExtension' extension on a Virtual Machine, an attacker can pass PowerShell commands to the VM as SYSTEM. +NOTE: This technique will take 10-15 minutes to warmup, and 10-15 minutes to cleanup. This is due to the time to deploy an Azure Bastion. References: -- https://blog.karims.cloud/2022/11/26/yet-another-azure-vm-persistence.html -- https://learn.microsoft.com/en-us/azure/bastion/shareable-link -- https://microsoft.github.io/Azure-Threat-Research-Matrix/Persistence/AZT509/AZT509/ +- https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/custom-script-windows +- https://microsoft.github.io/Azure-Threat-Research-Matrix/Execution/AZT301/AZT301-2/ Warm-up: -- Create a VM and VNet -- Create an Azure Bastion host with access to the VM, and shareable links enabled -NOTE: Warm-up and cleanup can each take 10-15 minutes to create and destroy the Azure Bastion instance +- Create a virtual machine Detonation: -- Create an Azure Bastion shareable link with access to the VM +- Configure a custom script extension for the virtual machine ## Instructions ```bash title="Detonate with Stratus Red Team" -stratus detonate azure.execution.bastion-shareable-link +stratus detonate azure.execution.vm-custom-script-extension ``` ## Detection -Identify Azure events of type Microsoft.Network/bastionHosts/createshareablelinks/action and Microsoft.Network/bastionHosts/getShareablelinks/action. A sample of createshareablelinks is shown below (redacted for clarity). +Identify Azure events of type Microsoft.Compute/virtualMachines/extensions/write. Sample below (redacted for clarity). ```json hl_lines="7" - { - "category": { - "value": "Administrative", - "localizedValue": "Administrative" - }, - "level": "Informational", - "operationName": { - "value": "Microsoft.Network/bastionHosts/createshareablelinks/action", - "localizedValue": "Creates shareable urls for the VMs under a bastion and returns the urls" - }, - "resourceGroupName": "stratus-red-team-shareable-link-rg-tz6o", - "resourceProviderName": { - "value": "Microsoft.Network", - "localizedValue": "Microsoft.Network" - }, - "resourceType": { - "value": "Microsoft.Network/bastionHosts", - "localizedValue": "Microsoft.Network/bastionHosts" - }, - "resourceId": "[removed]/resourceGroups/stratus-red-team-shareable-link-rg-tz6o/providers/Microsoft.Network/bastionHosts/stratus-red-team-shareable-link-bastion-tz6o", - "status": { - "value": "Succeeded", - "localizedValue": "Succeeded" - }, - "subStatus": { - "value": "", - "localizedValue": "" - }, - "properties": { - "eventCategory": "Administrative", - "entity": "[removed]/resourceGroups/stratus-red-team-shareable-link-rg-tz6o/providers/Microsoft.Network/bastionHosts/stratus-red-team-shareable-link-bastion-tz6o", - "message": "Microsoft.Network/bastionHosts/createshareablelinks/action", - "hierarchy": "[removed]" - }, - } +{ + "duration": 0, + "resourceId": "/SUBSCRIPTIONS//RESOURCEGROUPS/RG-HAT6H48Q/PROVIDERS/MICROSOFT.COMPUTE/VIRTUALMACHINES/VM-HAT6H48Q/EXTENSIONS/CUSTOMSCRIPTEXTENSION-STRATUS-EXAMPLE", + "evt": { + "category": "Administrative", + "outcome": "Start", + "name": "MICROSOFT.COMPUTE/VIRTUALMACHINES/EXTENSIONS/WRITE" + }, + "resource_name": "customscriptextension-stratus-example", + "time": "2022-06-18T19:57:27.8617215Z", + "properties": { + "hierarchy": "ecc2b97b-844b-414e-8123-b925dddf87ed/", + "message": "Microsoft.Compute/virtualMachines/extensions/write", + "eventCategory": "Administrative", + "entity": "/subscriptions//resourceGroups/rg-hat6h48q/providers/Microsoft.Compute/virtualMachines/vm-hat6h48q/extensions/CustomScriptExtension-Stratus-Example" + }, +} ``` \ No newline at end of file From cda9187a58082770f3007da23533052c950dd070 Mon Sep 17 00:00:00 2001 From: Katie Knowles Date: Thu, 10 Oct 2024 09:07:49 -0400 Subject: [PATCH 3/8] Add techniqe documentation --- .../azure.execution.bastion-shareable-link.md | 73 ++++++++++++------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md b/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md index f4c35687f..14df1f984 100644 --- a/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md +++ b/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md @@ -1,8 +1,8 @@ --- -title: Execute Command on Virtual Machine using Custom Script Extension +title: Access Virtual Machine using Bastion shareable link --- -# Execute Command on Virtual Machine using Custom Script Extension +# Access Virtual Machine using Bastion shareable link slow @@ -17,49 +17,68 @@ Platform: Azure ## Description -By utilizing the 'CustomScriptExtension' extension on a Virtual Machine, an attacker can pass PowerShell commands to the VM as SYSTEM. +By utilizing the 'shareable link' feature on Bastions where it is enabled, an attacker can create a link to allow access to a virtual machine (VM) from untrusted networks. Public links generated for an Azure Bastion can allow VM network access to anyone with the generated URL. NOTE: This technique will take 10-15 minutes to warmup, and 10-15 minutes to cleanup. This is due to the time to deploy an Azure Bastion. References: -- https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/custom-script-windows -- https://microsoft.github.io/Azure-Threat-Research-Matrix/Execution/AZT301/AZT301-2/ +- https://blog.karims.cloud/2022/11/26/yet-another-azure-vm-persistence.html +- https://learn.microsoft.com/en-us/azure/bastion/shareable-link +- https://microsoft.github.io/Azure-Threat-Research-Matrix/Persistence/AZT509/AZT509/ Warm-up: -- Create a virtual machine +- Create a VM and VNet +- Create an Azure Bastion host with access to the VM, and shareable links enabled +NOTE: Warm-up and cleanup can each take 10-15 minutes to create and destroy the Azure Bastion instance Detonation: -- Configure a custom script extension for the virtual machine - +- Create an Azure Bastion shareable link with access to the VM ## Instructions ```bash title="Detonate with Stratus Red Team" -stratus detonate azure.execution.vm-custom-script-extension +stratus detonate azure.execution.bastion-shareable-link ``` ## Detection - -Identify Azure events of type Microsoft.Compute/virtualMachines/extensions/write. Sample below (redacted for clarity). +Identify Azure events of type Microsoft.Network/bastionHosts/createshareablelinks/action and Microsoft.Network/bastionHosts/getShareablelinks/action. A sample of createshareablelinks is shown below (redacted for clarity). ```json hl_lines="7" -{ - "duration": 0, - "resourceId": "/SUBSCRIPTIONS//RESOURCEGROUPS/RG-HAT6H48Q/PROVIDERS/MICROSOFT.COMPUTE/VIRTUALMACHINES/VM-HAT6H48Q/EXTENSIONS/CUSTOMSCRIPTEXTENSION-STRATUS-EXAMPLE", - "evt": { - "category": "Administrative", - "outcome": "Start", - "name": "MICROSOFT.COMPUTE/VIRTUALMACHINES/EXTENSIONS/WRITE" - }, - "resource_name": "customscriptextension-stratus-example", - "time": "2022-06-18T19:57:27.8617215Z", - "properties": { - "hierarchy": "ecc2b97b-844b-414e-8123-b925dddf87ed/", - "message": "Microsoft.Compute/virtualMachines/extensions/write", - "eventCategory": "Administrative", - "entity": "/subscriptions//resourceGroups/rg-hat6h48q/providers/Microsoft.Compute/virtualMachines/vm-hat6h48q/extensions/CustomScriptExtension-Stratus-Example" - }, + { + "category": { + "value": "Administrative", + "localizedValue": "Administrative" + }, + "level": "Informational", + "operationName": { + "value": "Microsoft.Network/bastionHosts/createshareablelinks/action", + "localizedValue": "Creates shareable urls for the VMs under a bastion and returns the urls" + }, + "resourceGroupName": "stratus-red-team-shareable-link-rg-tz6o", + "resourceProviderName": { + "value": "Microsoft.Network", + "localizedValue": "Microsoft.Network" + }, + "resourceType": { + "value": "Microsoft.Network/bastionHosts", + "localizedValue": "Microsoft.Network/bastionHosts" + }, + "resourceId": "[removed]/resourceGroups/stratus-red-team-shareable-link-rg-tz6o/providers/Microsoft.Network/bastionHosts/stratus-red-team-shareable-link-bastion-tz6o", + "status": { + "value": "Succeeded", + "localizedValue": "Succeeded" + }, + "subStatus": { + "value": "", + "localizedValue": "" + }, + "properties": { + "eventCategory": "Administrative", + "entity": "[removed]/resourceGroups/stratus-red-team-shareable-link-rg-tz6o/providers/Microsoft.Network/bastionHosts/stratus-red-team-shareable-link-bastion-tz6o", + "message": "Microsoft.Network/bastionHosts/createshareablelinks/action", + "hierarchy": "[removed]" + }, } ``` \ No newline at end of file From b5b7503c9f922256c43ea0702c0bad68b28d6904 Mon Sep 17 00:00:00 2001 From: Katie Knowles Date: Thu, 10 Oct 2024 09:19:43 -0400 Subject: [PATCH 4/8] Change category to persistence --- ...-link.md => azure.persistence.bastion-shareable-link.md} | 4 ++-- .../bastion-shareable-link/main.go | 6 ++---- .../bastion-shareable-link/main.tf | 0 v2/internal/attacktechniques/main.go | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) rename docs/attack-techniques/azure/{azure.execution.bastion-shareable-link.md => azure.persistence.bastion-shareable-link.md} (97%) rename v2/internal/attacktechniques/azure/{execution => persistence}/bastion-shareable-link/main.go (97%) rename v2/internal/attacktechniques/azure/{execution => persistence}/bastion-shareable-link/main.tf (100%) diff --git a/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md b/docs/attack-techniques/azure/azure.persistence.bastion-shareable-link.md similarity index 97% rename from docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md rename to docs/attack-techniques/azure/azure.persistence.bastion-shareable-link.md index 14df1f984..96b8ec778 100644 --- a/docs/attack-techniques/azure/azure.execution.bastion-shareable-link.md +++ b/docs/attack-techniques/azure/azure.persistence.bastion-shareable-link.md @@ -12,7 +12,7 @@ Platform: Azure ## MITRE ATT&CK Tactics -- Execution +- Persistence ## Description @@ -39,7 +39,7 @@ NOTE: Warm-up and cleanup can each take 10-15 minutes to create and destroy the ## Instructions ```bash title="Detonate with Stratus Red Team" -stratus detonate azure.execution.bastion-shareable-link +stratus detonate azure.persistence.bastion-shareable-link ``` ## Detection diff --git a/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.go b/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.go similarity index 97% rename from v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.go rename to v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.go index e547faddc..a7b7988ed 100644 --- a/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.go +++ b/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.go @@ -17,7 +17,7 @@ var tf []byte func init() { const codeBlock = "```" stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ - ID: "azure.execution.bastion-shareable-link", + ID: "azure.persistence.bastion-shareable-link", FriendlyName: "Access Virtual Machine using Bastion shareable link", Description: ` By utilizing the 'shareable link' feature on Bastions where it is enabled, an attacker can create a link to allow access to a virtual machine (VM) from untrusted networks. Public links generated for an Azure Bastion can allow VM network access to anyone with the generated URL. @@ -83,15 +83,13 @@ Identify Azure events of type Microsoft.Network/bastionHosts/createshareab Platform: stratus.Azure, IsSlow: true, IsIdempotent: false, - MitreAttackTactics: []mitreattack.Tactic{mitreattack.Execution}, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Persistence}, PrerequisitesTerraformCode: tf, Detonate: detonate, Revert: revert, }) } -const ExtensionName = "CustomScriptExtension-StratusRedTeam-Example" - func detonate(params map[string]string, providers stratus.CloudProviders) error { bastionName := params["bastion_name"] resourceGroup := params["resource_group_name"] diff --git a/v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.tf b/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf similarity index 100% rename from v2/internal/attacktechniques/azure/execution/bastion-shareable-link/main.tf rename to v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index 1e4e66b9c..a0d63a261 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -42,7 +42,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-custom-script-extension" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-run-command" - _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/bastion-shareable-link" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/exfiltration/disk-export" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap" From 37dc8ac5c8caa8f267be8b01e8d34421b0e1e731 Mon Sep 17 00:00:00 2001 From: Katie Knowles Date: Thu, 10 Oct 2024 09:26:28 -0400 Subject: [PATCH 5/8] Fix tf formatting --- .../persistence/bastion-shareable-link/main.tf | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf b/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf index 16ceff2f5..5fcd8f497 100644 --- a/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf +++ b/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf @@ -124,12 +124,12 @@ resource "azurerm_windows_virtual_machine" "lab_windows_vm" { # Note: Creation/destruction of a Bastion can take 10 minutes each, see https://learn.microsoft.com/en-us/azure/bastion/tutorial-create-host-portal resource "azurerm_bastion_host" "bastion" { - name = "${local.resource_prefix}-bastion-${random_string.lab_name.result}" - location = azurerm_resource_group.lab_environment.location - resource_group_name = azurerm_resource_group.lab_environment.name + name = "${local.resource_prefix}-bastion-${random_string.lab_name.result}" + location = azurerm_resource_group.lab_environment.location + resource_group_name = azurerm_resource_group.lab_environment.name # Required for shareable link feature - sku = "Standard" - shareable_link_enabled = "true" + sku = "Standard" + shareable_link_enabled = "true" ip_configuration { name = "${local.resource_prefix}-ipconfig-${random_string.lab_name.result}" @@ -149,15 +149,15 @@ output "bastion_name" { value = azurerm_bastion_host.bastion.name } -output "vm_id"{ +output "vm_id" { value = azurerm_windows_virtual_machine.lab_windows_vm.id } -output "vm_name"{ +output "vm_name" { value = azurerm_windows_virtual_machine.lab_windows_vm.name } -output tenant_id{ +output "tenant_id" { value = data.azurerm_client_config.current.tenant_id } From 6268f5f9f71f5860d9114ffca3f9d1e1f2514980 Mon Sep 17 00:00:00 2001 From: Katie Knowles Date: Thu, 10 Oct 2024 17:01:39 -0400 Subject: [PATCH 6/8] Error and string handling + technique rename --- ...d => azure.create-bastion-shareable-link.md} | 0 .../main.go | 17 ++++++++--------- .../main.tf | 2 +- v2/internal/attacktechniques/main.go | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) rename docs/attack-techniques/azure/{azure.persistence.bastion-shareable-link.md => azure.create-bastion-shareable-link.md} (100%) rename v2/internal/attacktechniques/azure/persistence/{bastion-shareable-link => create-bastion-shareable-link}/main.go (90%) rename v2/internal/attacktechniques/azure/persistence/{bastion-shareable-link => create-bastion-shareable-link}/main.tf (99%) diff --git a/docs/attack-techniques/azure/azure.persistence.bastion-shareable-link.md b/docs/attack-techniques/azure/azure.create-bastion-shareable-link.md similarity index 100% rename from docs/attack-techniques/azure/azure.persistence.bastion-shareable-link.md rename to docs/attack-techniques/azure/azure.create-bastion-shareable-link.md diff --git a/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.go b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go similarity index 90% rename from v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.go rename to v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go index a7b7988ed..394e1b330 100644 --- a/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.go +++ b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go @@ -6,7 +6,6 @@ import ( "github.com/datadog/stratus-red-team/v2/pkg/stratus" "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" "log" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6" "fmt" ) @@ -17,7 +16,7 @@ var tf []byte func init() { const codeBlock = "```" stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ - ID: "azure.persistence.bastion-shareable-link", + ID: "azure.persistence.create-bastion-shareable-link", FriendlyName: "Access Virtual Machine using Bastion shareable link", Description: ` By utilizing the 'shareable link' feature on Bastions where it is enabled, an attacker can create a link to allow access to a virtual machine (VM) from untrusted networks. Public links generated for an Azure Bastion can allow VM network access to anyone with the generated URL. @@ -115,24 +114,24 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error VMs: []*armnetwork.BastionShareableLink{ { VM: &armnetwork.VM{ - ID: to.Ptr(vmId), + ID: &vmId, }, }, }, }, nil) if err != nil { - log.Fatalf("failed to create shareable link: %v", err) + return fmt.Errorf("failed to create shareable link: %v", err) } _, err = poller.PollUntilDone(ctx, nil) if err != nil { - log.Fatalf("failed to poll results: %v", err) + return fmt.Errorf("failed to poll results of shareable link request: %v", err) } log.Println("Shareable link created") // Provide URL to access Bastion shareable link // NOTE: Response via Go SDK methods does not return any page contents, so we'll supply a Portal URL to fetch the link for now. (The example cited in reference link above is not clear on how to resolve this.) - url := fmt.Sprintln("https://portal.azure.com/#@" + tenantId + "/resource/subscriptions/" + subscriptionID + "/resourceGroups/" + resourceGroup + "/providers/Microsoft.Network/bastionHosts/" + bastionName + "/shareablelinks") + url := fmt.Sprintf("https://portal.azure.com/#@%s/resource/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/bastionHosts/%s/shareablelinks", tenantId, subscriptionID, resourceGroup, bastionName) log.Println("You can view and fetch the shareable link URL here: " + url) @@ -163,17 +162,17 @@ func revert(params map[string]string, providers stratus.CloudProviders) error { VMs: []*armnetwork.BastionShareableLink{ { VM: &armnetwork.VM{ - ID: to.Ptr(vmId), + ID: &vmId, }, }, }, }, nil) if err != nil { - log.Fatalf("failed to finish the request: %v", err) + return fmt.Errorf("failed to delete shareable bastion link: %v", err) } _, err = poller.PollUntilDone(ctx, nil) if err != nil { - log.Fatalf("failed to pull the result: %v", err) + return fmt.Errorf("failed to poll results of deleting shareable bastion link: %v", err) } log.Println("Shareable link deleted") diff --git a/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.tf similarity index 99% rename from v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf rename to v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.tf index 5fcd8f497..e39fe5b3a 100644 --- a/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link/main.tf +++ b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.tf @@ -129,7 +129,7 @@ resource "azurerm_bastion_host" "bastion" { resource_group_name = azurerm_resource_group.lab_environment.name # Required for shareable link feature sku = "Standard" - shareable_link_enabled = "true" + shareable_link_enabled = true ip_configuration { name = "${local.resource_prefix}-ipconfig-${random_string.lab_name.result}" diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index a0d63a261..9c2518aa1 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -42,7 +42,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-custom-script-extension" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-run-command" - _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/persistence/bastion-shareable-link" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/exfiltration/disk-export" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap" From 33f611a0c6f17c55faf433fa1d1cdfbadefd900e Mon Sep 17 00:00:00 2001 From: Katie Knowles Date: Fri, 11 Oct 2024 11:27:59 -0400 Subject: [PATCH 7/8] Update v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go Co-authored-by: Christophe Tafani-Dereeper --- .../azure/persistence/create-bastion-shareable-link/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go index 394e1b330..9a82509cb 100644 --- a/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go +++ b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go @@ -103,7 +103,7 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error client, err := armnetwork.NewClientFactory(subscriptionID, cred, clientOptions) if err != nil { - log.Fatalf("failed to create client: %v", err) + return fmt.Errorf("failed to create client: %v", err) } // Create Bastion shareable link From 7fc68305a668800ed9251ac5fa20d39936f07678 Mon Sep 17 00:00:00 2001 From: Katie Knowles Date: Fri, 11 Oct 2024 11:28:07 -0400 Subject: [PATCH 8/8] Update v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go Co-authored-by: Christophe Tafani-Dereeper --- .../azure/persistence/create-bastion-shareable-link/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go index 9a82509cb..72b4f967d 100644 --- a/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go +++ b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go @@ -152,7 +152,7 @@ func revert(params map[string]string, providers stratus.CloudProviders) error { client, err := armnetwork.NewClientFactory(subscriptionID, cred, clientOptions) if err != nil { - log.Fatalf("failed to create client: %v", err) + return fmt.Errorf("failed to instantiate ARM Network client: %v", err) } // Delete shareable link that was previously created