2025-09-26
2435 words
12 minutes
Automation of VHDX Investigations

Introduction#

In large-scale environments using Virtual Desktop Infrastructure (VDI) platforms like Citrix, incident responders can find it challenging to identify the initial point of compromise. While some setups use non-persistent sessions with golden image restoration, many environments persist user data on remote file servers. Instead of storing profiles as standard folders, it is common to find them stored within Virtual Hard Disk (VHDX) files.
These VHDX files often contain valuable forensic artefacts such as NTUSER.DAT hives and application execution traces. Investigating them manually, however, becomes inefficient at scale, especially when dealing with thousands of user profiles.
This blog post introduces a method for automating forensic analysis of VHDX-based user profiles using Velociraptor, our preferred DFIR tool. The goal is to scale investigations efficiently and reliably without compromising forensic integrity.

VHDX#

Virtual Hard Disk v2 (VHDX) is a file format representing a virtual hard disk drive, containing its own partition layout and file system. It is commonly used in VDI environments to store individual user data, such as the roaming profile and the NTUSER.DAT registry hive.
From a forensic perspective, VHDX files can hold high-value artefacts, such as evidence of application execution via UserAssist and persistence mechanisms through registry run keys, both typically found within the NTUSER.DAT hive.
It is common for VDI platforms to store the contents of C:\Users\<Username>\ within a dedicated VHDX file, effectively treating each user profile as a standalone virtual disk image.

The Velociraptor Way#

Velociraptor is a modern, open-source DFIR (Digital Forensics and Incident Response) tool used by many organisations to hunt for threats, collect artefacts, and conduct scalable investigations across enterprise environments. The link below provides an introduction to the tool and details on building artefacts.
https://www.infoguard.ch/en/blog/csirt-optimisation-event-log-analysis-recording-dfir
While Velociraptor supports VHDX accessors that allow direct parsing of virtual disk images. This setup typically requires custom artefacts designed for specific use cases, such as parsing UserAssist or registry hives within a mounted VHDX file. Therefore, each forensic check would require a dedicated artefact capable of understanding the VHDX structure.
A more scalable approach is to reuse existing Velociraptor artefacts, such as Windows.Registry.UserAssist or Windows.Registry.NTUser, without modification. The purpose of this research was to enable that capability: to support native artefacts when analysing data stored in VHDX-based user profiles.
To achieve this, we leverage virtual Velociraptor clients, instances configured to remap their environment to a specific VHDX file, similar to how Velociraptor operates in deaddisk mode. In this blog post, we’ll refer to these as virtual Velociraptor clients.
The concept is simple: run a virtual client on the file server where VHDX profiles are stored, and remap its configuration to point to one or more VHDX images. Several technical steps are required to make this work reliably. Let’s explore them one by one.
VHDX.drawio

Windows.Sys.Users#

Many Velociraptor artefacts which target the user registry, such as Windows.Registry.UserAssist, access the NTUSER.DAT hive through the Windows.Registry.NTUser artefact. However, this artefact relies on Windows.Sys.Users to enumerate the list of user profiles present on the system.
By default, the Windows.Sys.Users artefact retrieves user information from the following registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*

This works as expected on endpoint systems but fails on virtual Velociraptor clients, where this registry key relates to the one on the file server and not from the VHDX-based profiles. In such environments, since the key is from a system-based hive, it only reflects the local system users, not those stored in VHDX containers.
As a result, Velociraptor is unable to discover or parse the NTUSER.DAT hives located inside the VHDX files.
To overcome this, we explore two possible solutions:

  1. Fake registry remapping: inject a synthetic ProfileList registry hive.
  2. Artefact override: customise Windows.Sys.Users to support VHDX discovery logic.

Fake registry remapping
One approach to solving the user enumeration problem is to remap not only the file system to the VHDX image, but also the ProfileList registry key. By providing a custom registry hive that mimics this key, Velociraptor can be tricked into “seeing” the VHDX-hosted profiles as if they were locally present.
In the example below, we use PowerShell to:

  1. Create temporary registry keys for each user.
  2. Set their ProfileImagePath values to simulate the standard Windows user structure.
  3. Export the registry hive to a .dat file.
# Define paths for the temporary hive and final file
$tempHivePath = "HKLM\Software\Velociraptor"
$binaryFilePath = "C:/Program Files/Velociraptor/Profile/ProfileList.dat"

# Add the users to the temp hive
reg.exe add "$($tempHivePath)\Brontosaurus" /v "ProfileImagePath" /t REG_EXPAND_SZ /d "C:\Users\Brontosaurus" /f
reg.exe add "$($tempHivePath)\Stegosaurus" /v "ProfileImagePath" /t REG_EXPAND_SZ /d "C:\Users\Stegosaurus" /f
reg.exe add "$($tempHivePath)\Tyrannosaurus" /v "ProfileImagePath" /t REG_EXPAND_SZ /d "C:\Users\Tyrannosaurus" /f

# Export the temp hive
reg exe save $tempHivePath $binaryFilePath

# Cleanup the temp hive
reg.exe delete $tempHivePath /f

Next, we configure a YAML remapping to instruct Velociraptor to mount this exported hive in place of the original ProfileList key:

- type: mount
  description: 'Registry - HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
  from:
    accessor: raw_reg
    prefix: |
      {
        "Path": "/"
        "DelegateAccessor": "file"
        "DelegatePath": "C:/Program Files/Velociraptor/Profile/ProfileList.dat"
      }
      path_type: registry
  "on" :
    accessor: registry
    prefix: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
    path_type: registry

With this setup, any call to Windows.Sys.Users will retrieve the synthetic entries we defined, allowing downstream artefacts (like Windows.Registry.NTUser) to function correctly. However, this approach has limitations. It involves manual steps that must be repeated whenever new users are added. Therefore, it is most suitable for small-scale, targeted investigations or proof-of-concept testing.

Artefact Override
A more robust and scalable solution is to override the default Windows.Sys.Users artefact. The customised version maintains compatibility with standard systems while adding logic to detect and enumerate VHDX-based profiles when operating in a virtual Velociraptor client context.
The core idea is to introduce a conditional check based on the Velociraptor client’s label. If the client is tagged with a specific label (e.g., remapped_profile), the artefact will scan for NTUSER.DAT files within C:\Users\*\ instead of relying on the traditional registry key.
The VQL override below illustrates this logic:

LET GetTimestamp(High, Low) = if(condition=High,
        then=timestamp(winfiletime=High * 4294967296 + Low))

// lookupSID() may not be available on deaddisk analysis
LET Standard = SELECT split(string=Key.OSPath.Basename, sep="-")[-1] as Uid,
   "" AS Gid,
   LookupSIDCache(SID=Key.OSPath.Basename || "") AS Name,
   Key.OSPath as Description,
   ProfileImagePath as Directory,
   Key.OSPath.Basename as UUID,
   Key.Mtime as Mtime,
   {
        SELECT Mtime
        FROM stat(filename=expand(path=ProfileImagePath))
    } AS HomedirMtime,
   dict(ProfileLoadTime=GetTimestamp(
           High=LocalProfileLoadTimeHigh, Low=LocalProfileLoadTimeLow),
        ProfileUnloadTime=GetTimestamp(
           High=LocalProfileUnloadTimeHigh, Low=LocalProfileUnloadTimeLow)
   ) AS Data
FROM read_reg_key(globs=remoteRegKey, accessor="registry")

// User list for remapped VHDX profiles emulating the Standard one
LET VHDXProfile = SELECT
      OSPath.Components[-2] AS Uid,
      OSPath.Dirname AS Directory,
      OSPath.Components[-2] AS UUID,
      OSPath.Components[-2] AS Name,
      "" AS Gid,
      Mtime,
      Mtime AS HomedirMtime,
      "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\" + OSPath.Components[-2] AS Description
    FROM glob(globs="C:/Users/*/NTUSER.DAT")

// Get the labels of the agent 
LET agent_config = SELECT labels FROM config

// Take the appropriate user list method
SELECT * FROM if(condition=agent_config.labels=~labelName, then=VHDXProfile, else=Standard)

Once the artefact override is written, it can be deployed at the server level using the following command:

/usr/local/bin/velociraptor —config /etc/velociraptor/server.config.yaml —definitions /etc/velociraptor/artifact_definitions frontend

With this configuration, the Velociraptor server will automatically distribute the custom artefact to clients. There’s no need to modify individual client configurations. Any virtual client launched with the appropriate label (e.g., remapped_profile) will use the VHDX logic seamlessly.
This is a better solution as it centralises logic, reduces operational overhead, and supports dynamic scaling, making it ideal for multi-user investigations.

Remapping Configuration#

Once the user enumeration issue is resolved using the artefact override, the next step is to create a remapping configuration that links specific user profile paths (e.g. C:\Users\Brontomerus) to their corresponding VHDX files.
This remapping is crucial: it allows Velociraptor’s artefacts, which expect standard Windows paths, to transparently operate on data stored within VHDX containers.
Below is an example of a remapping configuration for the NTFS accessor, pointing C:\Users\Brontomerus to a specific VHDX file:

- type: mount
  description: 'NTFS - Brontomerus'
  from:
    accessor: raw_ntfs
    prefix: |
      {
        "DelegateAccessor": "offset",
        "Delegate": {
          "DelegateAccessor": "vhdx",
          "DelegatePath": "F:/VHDX/Profile_Brontomerus.VHDX",
          "Path":"/1048576"
        },
        "Path": "/Profile"
      }
  "on":
    accessor: ntfs
    prefix: '\\.\C:\Users\Brontomerus'
    path_type: ntfs

This remapping allows the virtual Velociraptor client to access the contents of the VHDX file as if it were the actual C:\Users\Brontomerus directory on disk. To ensure full compatibility with Velociraptor artefacts, similar remappings should be created for:

  • file accessor for standard file operations
  • auto accessor for hybrid path resolution
  • registry accessor for registry hive access

When these accessors are mapped properly, standard artefacts (like Windows.Registry.NTUser, Windows.Registry.UserAssist, etc.) will function as expected within the virtual client context.

Virtual Client Creation#

With the remapping configuration in place, we can now launch a virtual Velociraptor client. This is a standalone Velociraptor process that operates like a deaddisk client, using the remapped paths to access the contents of a specific VHDX file as if it were a local user profile.
The following PowerShell command starts the virtual client using the appropriate configuration and remapping YAML file:

Start-Process -FilePath '.\Velociraptor.exe' -ArgumentList '—config client.config.yaml —config.client-writeback-windows="velociraptor_vhdx.writeback.yaml" —config.client-local-buffer-filename-windows="C:\Windows\Temp\velociraptor_vhdx_Buffer.bin" —remap "C:\Program Files\Velociraptor\velociraptor_vhdx_remapping.yaml" —config.client-labels=remapped_profile client' -WorkingDirectory 'C:\Program Files\Velociraptor' -WindowStyle Hidden

Once launched, the virtual client behaves like any standard Velociraptor endpoint, but its file system and registry access are redirected to the contents of the mounted VHDX. This enables seamless browsing of the virtual profile via the Virtual File System (VFS).

image

It also allows running existing artefacts or any other profile-level checks.
image
This virtual client is not persistent. If the system reboots or the client crashes, the process must be relaunched. Fortunately, this can be done using the same artefact described later in this post.

Let’s Scale Up#

With a single virtual client successfully analysing one user profile, the next logical step is to scale the approach. During real-world incidents, it is common to have multiple users to investigate. Investigating each profile manually is inefficient, so automation and parallelisation become essential.
However, running multiple virtual Velociraptor clients on the same host introduces performance considerations. In our tests, we evaluated three strategies for scaling up to 1,000 user profiles (each stored in a ~2 GB VHDX file).

All in One Remapping#

This approach loads all VHDX files into a single remapping configuration, resulting in a YAML file about 2.6 MB in size. While ideally efficient, it caused instability; the virtual client crashed during execution of simple artefacts (e.g., Generic.Client.Info), probably due to too many VHDX files being open simultaneously, exceeding system limits.
image

One-to-One Clients#

Here, we spun up one virtual Velociraptor client per user profile. This would work best as each profile has its own virtual client, which eases the logic. However, this resulted in excessive system resource usage and hundreds of active processes, which is not sustainable for large-scale investigations.

Batching: The Optimal Middle Ground#

The most effective approach was to launch multiple virtual clients, each handling a batch of 40 profiles. This method balances load across processes and avoids overwhelming system resources.
image
With this configuration, we analysed the UserAssist of 1,000 VHDX files in under 30 seconds.
image
Use a batching strategy tuned to your environment (e.g. 20–50 profiles per client). The optimal batch size will depend on your hardware and the size of the VHDX files.

Artefacts#

The artefacts have been divided into four main functions: the initial setup, the creation of the remapping configuration file, the virtual Velociraptor client runner, and the virtual Velociraptor client remover.
To streamline the investigation of VHDX-based user profiles, we developed a suite of Velociraptor artefacts to automate the entire process, from configuration to client execution and teardown. These artefacts fall into four primary categories:

  1. Server setup: allow the whole setup to work
  2. Configuration builder: generates remapping YAML files for virtual clients
  3. Virtual client runner: launches Velociraptor processes with profile mappings
  4. Cleanup manager: shuts down virtual clients and removes config artefacts

Windows.Sys.Users (Override)#

This custom override of the standard Windows.Sys.Users artefact handles both traditional systems and virtual clients. It determines which enumeration logic to use based on a label assigned to the client (e.g. remapped_profile).
Parameters:

  • remoteRegKey: Location of the registry key holding the profile list.

  • labelName: Client label that signals the use of VHDX-based enumeration.

In standard mode, it enumerates from the Windows registry. For virtual clients, it uses a glob search for C:\Users\*\NTUSER.DAT, indicating valid user hives within mounted VHDX files.

Windows.Vhdx.RemapConfigBuilder#

This artefact generates the remapping configuration files required by each virtual Velociraptor client. It automatically detects user profiles, organises them into batches, and writes the YAML files to disk.
Parameters:

  • vhdxFolderPath: Directory containing VHDX profile files.

  • usernameExtractor: Regex used to extract usernames from filenames.

  • user: Optional filter to restrict which profiles to process.

  • virtualHostname: Hostname to assign to the virtual clients.

  • batchSize: Number of profiles per virtual client.

  • vhdxOffset: NTFS start offset inside VHDX (default: 1048576).

The artefact creates a Vhdx/Remapping directory within the Velociraptor staging directory and populates it with one YAML file per batch. Filenames are sorted using the first extracted username for easy lookup. Internally, the artefact uses:

  • Shadow remapping for data, raw_reg, and zip accessors

  • Mount remapping for ntfs, file, auto, and registry accessors

Once executed, the artefact will list the path of the created profiles in the results section.
image

Windows.Vhdx.VirtualClientRunner#

This artefact launches one Velociraptor process per remapping file, effectively simulating a client that represents a set of user profiles.
Parameters:

  • customLabel: Label to assign to virtual clients (must match label used in Windows.Sys.Users).

  • remappingFile: One or more remapping YAML files to launch.

It creates a subdirectory Vhdx/Writeback for temporary writeback data. Each remapping file is passed into a PowerShell process that starts the virtual client. Once executed, the artefact lists all successfully started virtual clients.
image

Windows.Vhdx.VirtualClientRemover#

This artefact stops running virtual clients and optionally deletes all related configuration files.
Parameters:

  • RemappingFile: Specific remapping file or directory to target.

  • RemoveConfiguration: If enabled, deletes Vhdx folder and its subfolders.

  • ReallyKillProcess: If enabled, terminates running Velociraptor processes.

It searches for running clients based on remapping file usage and performs cleanup accordingly.
image

Security Considerations#

All testing described in this research was conducted on offline VHDX files, that is, files no longer actively managed by services like Citrix or Windows profile management tools.
In live environments, where VHDX profiles may still be in use or mounted, several risks should be considered:

  • Writeback conflicts: If Velociraptor or the OS attempts to write to an active VHDX file, it may result in data corruption or race conditions.
  • Profile locking: Some virtualisation platforms may lock VHDX files during use, preventing remapping or causing artefact failures.
  • System performance: Mounting and scanning a large number of VHDX files can place a significant load on the host system.

Recommendations:

  • Perform investigations on copies of VHDX files whenever possible.
  • Avoid using this approach directly on live Citrix or VDI environments without controlled testing.
  • Start with small user subsets and adjust batch size based on your environment’s capacity.
  • Monitor resource usage (I/O, memory, open handles) when scaling beyond hundreds of profiles.

While we successfully analysed 1,000 small (2 GB) VHDX profiles in under a minute, larger profiles or more aggressive batching may introduce bottlenecks. Tailor your approach to match the capabilities and constraints of your infrastructure.

Conclusion#

This VHDX Velociraptor artefact suite provides a scalable and efficient method for conducting forensic investigations across user profiles stored in VHDX files. By leveraging virtual clients with remapped environments, analysts can reuse standard artefacts, such as UserAssist or NTUSER.DAT parsers, without modification.
In our testing, this approach enabled the analysis of over 1,000 VHDX profiles in under a minute, significantly reducing the effort and complexity traditionally associated with VHDX triage.
While further validation is recommended in live or larger-scale environments, this methodology opens the door to high-volume, profile-level investigations using familiar DFIR tooling, without sacrificing speed or forensic integrity.

Automation of VHDX Investigations
https://labs.infoguard.ch/posts/automation_of_vhdx_investigations/
Author
Yann Malherbe
Published at
2025-09-26