Date: 28/10/2025

Difficulty: Very Easy

Category: DFIR

Description: Phantom Check is a Sherlock challenge showcasing some of the common virtualization detection techniques used by attackers. Players will gain the ability to create detection rules by identifying specific WMI queries, comparing processes for virtual machine detection, and analyzing registry keys or file paths associated with virtual environments.


1. Executive Summary

This report details the forensic investigation into a [System/Service, e.g., Confluence Server] compromise via a [Attack Type, e.g., Brute Force Attack] against its SSH service. The investigation utilised the provided Unix auth.log and wtmp artifacts. Analysis confirmed a successful breach, post-exploitation activities including privilege escalation and persistence via a new user account, and the subsequent download of an external script. The threat actor’s initial access was gained using the root account.

2. Artefacts

ArtefactDescription
Microsoft-Windows-Powershell.evtxA Windows event log file that stores PowerShell events.
Windows-Powershell-Operational.evtxA Windows event log file that stores a more broad range of events.

3. Questions

3.1. Which WMI class did the attacker use to retrieve model and manufacturer information for virtualization detection?

After importing the saved logs, we can easily use the ‘Find’ function to search for keywords. In this case, we know that the powershell script would use the Get-WmiObject cmdlet to get an instance of a WMI (Windows Management Instrumentation) class.

WMI Classes

The WMI system classes are a collection of predefined classes based on the Common Information Model (CIM). Unlike classes supplied by providers, the system classes are not declared in a Managed Object Format (MOF) file. WMI creates a set of these classes whenever a new WMI namespace is created.

Source

After moving through a few of the returned results we see:

CommandInvocation(Get-WmiObject): "Get-WmiObject"
ParameterBinding(Get-WmiObject): name="Class"; value="Win32_ComputerSystem"
CommandInvocation(Select-Object): "Select-Object"
ParameterBinding(Select-Object): name="ExpandProperty"; value="Model"
ParameterBinding(Select-Object): name="InputObject"; value="\\DESKTOP-M3AKJSD\root\cimv2:Win32_ComputerSystem.Name="DESKTOP-M3AKJSD""

The Win32_ComputerSystem WMI class represents a computer system running Windows.

Source

This class returns key properties of a device running Windows and is used for retrieving general, high-level information about a machine. The key to using Win32_ComputerSystem for virtualisation detection lies in examining properties that hypervisors must expose, this often reveals the virualisation vendors name.

For example, when the ‘model’ or ‘manufacturer’ values are returned from this class, the manufacturer might be set to a value such as ‘VMWare’ or ‘Oracle’ and the model might include words such as ‘Virtual’.

3.2. Which WMI query did the attacker execute to retrieve the current temperature value of the machine?

While searching through events within the Windows-Powershell-Operational.evtx file for Get-WmiObject we likely saw another event with the below details:

CommandInvocation(Get-WmiObject): "Get-WmiObject"
ParameterBinding(Get-WmiObject): name="Query"; value="SELECT * FROM MSAcpi_ThermalZoneTemperature"
ParameterBinding(Get-WmiObject): name="ErrorAction"; value="SilentlyContinue"
NonTerminatingError(Get-WmiObject): "Invalid class "MSAcpi_ThermalZoneTemperature""

The MSAcpi_ThermalZoneTemperature WMI class is designed to retrieve temperature readings from thermal sensors on the physical machine. The key property it exposes is CurrentTemperature. When this query is made on a virtual machine an error is returned. The result of this query is another technique for identifying virtualisation.

3.3. The attacker loaded a PowerShell script to detect virtualization. What is the function name of the script?

We can use the ‘Filter Current Log’ feature to search the Windows-Powershell-Operational.evtx logs for a specific event. In this case we will filter the logs to only show IDs that match 4104.

4104 Script Block Logging

This is the most crucial for script execution. When Script Block Logging is enabled (available in PowerShell 5.0 and later), this event ID records the content of all script blocks, functions, and scripts that are processed, whether interactive or through a script file.

Looking through the returned results, the ‘Verbose’ level events stand out, so we start there. Running through these events the below function can be found:

function Check-VM
{

<# 
.SYNOPSIS 
Nishang script which detects whether it is in a known virtual machine.
 
.DESCRIPTION 
This script uses known parameters or 'fingerprints' of Hyper-V, VMWare, Virtual PC, Virtual Box,
Xen and QEMU for detecting the environment.

.EXAMPLE 
PS > Check-VM
 
.LINK 
http://www.labofapenetrationtester.com/2013/01/quick-post-check-if-your-payload-is.html
https://github.com/samratashok/nishang

.NOTES 
The script draws heavily from checkvm.rb post module from msf.
https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/checkvm.rb
#> 
    [CmdletBinding()] Param()
    $ErrorActionPreference = "SilentlyContinue"
    #Hyper-V
    $hyperv = Get-ChildItem HKLM:\SOFTWARE\Microsoft
    if (($hyperv -match "Hyper-V") -or ($hyperv -match "VirtualMachine"))
        {
            $hypervm = $true
        }

    if (!$hypervm)
        {
            $hyperv = Get-ItemProperty hklm:\HARDWARE\DESCRIPTION\System -Name SystemBiosVersion
            if ($hyperv -match "vrtual")
                {
                    $hypervm = $true
                }
        }
    
    if (!$hypervm)
        {
            $hyperv = Get-ChildItem HKLM:\HARDWARE\ACPI\FADT
            if ($hyperv -match "vrtual")
                {
                    $hypervm = $true
                }
        }
            
    if (!$hypervm)
        {
            $hyperv = Get-ChildItem HKLM:\HARDWARE\ACPI\RSDT
            if ($hyperv -match "vrtual")
                {
                    $hypervm = $true
                }
        }

    if (!$hypervm)
        {
            $hyperv = Get-ChildItem HKLM:\SYSTEM\ControlSet001\Services
            if (($hyperv -match "vmicheartbeat") -or ($hyperv -match "vmicvss") -or ($hyperv -match "vmicshutdown") -or ($hyperv -match "vmiexchange"))
                {
                    $hypervm = $true
                }
        }
   
    if ($hypervm)
        {
    
             "This is a Hyper-V machine."
    
        }

    #VMWARE

    $vmware = Get-ChildItem HKLM:\SYSTEM\ControlSet001\Services
    if (($vmware -match "vmdebug") -or ($vmware -match "vmmouse") -or ($vmware -match "VMTools") -or ($vmware -match "VMMEMCTL"))
        {
            $vmwarevm = $true
        }

    if (!$vmwarevm)
        {
            $vmware = Get-ItemProperty hklm:\HARDWARE\DESCRIPTION\System\BIOS -Name SystemManufacturer
            if ($vmware -match "vmware")
                {
                    $vmwarevm = $true
                }
        }
    
    if (!$vmwarevm)
        {
            $vmware = Get-Childitem hklm:\hardware\devicemap\scsi -recurse | gp -Name identifier
            if ($vmware -match "vmware")
                {
                    $vmwarevm = $true
                }
        }

    if (!$vmwarevm)
        {
            $vmware = Get-Process
            if (($vmware -eq "vmwareuser.exe") -or ($vmware -match "vmwaretray.exe"))
                {
                    $vmwarevm = $true
                }
        }

    if ($vmwarevm)
        {
    
             "This is a VMWare machine."
    
        }
    
    #Virtual PC

    $vpc = Get-Process
    if (($vpc -eq "vmusrvc.exe") -or ($vpc -match "vmsrvc.exe"))
        {
        $vpcvm = $true
        }

    if (!$vpcvm)
        {
            $vpc = Get-Process
            if (($vpc -eq "vmwareuser.exe") -or ($vpc -match "vmwaretray.exe"))
                {
                    $vpcvm = $true
                }
        }

    if (!$vpcvm)
        {
            $vpc = Get-ChildItem HKLM:\SYSTEM\ControlSet001\Services
            if (($vpc -match "vpc-s3") -or ($vpc -match "vpcuhub") -or ($vpc -match "msvmmouf"))
                {
                    $vpcvm = $true
                }
        }

    if ($vpcvm)
        {
    
         "This is a Virtual PC."
    
        }


    #Virtual Box

    $vb = Get-Process
    if (($vb -eq "vboxservice.exe") -or ($vb -match "vboxtray.exe"))
        {
    
        $vbvm = $true
    
        }
    if (!$vbvm)
        {
            $vb = Get-ChildItem HKLM:\HARDWARE\ACPI\FADT
            if ($vb -match "vbox_")
                {
                    $vbvm = $true
                }
        }

    if (!$vbvm)
        {
            $vb = Get-ChildItem HKLM:\HARDWARE\ACPI\RSDT
            if ($vb -match "vbox_")
                {
                    $vbvm = $true
                }
        }

    
    if (!$vbvm)
        {
            $vb = Get-Childitem hklm:\hardware\devicemap\scsi -recurse | gp -Name identifier
            if ($vb -match "vbox")
                {
                    $vbvm = $true
                }
        }



    if (!$vbvm)
        {
            $vb = Get-ItemProperty hklm:\HARDWARE\DESCRIPTION\System -Name SystemBiosVersion
            if ($vb -match "vbox")
                {
                     $vbvm = $true
                }
        }
  

    if (!$vbvm)
        {
            $vb = Get-ChildItem HKLM:\SYSTEM\ControlSet001\Services
            if (($vb -match "VBoxMouse") -or ($vb -match "VBoxGuest") -or ($vb -match "VBoxService") -or ($vb -match "VBoxSF"))
                {
                    $vbvm = $true
                }
        }

    if ($vbvm)
        {
    
         "This is a Virtual Box."
    
        }



    #Xen

    $xen = Get-Process

    if ($xen -eq "xenservice.exe")
        {
    
        $xenvm = $true
    
        }
    
    if (!$xenvm)
        {
            $xen = Get-ChildItem HKLM:\HARDWARE\ACPI\FADT
            if ($xen -match "xen")
                {
                    $xenvm = $true
                }
        }

    if (!$xenvm)
        {
            $xen = Get-ChildItem HKLM:\HARDWARE\ACPI\DSDT
            if ($xen -match "xen")
                {
                    $xenvm = $true
                }
        }
    
    if (!$xenvm)
        {
            $xen = Get-ChildItem HKLM:\HARDWARE\ACPI\RSDT
            if ($xen -match "xen")
                {
                    $xenvm = $true
                }
        }

    
    if (!$xenvm)
        {
           $xen = Get-ChildItem HKLM:\SYSTEM\ControlSet001\Services
            if (($xen -match "xenevtchn") -or ($xen -match "xennet") -or ($xen -match "xennet6") -or ($xen -match "xensvc") -or ($xen -match "xenvdb"))
                {
                    $xenvm = $true
                }
        }


    if ($xenvm)
        {
    
         "This is a Xen Machine."
    
        }


    #QEMU

    $qemu = Get-Childitem hklm:\hardware\devicemap\scsi -recurse | gp -Name identifier
    if ($qemu -match "qemu")
        {
    
            $qemuvm = $true
    
        }
    
    if (!$qemuvm)
        {
        $qemu = Get-ItemProperty hklm:HARDWARE\DESCRIPTION\System\CentralProcessor\0 -Name ProcessorNameString
        if ($qemu -match "qemu")
            {
                $qemuvm = $true
            }
        }    

    if ($qemuvm)
        {
    
         "This is a Qemu machine."
    
        }
}

This is clearly the function used to detect virtualisation.

3.4. Which registry key did the above script query to retrieve service details for virtualization detection?

We can see from the code that HKLM is being used to query the required registry keys. HKLM stands for ‘HKEY_LOCAL_MACHINE’ and represents a Registry Hive that contains configuration settings specific to the local computer.

By searching for this string within the found script we can find 20 different lines of code using the Get-ItemProperty and Get-ChildItem commands to query registry keys. looking through this list the below stands out the most:

$hyperv = Get-ChildItem HKLM:\SYSTEM\ControlSet001\Services
if (($hyperv -match "vmicheartbeat") -or ($hyperv -match "vmicvss") -or ($hyperv -match "vmicshutdown") -or ($hyperv -match "vmiexchange"))
{
    $hypervm = $true
}
HKLM:\SYSTEM\ControlSet001\Services

This registry path can be used for virtualisation detection by serving as the primary location where an operating system registers device drivers and services, including those installed by virtualisation software.

$hyperv -match "vmicheartbeat"

The script uses the -match comparrison operator to hunt for any services that match: ‘vmicheartbeat’, ‘vmicvss’, ‘vmicshutdown’ or ‘vmiexchange’. If a match is found the IF statement will pass as true.

3.5. The VM detection script can also identify VirtualBox. Which processes is it comparing to determine if the system is running VirtualBox?

In this scenario the threat actor, very usefully, labelled each section of code by the virtualisation vendor. After searching for ‘Box’ we can quickly find the section of code we need to analyse. This section starts with the below code:

$vb = Get-Process
if (($vb -eq "vboxservice.exe") -or ($vb -match "vboxtray.exe"))
{
    $vbvm = $true
}

The IF statement within this code first checks if any process names exactly equals (-eq) vboxservice.exe, this process is a core component of the VirtualBox Guest Additions. The second check uses the -match operator to see if any process name in the collection contains a match for vboxtray.exe, which is the user-facing tray application for Guest Additions.

3.6. The VM detection script prints any detection with the prefix ‘This is a’. Which two virtualization platforms did the script detect?

This information is easily found by returning to Event Viewer and using the Find function to search for “This is a " within the Windows-Powershell-Operational.evtx logs.

This immediatly returns the below event log:

CommandInvocation(Out-Default): "Out-Default"
ParameterBinding(Out-Default): name="InputObject"; value="This is a Hyper-V machine."
ParameterBinding(Out-Default): name="InputObject"; value="This is a VMWare machine."

Bonus

By running a search function against the same Get-WmiObject cmdlet we can find the latest instance of a WMI class being called that we suspect is malicious. The first result that is returned:

Pipeline execution details for command line: Get-WmiObject -Query "SELECT * FROM MSAcpi_ThermalZoneTemperature" -ErrorAction SilentlyContinue. 

Context Information: 
	DetailSequence=1
	DetailTotal=1

	SequenceNumber=53

	UserId=DESKTOP-M3AKJSD\User
	HostName=ConsoleHost
	HostVersion=5.1.26100.2161
	HostId=0fad0cf8-6cb6-4657-86f7-655ec22eed9f
	HostApplication=C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
	EngineVersion=5.1.26100.2161
	RunspaceId=2aeeba59-d0f6-4ce7-b41c-e07625b3beec
	PipelineId=21
	ScriptName=
	CommandLine=Get-WmiObject -Query "SELECT * FROM MSAcpi_ThermalZoneTemperature" -ErrorAction SilentlyContinue 

Details: 
CommandInvocation(Get-WmiObject): "Get-WmiObject"
ParameterBinding(Get-WmiObject): name="Query"; value="SELECT * FROM MSAcpi_ThermalZoneTemperature"
ParameterBinding(Get-WmiObject): name="ErrorAction"; value="SilentlyContinue"
NonTerminatingError(Get-WmiObject): "Invalid class "MSAcpi_ThermalZoneTemperature""

From the ‘Context Information’ we can see that this command was ran with the below RunspaceId:

2aeeba59-d0f6-4ce7-b41c-e07625b3beec

RunspaceId

The RunspaceId is a Globally Unique Identifier (GUID) that PowerShell assigns to a specific runspace - the environment - where commands are being processed.

We can use this identifier to find the original script execition. This is because when the script was executed PowerShell assigned it this RunspaceId, all events then originating from this script will be assigned the same ID.

We can also use this technique to find all of the PowerShell commands ran from this script.