Recently, I have been thinking about alternate means of transferring content to and from enterprises in the presence of network inspection technology, like host based DLP and AVs.

So I looked into what Hyper-V Virtual Drives (VHDX) could offer in terms of packaging and whether they could be used as an alternative to something like a TAR/ZIP archives, and how the defensive tech interfaces with them on a Windows machine.

VHD/VHDX is a virtual container technology employed (created/designed?) by Microsoft for it’s Hyper-V stack. The drives are normally used to support VM images and can be created as either a fixed size container or a dynamic container. Something you would normally expect as part of a normal VM provisioning and operation.

The containers can be Attached , Mounted, Initialized, and managed with common Windows disk tools. They are also portable and Self Contained. Which would mean they could be built on one machine, transfered to another one and attached to a different VM, or can be Mounted standalone as drives.

Properties like portability, self-hosting and containerization appeared to be interesting enough to warrant a deeper dive.

Hyper-V feature support. Installing Hyper-V and VHD powershell tools

To build a VHD/VHDX drive I could just used the Windows drive management GUI, but I wanted to see if I could do it in a more automated fashion, partly because there could be a time when this process needs to be executed over a remote agent, without access to RDP.

Microsoft provides a set of features for Hyper-V management. They carry the various management tools, among them Powershell cmdlets like New-VHDX which will make creation of the VHDX container easy. However, the process of installation of those features takes a significant overhead and modifies the system enough to make it somewhat “heavy” for the purpose of just creating a disk container. But you could do this like so:

Elevated> Get-WindowsOptionalFeature -Online -FeatureName *hyper-v* | select DisplayName, FeatureName

1
2
3
4
5
6
7
8
9
DisplayName                           FeatureName
-----------                           -----------
Hyper-V                               Microsoft-Hyper-V-All
Hyper-V Platform                      Microsoft-Hyper-V
Hyper-V Management Tools              Microsoft-Hyper-V-Tools-All
Hyper-V Module for Windows PowerShell Microsoft-Hyper-V-Management-PowerShell
Hyper-V Hypervisor                    Microsoft-Hyper-V-Hypervisor
Hyper-V Services                      Microsoft-Hyper-V-Services
Hyper-V GUI Management Tools          Microsoft-Hyper-V-Management-Clients

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Management-PowerShell

No Hyper-V support needed.

I opted for a more portable way with less overhead. Plus, I wanted to learn some of the low level storage APIs Microsoft had bult the Hyper-V Powershell tooling on.

Hyper-V’s VHD/VHDX is a type of storage, and so for a regular disk query and management, Microsoft already created a Powershell Storage module which I could import without installing Hyper-V tooling. I could leverage them for my purposes like so:

Import Storage module and use disk and image cmdlets

User> Import-Module Storage

User> get-disk

1
2
3
4
5

Number Friendly Name             Serial Number                    HealthStatus         OperationalStatus      Total Size Partition
                                                                                                                         Style
------ -------------             -------------                    ------------         -----------------      ---------- ---------- 
0      VMware, VMware Virtual S                                   Healthy              Online                     100 GB MBR

Getting to the API

Window’s Virtual Storage virtdisk.h describes a handful of useful functions for us to be able to create VHD/VHDX containers ourselves.

and others.

Armed with this information and some references from MSDN it is possible to code up a utility to create small size (8MB in this case) fixed container that we can populate with data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Usage:  Vhdx.exe <Action> <Arguments>
Supported Actions and Arguments:
   GetVirtualDiskInformation <path>
   CreateFixedVirtualDisk <path> <file size> <block size> <logical sector size> <physical sector size>
   CreateDynamicVirtualDisk <path> <file size> <block size> <logical sector size> <physical sector size>
   AttachVirtualDisk <path> <readonly>
   DetachVirtualDisk <path>
   GetAllAttachedVirtualDiskPhysicalPaths

Examples:
    GetVirtualDiskInformation c:\fixed.vhd
    CreateFixedVirtualDisk c:\fixed.vhd 1073741824 0 0 0
    CreateDynamicVirtualDisk c:\dynamic.vhdx 1073741824 0 0 0
    AttachVirtualDisk c:\fixed.vhd true
    DetachVirtualDisk c:\fixed.vhd
    GetAllAttachedVirtualDiskPhysicalPaths

User> .\Vhdx.exe CreateFixedVirtualDisk c:\Temp\fixed.vhdx 8388608 0 0 0

After the container is created, there is nothing in it, but Windows still wants Admin rights to Attach the disk to the OS.

Elevated>

.\Vhdx.exe AttachVirtualDisk C:\Temp\fixed.vhdx false

Verifying newly created disk

After this has been done, the disk appears ready and recognized by the OS.

User>

1
2
3
4
5
6
get-disk
Number Friendly Name             Serial Number                    HealthStatus         OperationalStatus      Total Size Partition
                                                                                                                         Style
------ -------------             -------------                    ------------         -----------------      ---------- ---------- 
1      Msft Virtual Disk                                          Healthy              Online                       8 MB RAW
0      VMware, VMware Virtual S                                   Healthy              Online                     100 GB MBR

We can also code up the query for the disk we have just created with Storage API, to show information similar to this:

User> .\Vhdx.exe GetVirtualDiskInformation c:\Temp\fixed.vhdx

1
2
3
4
5
6
7
8
9
driveType = 2 (fixed)
driveFormat = 3 (vhdx)
physicalSize = 12582912
virtualSize = 8388608
sectorSize = 512
blockSize = 0
physicalSectorSize = 4096
_identifier = {B951E523-9D3F-427E-9EDBF77C6C1BEB0E}_
fragmentationPercentage = 0

Initialize disk, partition

While the VHDX is now attached and recognizable by the OS, it lacks any structured partition, and has no filesystem on it. Here is how we can get around that. It is possible to code up disk partitioning and NTFS by hand with the low level API. But I thought this would take a lot more effort and learning to do so than I had time for, so let’s use utilities provided by Windows. We will continue to use PowerShell, WMI and diskpart utility.

When we ran a query on visible disks in our previous step, we could see the new disk attached to the system, of type RAW. This is disk number 1. To lay over a filesystem the disk first needs to be Initialized , and a primary partition has to be created for the filesystem to live in.

The first (and the only) logical partition on an empty disk would be also number 1. We can define some variables for both of these parameters, and initialize the disk.

User> $disknumber = 1 ; $partnumber = 1 User> Initialize-Disk -Number $disknumber -PartitionStyle MBR

Then we can create the sigle partition, spanning the entire 8MB VHDX.

User> New-Partition -DiskNumber $disknumber -UseMaximumSize

1
2
3
4
5
   DiskPath: \\?\scsi#disk&ven_msft&prod_virtual_disk#2&1f4adffe&0&000003#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}

PartitionNumber  DriveLetter Offset                                              Size Type
---------------  ----------- ------                                              ---- ----
1                            65536                                            6.94 MB Logical

Laying over NTFS and marking the drive letter

Let’s also assign the NTFS Label - Install to the FS, and choose the drive letter which will be used persistently for this volume: I Automating diskpart requires us to pipe commands into it like so:

1
2
3
4
5
6
7
8
"@
select disk $disknumber
select partition $partnumber
format quick fs=ntfs label=Install
select partition $partnumber
assign letter=I:
exit
@" | diskpart
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
DISKPART>
Disk 1 is now the selected disk.
DISKPART>
Partition 1 is now the selected partition.
DISKPART>
  100 percent completed
DiskPart successfully formatted the volume.
DISKPART>
Partition 1 is now the selected partition.
DISKPART>
DiskPart successfully assigned the drive letter or mount point.
DISKPART>
Leaving DiskPart...

Optionally, mount the disk if not mounted

The OS automatically mounts the drive to the letter but if needed we can mount the drive outselves:

User> Mount-DiskImage -ImagePath C:\Temp\fixed2.vhd

Checking the container state, location and parameters

Of course, we can use PowerShell storage module to query the mounted volume:

User>

Get-PSDrive | where { $_.Root -eq 'I:\'}

1
2
3
Name           Used (GB)     Free (GB) Provider      Root    CurrentLocation 
----           ---------     --------- --------      ----    --------------- 
I                   0.00          0.00 FileSystem    I:\

The disk can still be seen:

User> Get-DiskImage -ImagePath C:\Temp\fixed.vhdx

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Attached          : True
BlockSize         : 0
DevicePath        : \\.\PHYSICALDRIVE1
FileSize          : 12582912
ImagePath         : C:\Temp\fixed.vhdx
LogicalSectorSize : 512
Number            : 1
Size              : 8388608
StorageType       : 3
PSComputerName    :

We can copy files and folders into the 8MB drive, unmount it and move it across the wire to the destination system. In my tests, the mail system did not trigger a scan on the attachment which contained a filesystem with an exploit. So far, it worked well.

Target: Container mount

Let’s see what the target machine needs to work withe that container.

Mounting VHDX

Mount-DiskImage : A required privilege is not held by the client.` is the message you get when attempting to mount the container. So you need to run from the elevated prompt. However, I could mount from the explorer shell menu without elevation or UAC. This remains an item for further investigation.

Target:Elevated> Mount-DiskImage -ImagePath C:\Temp\fixed.vhdx

Mounting without a drive letter (Elevated)

Also, it is possible to mount VHDX without the drive letter. This is useful when you want to operate in a more stealthy fashion on the system. You could still interact with the contents of the container but the drive is not assigned and so the VHDX is not in the Explorer.

Mount the drive:

Target:Elevated> Mount-DiskImage -NoDriveLetter -ImagePath C:\Temp\fixed.vhdx

Check that it’s attached

Target> Get-DiskImage -ImagePath C:\Temp\fixed.vhdx

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Attached          : True
BlockSize         : 0
DevicePath        : \\.\PHYSICALDRIVE1
FileSize          : 12582912
ImagePath         : C:\Temp\fixed.vhdx
LogicalSectorSize : 512
Number            : 1
Size              : 8388608
StorageType       : 3
PSComputerName    :

Check for the mounted volume without a drive letter by using WMI searching for the NTFS Label we have assigned before:

Target> Get-WmiObject win32_volume -Filter "Label = 'Install'" -ErrorAction Stop

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
__PATH                       : \\DESKTOP-JMJMNLI\root\cimv2:Win32_Volume.DeviceID="\\\\?\\Volume{080f6ce3-0000-0000-0000-0100000000
                               00}\\"
Access                       :
Automount                    : True
Availability                 :
BlockSize                    : 4096
BootVolume                   : False
Capacity                     : 7270400
Caption                      : \\?\Volume{080f6ce3-0000-0000-0000-010000000000}\
Compressed                   : False
DeviceID                     : \\?\Volume{080f6ce3-0000-0000-0000-010000000000}\
DirtyBitSet                  : False
DriveLetter                  :
DriveType                    : 3
FileSystem                   : NTFS
FreeSpace                    : 3358720
IndexingEnabled              : True
InstallDate                  :
Label                        : Install
LastErrorCode                :
MaximumFileNameLength        : 255
Name                         : \\?\Volume{080f6ce3-0000-0000-0000-010000000000}\
NumberOfBlocks               :
PageFilePresent              : False
SerialNumber                 : 677415077
Status                       :
StatusInfo                   :
SupportsDiskQuotas           : True
SupportsFileBasedCompression : True
SystemCreationClassName      :
SystemName                   :
SystemVolume                 : False
PSComputerName               : DESKTOP-JMJMNLI

We could also do this from Powershell if needed like so:

Target> Get-Volume -FriendlyName Install

1
2
3
DriveLetter FriendlyName FileSystemType DriveType HealthStatus OperationalStatus SizeRemaining    Size
----------- ------------ -------------- --------- ------------ ----------------- -------------    ----
            Install      NTFS           Fixed     Healthy      OK                       3.2 MB 6.93 MB

Example: Operating on resources on the hidden volume without a drive

List files:

Target>

ls -l (Get-Volume -FriendlyName 'Install' ).Path

1
2
3
4
5
6
    Directory: \\?\Volume{080f6ce3-0000-0000-0000-010000000000}


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        10/9/2019  12:22 PM              0 Hello.md.txt

Start execution from volume:

Target> start (Join-Path -Path (Get-Volume -FriendlyName 'Install' ).Path -ChildPath "Hello.md.txt")

Dismountng the VHDX container

Unmounting does not require elevation and can be done with PowerShell WMI or mountvol:

Target> Dismount-DiskImage -ImagePath C:\Temp\fixed.vhdx

Conclusion

  • VHDX is an interesting way to package and transfer files under the radar. Unlike ISOs they are not yet scanable by many defense stacks.
  • VHDX can be a hidden volume. Gains stealth in the operation.
  • It is possible to create VHD/VHDX witjout Hyper-V tooling. The code sample will be released shortly.
  • More work is needed to proof what EDRs/AVs are seeing in the containers.

Thanks for reading.