diff --git a/docs/attack-techniques/azure/azure.create-bastion-shareable-link.md b/docs/attack-techniques/azure/azure.create-bastion-shareable-link.md new file mode 100644 index 00000000..96b8ec77 --- /dev/null +++ b/docs/attack-techniques/azure/azure.create-bastion-shareable-link.md @@ -0,0 +1,84 @@ +--- +title: Access Virtual Machine using Bastion shareable link +--- + +# Access Virtual Machine using Bastion shareable link + + slow + + +Platform: Azure + +## MITRE ATT&CK Tactics + + +- Persistence + +## 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. +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/ + +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.persistence.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/docs/attack-techniques/azure/azure.persistence.create-bastion-shareable-link.md b/docs/attack-techniques/azure/azure.persistence.create-bastion-shareable-link.md new file mode 100755 index 00000000..81af9747 --- /dev/null +++ b/docs/attack-techniques/azure/azure.persistence.create-bastion-shareable-link.md @@ -0,0 +1,89 @@ +--- +title: Create Azure VM Bastion shareable link +--- + +# Create Azure VM Bastion shareable link + + slow + + +Platform: Azure + +## MITRE ATT&CK Tactics + + +- Persistence + +## 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.persistence.create-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]" + }, +} +``` + + diff --git a/docs/attack-techniques/azure/index.md b/docs/attack-techniques/azure/index.md index f5b5789a..bcf637e4 100755 --- a/docs/attack-techniques/azure/index.md +++ b/docs/attack-techniques/azure/index.md @@ -15,3 +15,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Export Disk Through SAS URL](./azure.exfiltration.disk-export.md) + +## Persistence + +- [Create Azure VM Bastion shareable link](./azure.persistence.create-bastion-shareable-link.md) + diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 91cc55b1..be75c57b 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -52,6 +52,7 @@ This page contains the list of all Stratus Attack Techniques. | [Execute Command on Virtual Machine using Custom Script Extension](./azure/azure.execution.vm-custom-script-extension.md) | [Azure](./azure/index.md) | Execution | | [Execute Commands on Virtual Machine using Run Command](./azure/azure.execution.vm-run-command.md) | [Azure](./azure/index.md) | Execution | | [Export Disk Through SAS URL](./azure/azure.exfiltration.disk-export.md) | [Azure](./azure/index.md) | Exfiltration | +| [Create Azure VM Bastion shareable link](./azure/azure.persistence.create-bastion-shareable-link.md) | [Azure](./azure/index.md) | Persistence | | [Create Admin EKS Access Entry](./EKS/eks.lateral-movement.create-access-entry.md) | [EKS](./EKS/index.md) | Lateral Movement | | [Backdoor aws-auth EKS ConfigMap](./EKS/eks.persistence.backdoor-aws-auth-configmap.md) | [EKS](./EKS/index.md) | Persistence, Privilege Escalation | | [Backdoor Entra ID application through service principal](./entra-id/entra-id.persistence.backdoor-application-sp.md) | [Entra ID](./entra-id/index.md) | Persistence, Privilege Escalation | diff --git a/docs/index.yaml b/docs/index.yaml index dc3d7c6b..e87c594a 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -472,6 +472,14 @@ Azure: - Exfiltration platform: Azure isIdempotent: true + Persistence: + - id: azure.persistence.create-bastion-shareable-link + name: Create Azure VM Bastion shareable link + isSlow: true + mitreAttackTactics: + - Persistence + platform: Azure + isIdempotent: false Entra ID: Persistence: - id: entra-id.persistence.backdoor-application-sp diff --git a/v2/go.mod b/v2/go.mod index 464ea7f4..2ec5cd7c 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -9,7 +9,8 @@ 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/network/armnetwork/v6 v6.1.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 github.com/aws/aws-sdk-go-v2 v1.31.0 github.com/aws/aws-sdk-go-v2/config v1.25.11 github.com/aws/aws-sdk-go-v2/credentials v1.16.9 diff --git a/v2/go.sum b/v2/go.sum index 686151ff..35c5f8ac 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -13,10 +13,18 @@ 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/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= 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 v1.0.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= -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/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.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/persistence/create-bastion-shareable-link/main.go b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go new file mode 100644 index 00000000..94b07ac7 --- /dev/null +++ b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.go @@ -0,0 +1,201 @@ +package azure + +import ( + "context" + _ "embed" + "fmt" + "encoding/json" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "log" +) + +//go:embed main.tf +var tf []byte + +func init() { + const codeBlock = "```" + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "azure.persistence.create-bastion-shareable-link", + FriendlyName: "Create Azure VM 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.Persistence}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + Revert: revert, + }) +} + +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"] + adminUsername := params["admin_username"] + // String requires extra quotations for unmarshaling, see below for more on this + adminPassword := fmt.Sprintf(`"%s"`, params["admin_password"]) + + 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 { + return fmt.Errorf("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: &vmId}}}, + }, nil) + if err != nil { + return fmt.Errorf("failed to create shareable link: %v", err) + } + + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return fmt.Errorf("failed to poll results of shareable link request: %v", err) + } + log.Println("Shareable link created") + + // Get Bastion shareable link + // Reference method: https://learn.microsoft.com/en-us/rest/api/virtualnetwork/get-bastion-shareable-link/get-bastion-shareable-link + // No error is returned by this method + pager := client.NewManagementClient().NewGetBastionShareableLinkPager(resourceGroup, bastionName, armnetwork.BastionShareableLinkListRequest{ + VMs: []*armnetwork.BastionShareableLink{ + { + VM: &armnetwork.VM{ + ID: &vmId, + }, + }, + }, + }, nil) + + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return fmt.Errorf("failed to get results page: %v", err) + } + for _, result := range page.Value { + log.Println("Bastion shareable link URL: " + *result.Bsl) + } + } + + log.Println("Bastion username: " + adminUsername) + + // Password needs to be unmarshaled, as the resulting string from Terraform has json.HTMLEscape applied. Unmarshal is the correct operation, but string needs to be correctly formatted to work (see above). + var adminPasswordDecoded string + err = json.Unmarshal([]byte(adminPassword), &adminPasswordDecoded) + if err != nil{ + return fmt.Errorf("failed to unmarshal password string: %v", err) + } + log.Println("Bastion password: " + adminPasswordDecoded) + + 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 { + return fmt.Errorf("failed to instantiate ARM Network 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: &vmId}}, + }}, nil) + if err != nil { + return fmt.Errorf("failed to delete shareable bastion link: %v", err) + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + return fmt.Errorf("failed to poll results of deleting shareable bastion link: %v", err) + } + + log.Println("Shareable link deleted") + + return nil +} diff --git a/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.tf b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.tf new file mode 100644 index 00000000..a1bb6076 --- /dev/null +++ b/v2/internal/attacktechniques/azure/persistence/create-bastion-shareable-link/main.tf @@ -0,0 +1,180 @@ +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 "admin_username" { + value = azurerm_windows_virtual_machine.lab_windows_vm.admin_username +} + +output "admin_password" { + sensitive = true + value = random_password.password.result +} + +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 29cd3996..f2b1b98c 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -43,6 +43,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/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"