VMware CVE-2020-4004 workaround using PowerCLI

This post is about a recent vulnerability published by VMware, allowing hackers to run code on the virtual machines host system. The following PowerCLI functions will help you to set up the workaround faster.

Official statement of VMware: https://www.vmware.com/security/advisories/VMSA-2020-0026.html

Description by VMware

VMware ESXi, Workstation, and Fusion contain a use-after-free vulnerability in the XHCI USB controller. VMware has evaluated the severity of this issue to be in the Critical severity range with a maximum CVSSv3 base score of 9.3.

Known Attack Vectors by VMware

A malicious actor with local administrative privileges on a virtual machine may exploit this issue to execute code as the virtual machine’s VMX process running on the host.

Workaround

The suggested workaround for this issue is to remove the XHCI controllers from your virtual machines. But if you are working in a big environment you need to apply this workaround to a couple of hundred machines. Add the following two functions to your PowerShell/PowerCLI script to get the adapters and in the next step to remove the adapters. Please note this is not a full script, it is a function that does not exist in PowerCLI 6.5. Since I do not want to be the one responsible for crashing your environment, I decided to share the functions instead of a full script. Remove all the USB-Devices first!

This is inspired by a script that appeared in 2013 in the VMware community, sadly I don’t remember who wrote it. (If you know please send me a message)

Edit: 23.11.2020 – Changed the functions for better output and added a function to add the controlers again, after the patching is done.

Function Remove-USB3Controller {
    Param (
    [Parameter(Mandatory=$True,ValueFromPipelinebyPropertyName=$True)]
    $VM
    )
    Process {
        $VMConf = New-Object VMware.Vim.VirtualMachineConfigSpec
        $VMConf.deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
        $VMConf.deviceChange[0] = New-Object VMware.Vim.VirtualDeviceConfigSpec
        $VMConf.deviceChange[0].operation = “remove”
        $Device = $VM.ExtensionData.Config.Hardware.Device | ForEach-Object {
            $_ | Where-Object {$_.gettype().Name -like "*xHCI*"}
        }
        $VMConf.deviceChange[0].device = $Device
        $VM.ExtensionData.ReconfigVM_Task($VMConf)
    }
}

Function Add-USB3Controller {
    Param (
    [Parameter(Mandatory=$True,ValueFromPipelinebyPropertyName=$True)]
    $VM
    )
    Process {
        $VMConf = New-Object VMware.Vim.VirtualMachineConfigSpec
        $VMConf.deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
        $VMConf.deviceChange[0] = New-Object VMware.Vim.VirtualDeviceConfigSpec
        $VMConf.deviceChange[0].operation = “add”
        $VMConf.DeviceChange[0].Device = New-Object VMware.Vim.VirtualUSBXHCIController
        $VM.ExtensionData.ReconfigVM_Task($VMConf)
    }
}

Function Get-USB3Controller {
    Param (
    [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
    $VM
    )
    Process {
        Foreach ($VMachine in $VM) {
            Foreach ($Device in $VMachine.ExtensionData.Config.Hardware.Device) {
                If ($Device.gettype().Name -like "*xHCI*"){
                    $Details = New-Object PsObject
                    $Details | Add-Member Noteproperty VM -Value $VMachine
                    $Details | Add-Member Noteproperty USB3Controller -Value True
                    return $Details
                }
            }
        }
        $Details = New-Object PsObject
        $Details | Add-Member Noteproperty VM -Value $VMachine
        $Details | Add-Member Noteproperty USB3Controller -Value False
        return $Details
                
    }
}

To test this use the following lines:

$TestVM = Get-VM -Name "TestVM"
$TestVM | Get-USB3Controller
Remove-USB3Controller -VM $TestVM
$TestVM | Add-USB3Controller

Yes, you can run this against your whole environment, but please make sure you remove all the USB Devices first. This can be done by using the Remove-USB device cmdlet or by removing the devices manually.

9 thoughts on “VMware CVE-2020-4004 workaround using PowerCLI

Add yours

  1. Having trouble with only the Remove command.
    Remove-USB3Controller : The input object cannot be bound to any parameters for the command either because the command does
    not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.

    The other commands work fine. But even taking only the cmd to assign the name to the TestVM and $testvm | remove-usb3controller fails with the exact same error. And if i write-host $testvm that shows correctly as well.

    Any thoughts?

  2. (I realize you’ve probably gone to bed etc…don’t feel like you need to respond right now at all heh)

    This is what i currently see on your page:

    Function Remove-USB3Controller {
    Param (
    [Parameter(Mandatory=$True,ValueFromPipelinebyPropertyName=$True)]
    $VM
    )
    Process {
    $VMConf = New-Object VMware.Vim.VirtualMachineConfigSpec
    $VMConf.deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $VMConf.deviceChange[0] = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $VMConf.deviceChange[0].operation = “remove”
    $Device = $VM.ExtensionData.Config.Hardware.Device | ForEach-Object {
    $_ | Where-Object {$_.gettype().Name -like “*xHCI*”}
    }
    $VMConf.deviceChange[0].device = $Device
    $VM.ExtensionData.ReconfigVM_Task($VMConf)
    }
    }

    I am now running this command to call the Remove function:
    Remove-USB3Controller -VM $TestVM

    That said, this is my code i’m using to test with etc:

    $ListofVMs = import-csv .\testvms.csv
    foreach ($Named in $ListofVMs)
    {
    #write-host $Named.VMs
    $vmName = Get-VM -Name $Named.VMs
    $vmName | Get-USB3Controller | export-csv TestExport.csv
    $ListofTruths = import-csv .\TestExport.csv
    #write-host $ListofTruths.VMs, $ListofTruths.USB3Controller
    If ($ListofTruths.USB3Controller -eq $True)
    {
    write-host $vmname “<—- Step Before Remove-Usb"
    Remove-USB3Controller -VM $TestVM | export-csv RemovalLog.csv #-Append
    write-host $vmname "<—- Step after Remove-USB"
    #$Export-csv $vmName.VMs Logfile.csv
    write-host $vmname "<—- Step before Get-USB"
    $vmName | Get-USB3Controller #| export-csv PostRemovalLog.csv #-append
    write-host $vmname "<—- Step after Get-USB"
    }
    }

    I have 3 test VMs, 2 with a USB controller and 1 without. Those are named in the testvms.csv.

    TestVM2 and TestVM3 both give this error (they both have the USB controller but no usb devices, it skips TestVM1 based on the If statement. I also tested this with just the Remove cmd from a separate ps1 file)

    When i run it, this is the error that comes up from the Remove command:
    Exception calling "ReconfigVM_Task" with "1" argument(s): "
    Required property device is missing from data object of type VirtualDeviceConfigSpec
    while parsing serialized DataObject of type vim.vm.device.VirtualDeviceSpec
    at line 1, column 258
    while parsing property "deviceChange" of static type ArrayOfVirtualDeviceConfigSpec
    while parsing serialized DataObject of type vim.vm.ConfigSpec
    at line 1, column 252
    while parsing call information for method ReconfigVM_Task
    at line 1, column 171
    while parsing SOAP body
    at line 1, column 64
    while parsing SOAP envelope
    at line 1, column 0
    while parsing HTTP request for method reconfigure
    on object of type vim.VirtualMachine
    at line 1, column 0"
    At C:\Users\*****\Desktop\USBFunctions\Remove-USB3Controller.ps1:15 char:9
    + $VM.ExtensionData.ReconfigVM_Task($VMConf)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : VimException

  3. $TestVM yep thats a fail on my part. Fixed. As far as USB 3 (xHCI) vs USB 3…There are a lot of vm’s, i’m not certain if all the controllers are xhci or not. Prior test which was just the Remove was against testvm1 which had no usb controller. Oddly…After running remove against Testvm’s 02, 03…they still show as True for USB3 Controller…but no longer give the error now that i’ve fixed the vm object to be the correct one. However re-running the program worked fine…The If statement caught that it was now false and didn’t run the script against Testvm’s 02, 03.

    So i still have some work to do on the script, but the commands/functions all are working properly now i believe. Thanks for all the work/help!

    1. Did you refresh the object holding the VM data? If you load $VM = Get-VM -Name “Blabla” the object stays the same after Remove-USB3Controller. It will not refresh unless you do Get-VM again.

      1. I had not done that. I ended up adding it, and it was still showing True for Falses when run right afterwards…but, i added a 15 second delay and i was good to go (and did that in a separate For Loop, where i re-ran Get-VM). Everything looks good at this point. I had some worry that the way i was calling the remove function that it may have been running all of the vm’s in the file rather than just the specific vm, but it seems ok based on the logs.

Leave a Reply

Powered by WordPress.com.

Up ↑

%d bloggers like this: