Guidance for CVE Crypto and RDG vulnerability patching on Azure VM

There are a lot of buzz these days around the most recent Microsoft Tuesday Patch January 2020.  There are critical vulnerabilities found in the core Windows crypto functionality as well as Remote Desktop Gateway (RDG).  While the crypto related vulnerability (CVE-2020-0601) may let attacker to abuse the signing certificate, the RDG vulnerability (CVE-2020-0609 & 2020-0610) sounds very dangerous as it is classified in Remote Code Execution (RCE).

Patching these vulnerabilities is undeniably imperative. For those who have Windows virtual machines especially Windows Server hosting directory service or business productivity application may have some questions that need answers. This article would hopefully give you a bit of information about vulnerability patching in Azure – these vulnerabilities are brought as example.

I’d assume you have basic understanding on how to patch a Windows vulnerability with KB that Microsoft provides, as well as what to verify in a minimum level to ensure the patch is in place. Specific to CVE-2020-0601 you can check crypt32.dll. I have collected materials which are helpful:

There are really a lot more related helpful articles. The best source I’d recommend is the BlueTeam cheatsheet one which collects sources from other security researches and companies.

VM Patching Responsibility

I’ve seen around the community some questions related to responsibility to patch the vulnerabilities. Here are some questions that are really valid:

  • Do I have patch my Windows Virtual Desktop that uses Remote Desktop Gateway service?
  • If I deploy the last Windows Server image is the fix included?
  • Is Microsoft responsible for patching customer’s virtual machines?

Each of question may lead to an analysis. In a nutshell, if a vulnerability is an application level impact you are responsible for patching. Microsoft is only responsible for building, testing and releasing the patch.

Microsoft Cloud Shared Responsibility Model

Specific to Windows Virtual Desktop, the Remote Desktop Gateway service is managed by Microsoft – it has the responsibility to mitigate the vulnerability. However, it would be much better to reach out to Microsoft for the answer though.

The question that is related to Windows server image is fairly interesting. In a nutshell, if you deploy an image build 14393.3443 you shouldn’t worry about the vulnerability. Let’s examine to see if that is true.

VM Deployment for Testing

First, let’s see all available Windows Server images in Azure marketplace by running the following PowerShell script:

$location = "West Us"
$publisherName = "MicrosoftWindowsServer"
$offerName = "WindowsServer"
$skuName = "2016-Datacenter"

Get-AzVMImage -Location $location `
              -PublisherName $publisherName `
              -Offer $offerName `
              -Skus $skuName

There are two types of Windows Server image: non-BYOL (Bring Your Own License) and BYOL (Hybrid Use Benefit). You can pick a set of 3-5 images like below:

  • 14393.3443.2001090113
  • 14393.3384.1912042333
  • 14393.3326.1911120150

Our goal is to deploy 3 virtual machines and perform some verification steps to see if any of these images have vulnerability patches installed. These virtual machines are also added to an existing Log Analytics workspace – we will see why this is helpful for your patch monitoring. Use this template from here to quickly deploy virtual machines for testing purpose.

Here is the result of each image when checking crypt32.dll file

  • 14393.3326.1911120150
    • File version: 0.0.14393.3324
    • SHA256: 7EB6EC836EA826181D0B77EF4BB211ECFA68B9645C19B87D2841F11029C50C6F
  • 14393.3384.1912042333
    • File version: 10.0.14393.3324
    • SHA256: 7EB6EC836EA826181D0B77EF4BB211ECFA68B9645C19B87D2841F11029C50C6F
  • 14393.3443.2001090113
    • File version: 10.0.14393.3442
    • SHA256: 6AE927255B0576AF136DF57210A1BA64C42A504D50867F58B7A128B4FD26A77C

Based on the baseline, it looks like the only image 14393.3443.2001090113 gives the updated crypt32.dll file. If you run Get-Hotfix  you will notice that the KB 4534271 was installed on 01/09/2020 -I guess this is the date this image was released and got pushed to Azure marketplace.

Do not confuse with Windows Update Catalog that you may see a different release date – 01/13/2020.  Note that this is an image that Microsoft build versus an individual public patch released to wider audience.

Below is the sample PowerShell script you can use to verify on a Windows Server 2016. You could apply to other OS version by modifying some constants in the script.

$crypt32Path = "C:\Windows\System32\crypt32.dll"
$validHash = "6AE927255B0576AF136DF57210A1BA64C42A504D50867F58B7A128B4FD26A77C"
$validFileVesion = "10.0.14393.3442"
$osBuild = "14393.3443"
$osVersion = "1607"

# Variables for getting VM Info
$azImdsUri = "http://169.254.169.254/metadata/instance?api-version=2019-06-01"
$vmName = (Invoke-RestMethod -Headers @{"Metadata"="true"} -URI $azImdsUri -Method get).compute.name
$rgName = (Invoke-RestMethod -Headers @{"Metadata"="true"} -URI $azImdsUri -Method get).compute.resourceGroupName
$hostName = $env:COMPUTERNAME

$fileVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Windows\System32\crypt32.dll").FileVersionRaw.ToString()
Write-Host "File Version:" $fileVersion

$fileHash = (Get-FileHash -Path $crypt32Path).Hash
Write-Host "File Hash: " $fileHash

$currentBuild = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").CurrentBuildNumber
$currentUBR = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").UBR
Write-Host "Current OS Build: " ($currentBuild + "." + $currentUBR)

$currentOsVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").ReleaseID
Write-Host "Current OS Version: " $currentOsVersion


$result = $fileVersion -eq $validFileVesion -and `
          $fileHash -eq $validHash -and `
          $osBuild -eq ($currentBuild + "." + $currentUBR) -and `
          $currentOsVersion -eq $osVersion

if ($result -eq $true) {
    Write-Host -ForegroundColor Green "[-] Your virtual machine has applied the patch"
    Write-Host -ForegroundColor DarkYellow "`t [-] Virtual Machine name: " $vmName
    Write-Host -ForegroundColor DarkYellow "`t [-] Virtual Machine RG name: " $rgName
    Write-Host -ForegroundColor DarkYellow "`t [-] Host name: " $hostName
}
elseif ($result -ne $true) {
    Write-Host -ForegroundColor Yellow "[!] Please verify again!"
    Write-Host -ForegroundColor DarkYellow "`t [-] Virtual Machine name: " $vmName
    Write-Host -ForegroundColor DarkYellow "`t [-] Virtual Machine RG name: " $rgName
    Write-Host -ForegroundColor DarkYellow "`t [-] Host name: " $hostName
}

VM Audit with RunScript

RunScript is a feature in Azure that allows you to run a script remotely. Using this feature, you don’t have to RDP to the target virtual machine or enable PsRemoting. RunScript is not really recommended for advanced audit. It can be used for quick audit e.g checking patch installation, or namely crypt32 file verification.

Below is the sample script you can use to perform the CVE audit on multiple virtual machines (all Windows VMs in a specified resource group).

$rgName = "azsec-corporate-rg"
$id = "RunPowerShellScript"
$scriptPath = ".\verify-cve-2020-0601.ps1"

$vms = Get-AzVm -ResourceGroupName $rgName | Where-Object { $_.OSProfile.WindowsConfiguration -ne $null }
foreach ($vm in $vms) {
    Invoke-AzVMRunCommand -ResourceGroupName $rgName `
                          -Name $vm.Name `
                          -CommandId $id `
                          -ScriptPath $scriptPath
}

Once you execute Invoke-AzVmRunCommand , your script will be uploaded over Https to Azure Automation back-end script repository. There is a setting file that contains script content. The RunCommandExtension utility reads that setting file, extract the script content and write to a local file.  From here, the script is executed. The script output is in Message field, and it displays exactly what you have in Write-Host .

VM Audit with Azure Automation DSC

Azure Automation DSC is just another way to monitor and audit your virtual machine. It is designed for periodical advanced compliance audit (e.g CIS Compliance, Corporate hardening server configuration…). DSC works by pushing a compiled audit script to a master node. Each target virtual machine that you want to audit pulls the audit script locally and execute. The concept would be similar to RunScript desrcibed above. The purpose of use would be different. One is on-demand execution and the other one is for periodical audit execution – it is very helpful to track new vulnerable virtual machine. If you already have Azure Automation DSC in your organization you can utilize it to do the audit for not only existing virtual machine but also for new one.

Azure Automation DSC requires an extension named Microsoft.PowerShell.DSC. Below is the sample script to onboard your virtual machine to DSC node cluster:

$rgName = "azsec-corporate-rg"
$aaName = "Automate-3536378-EUS"
$nodeName = "Cve20200601Verification"
$vms = Get-AzVm -ResourceGroupName $rgName | Where-Object { $_.OSProfile.WindowsConfiguration -ne $null }
$aaKey = (Get-AzAutomationRegistrationInfo -ResourceGroupName $rgName `
                                           -AutomationAccountName $aaName).PrimaryKey
$aaRegUrl = (Get-AzAutomationRegistrationInfo -ResourceGroupName $rgName `
                                              -AutomationAccountName $aaName).Endpoint


$vmExtensionName = "Microsoft.Powershell.DSC"
$extensionPublisher = "Microsoft.Powershell"
$settings = @{
        "configurationArguments" = @{
        "RegistrationUrl" = $aaRegUrl; 
        "NodeConfigurationName" = "$nodeName.localhost"; 
        "ConfigurationMode" = "applyAndMonitor"; 
        "RebootNodeIfNeeded" = $false;
        "ActionAfterReboot" = "continueConfiguration";
        "AllowModuleOverwrite" = $false;
        "ConfigurationModeFrequencyMins" = "15";
        "RefreshFrequencyMins" = "30"
    }
}
$protectedSettings = @{
    "configurationArguments" = @{
        "RegistrationKey" = @{
            "Username" = "null"; 
            "Password" = $aaKey
        } 
    } 
}

foreach ($vm in $vms) {
    Set-AzVMExtension -VMName $vm.Name `
                      -Location "westus"`
                      -ResourceGroupName $rgName `
                      -Name $vmExtensionName `
                      -Publisher $extensionPublisher `
                      -Type "DSC" `
                      -TypeHandlerVersion "2.8" `
                      -Settings $settings `
                      -ProtectedSettings $protectedSettings


    Update-AzVM -ResourceGroupName $rgName -VM $vm
}

and the node configuration script (aka CVE audit script) would look like this:

configuration Cve20200601Verification {
    Import-DscResource -ModuleName PSDesiredStateConfiguration   
    Node 'localhost' {
        Script 'Cve20200601Verification' {
            GetScript  = {

            }
            TestScript = {
                $crypt32Path = "C:\Windows\System32\crypt32.dll"
                $validHash = "6AE927255B0576AF136DF57210A1BA64C42A504D50867F58B7A128B4FD26A77C"
                $validFileVesion = "10.0.14393.3442"
                $osBuild = "14393.3443"
                $osVersion = "1607"

                $fileVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Windows\System32\crypt32.dll").FileVersionRaw.ToString()
                Write-Host "File Version:" $fileVersion

                $fileHash = (Get-FileHash -Path $crypt32Path).Hash
                Write-Host "File Hash: " $fileHash

                $currentBuild = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").CurrentBuildNumber
                $currentUBR = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").UBR
                Write-Host "Current OS Build: " ($currentBuild + "." + $currentUBR)

                $currentOsVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").ReleaseID
                Write-Host "Current OS Version: " $currentOsVersion


                $result = $fileVersion -eq $validFileVesion -and `
                    $fileHash -eq $validHash -and `
                    $osBuild -eq ($currentBuild + "." + $currentUBR) -and `
                    $currentOsVersion -eq $osVersion

                return $result
            }
            SetScript  = {

            }
        }
    }
}

You can view the compliance dashboard and also status of the compiled configuration run on each virtual machine being added to the DSC Node cluster.

Here is the bonus for those who would like to add a virtual machine to an existing Azure Automation DSC cluster node from Azure ARM template deployment:

{
    "type": "Microsoft.Compute/virtualMachines/extensions",
    "apiVersion": "2019-03-01",
    "name": "[concat(variables('vmName'),'/Microsoft.Powershell.DSC')]",
    "location": "[resourceGroup().location]",
    "dependsOn": [
        "[resourceId('Microsoft.Compute/virtualMachines/', variables('vmName'))]",
        "[concat('Microsoft.Compute/virtualMachines/',variables('vmName'), '/extensions/LogAnalytics')]"
    ],
    "properties": {
        "publisher": "Microsoft.PowerShell",
        "type": "DSC",
        "typeHandlerVersion": "2.8",
        "autoUpgradeMinorVersion": true,
        "protectedSettings": {
            "Items": {
                "registrationKeyPrivate": "[listKeys(variables('aaResourceid'), '2018-06-30').Keys[0].value]"
            }
        },
        "settings": {
            "properties": [
                {
                    "name": "RegistrationKey",
                    "value": {
                        "UserName": "null",
                        "Password": "PrivateSettingsRef:registrationKeyPrivate"
                    },
                    "typename": "System.Management.Automation.PSCredential"
                },
                {
                    "Name": "RegistrationUrl",
                    "Value": "[reference(variables('aaResourceid'), '2018-06-30').registrationUrl]",
                    "TypeName": "System.String"
                },
                {
                    "Name": "NodeConfigurationName",
                    "Value": "[parameters('nodeConfigurationName')]",
                    "TypeName": "System.String"
                },
                {
                    "Name": "ConfigurationMode",
                    "Value": "applyAndMonitor",
                    "TypeName": "System.String"
                },
                {
                    "Name": "RebootNodeIfNeeded",
                    "Value": false,
                    "TypeName": "System.Boolean"
                },
                {
                    "Name": "ActionAfterReboot",
                    "Value": "continueConfiguration",
                    "TypeName": "System.String"
                },
                {
                    "Name": "AllowModuleOverwrite",
                    "Value": false,
                    "TypeName": "System.Boolean"
                },
                {
                    "Name": "ConfigurationModeFrequencyMins",
                    "Value": 15,
                    "TypeName": "System.Int32"
                },
                {
                    "Name": "RefreshFrequencyMins",
                    "Value": 30,
                    "TypeName": "System.Int32"
                }
            ]
        }
    }
}

The template can be found here.

Update & Patch Management

Update Management in Log Analytics is something I really want to introduce you. There is not a better place than here to grab information about it. Update Management works with Microsoft Monitoring agent (in Windows) and Linux agent (in Linux) to collect information about the host and send data to Azure back-end system. The agent sends data every 60 min and is followed the concept of Desired State Configuration (DSC) you may have known. In a nutshell, you need Azure Automation DSC to be in place to work with Update management feature.

Once you onboard your virtual machines to the Log Analytics workspace you can add them to be monitored by Update module in the agent.

In Missing updates tab you can see all the updates that a virtual machine is recommended to install. You can also filter classification (e.g. Critical Update, Security Update).

Not only Update Management works to monitor your missing critical and security update. The agent sends data of each host to Log Analytics workspace that help you write a query to check on each virtual machine. Below is the sample query to check missing security and critical update:

let duration = totimespan(1h);
let sourceComputerIds =
Heartbeat
| where TimeGenerated >=ago(duration) and
        OSType =~ "Windows" and
        notempty(Computer) and
        Solutions has "updates"
| summarize arg_max(TimeGenerated, Solutions) by SourceComputerId
| distinct SourceComputerId;
Update
| where UpdateState =~ "Needed" and
        Classification in ("Security Updates", "Critical Updates") and
        Optional == false and
        KBID == "4534271" and
        Product == "Windows Server 2016"
| where SourceComputerId in (sourceComputerIds)
| summarize arg_max(TimeGenerated, *) by Resource, ResourceGroup, Computer, Title, KBID

The query above filters all virtual machines that require to update KB4534271. You can remove UpdateState=~”Needed”  filter to get all virtual machines including ones that already installed the KB.

Update Management doesn’t only give the information about patch, it allows you to schedule to install missing security patch. This feature allows you to schedule patch installation on multiple virtual machines that have been connected to the Log Analytics workspace. You can configure KB inclusion or exclusion from this feature.

You can see the output as well as error if any during the patch deployment. Patch deployment was executed by the automation account that has privilege (by default Contributor).

Qualys Vulnerability Assessment

If your virtual machine is covered by Azure Security Center Standard pricing tier you are charged zero dollar for using Qualys Vulnerability Assessment. To enable Qualys, go to Azure Security Center Recommendation page and look for Enable the built-in vulnerability assessment solution on virtual machines (powered by Qualys). You are redirected to the page where you can enable Qualys Vulnerability Assessment on selected virtual machine. Qualys will deploy Qualys Cloud Security Agent on target virtual machine and then start collecting information. The agent reports back to Azure Security Center.

In the recommendation page look for Remediate vulnerabilities found on your virtual machines (powered by Qualys) you will get to what you expect.

Qualys reported Crypto API Vulnerability in ASC

Once you know affected virtual machines, it is time for patching.

RDG Vulnerability Mitigation

The KB4534271 includes everything in Tuesday Patch January 2020 for Windows Server 2016. It means when you install the patch both Crypto API and RDG vulnerabilities are patched. While no mitigation is provided for Crypto API beyond patch installation, For RDG vulnerability followed by this analysis you can use PoC verification script here.

You could also check your Network Security Group and ensure there is not any inbound rule that allows UDP port 3391 while you are in progress of patching. This is just to add additional prevention layer to ensure no one from Internet could exploit your internet-facing RDG service.

$nsgs = Get-AzNetworkSecurityGroup | Where-Object {$_.SecurityRules.DestinationPortRange -ccontains "3391" }
foreach ($nsg in $nsgs) {
    $rules = $nsg.SecurityRules | Where-Object {$_.DestinationPortRange -ccontains "3391" -AND `
                                                $_.Direction -eq "Inbound" -AND `
                                                $_.Access -eq "Allow"}
    Write-Host -ForegroundColor Yellow "[-] NSG Name: " $nsg.Name
    Write-Host -ForegroundColor DarkYellow "[-] RG Name: " $nsg.ResourceGroupName
    foreach ($rule in $rules) {
        $r = $rule | Where-Object {$_.DestinationPortRange -ccontains "3391" }
        Write-Host -ForegroundColor Green "`t [-] Rule Name: " $r.Name
        Write-Host -ForegroundColor Green "`t `t [-] Protocol: " $r.Protocol
        Write-Host -ForegroundColor Green "`t `t [-] Source Port Range: " $r.SourcePortRange
        Write-Host -ForegroundColor Red "`t `t [-] Destination Port Range: " $r.DestinationPortRange
        Write-Host -ForegroundColor Red "`t `t [-] Direction: " $r.Direction
        Write-Host -ForegroundColor Red "`t `t [-] Access : " $r.Access
    }
}

Conclusion

There are enterprise software management solution as well as vulnerability assessment products that definitely help in this case. You may use PsRemote to do the audit, or have a tool like Ansible or PowerShell DSC that could help simplify the automation.  This article simply shows you a few built-in ways to audit as well as to patch CVE on your virtual machines.

This article only gives you analysis on Windows Server 2016 as it is the most common OS today. The concept would still be similar for Windows Server 2012 R2 or Windows Server 2019 from audit and patching perspective. You would need to know the OS version and corresponding KB. Every KB Microsoft also provides list of file changes to let people check file version. Specific to Windows Crypto API the file named crypt32.dll is affected.

The article assumes you have fundamental knowledge of Azure infrastructure and management service as it provides shortcuts. Below are some articles that you should check too:

All scripts used in this article can be found here.

This entry was posted in Security Automation and tagged , , . Bookmark the permalink.

11 Responses to Guidance for CVE Crypto and RDG vulnerability patching on Azure VM

  1. Ying Chong says:

    AzSec saves my day. I have like 300 virtual machines running on Azure and couldn’t know what to do. Now I now that I can use Update Management or DSC. I could not thank enough for this article. It is possible to change DSC cluster node anytime?

    • azsec says:

      Hi Ying Chong,

      Yes you can update DSC configuration on virtual machine anytime. You simply need to remove the existing PowerShell DSC extension and onboard it into another Azure Automation DSC Node cluster.

      Glad this article helps!

  2. alan-msft says:

    Just another great article. Thank you for your time doing analysis and writing up.

    Alan – MSFT PFE

  3. It is a very helpful article. Thanks for your sharing.

    It looks like we need Microsoft Monitoring Agent to be stable in order to use along with Update Management solution in Log Analytics. As far as I know Azure Security Center also deploys its codebase into the agent for security event data collection.

    • azsec says:

      That is correct. The core Azure management service heavily relies on Microsoft Monitoring Agent on Windows. I will uncover what Microsoft Monitoring Agent does with Azure Security Center and Update Management soon in the future. Stay tuned and subscribe the blog.

      Thank you.

  4. abilla says:

    Great article! I like the part of using Azure Automation DSC to onboard to DSC node to verify CVE. Could you please share ARM template for DSC extension deployment if any?

    Thank you very much!

  5. mohamed.dham says:

    What an incredibly helpful article! Thank you a lot for your effort. Your guidance has helped me successfully audit nearly 100 virtual machines in my production environment.

  6. chingy says:

    The guidance saves my life. Thanks AzSec a lot!

  7. Pingback: Query Azure Security Center Recommendation by different ways

  8. Pingback: Guidance for CVE-2020-0796 SMBv3 Compression vulnerability patching on Azure VM

Leave a Reply