Smuggling payloads and tools in, using WIM images, Part 2

In this post we explore the dism.exe and WIM images a bit more.

It turns out that WIM files are containers that can include more than one image. One can create the first image using the /Capture-Image option, and then append new images to the same WIM file using the /Append-Image command line argument.

In a test scenario, I created 3 subfolders containing:

  • Image1 – Sysmon
  • Image2 – Eicar
  • Image3 – Mimikatz

I then created a multi-image newtest.wim file using the following syntax:

Dism /Capture-Image /ImageFile:”newtest.wim” /CaptureDir:image1_sysmon /Name:sysmon 
Dism /Append-Image  /ImageFile:”newtest.wim” /CaptureDir:image2_eicar /Name:eicar 
Dism /Append-Image  /ImageFile:”newtest.wim” /CaptureDir:image3_mimikatz /Name:mimikatz 

To confirm the images were added to the newtest.wim file, I ran these commands:

dism /list-image /imagefile:"newtest.wim" /index:1
dism /list-image /imagefile:"newtest.wim" /index:2
dism /list-image /imagefile:"newtest.wim" /index:3

I was a bit surprised the ADSs were not listed.

Luckily, 7z lists a bit more information:

The content of [1].xml is forensically interesting:

<WIM>
 <TOTALBYTES>2122196</TOTALBYTES>

 <IMAGE INDEX="1">
  <DIRCOUNT>0</DIRCOUNT>
  <FILECOUNT>1</FILECOUNT>
  <TOTALBYTES>4563248</TOTALBYTES>
  <HARDLINKBYTES>0</HARDLINKBYTES>
  <CREATIONTIME>
   <HIGHPART>0x01DB5BD0</HIGHPART>
   <LOWPART>0x5FAF914D</LOWPART>
  </CREATIONTIME>
  <LASTMODIFICATIONTIME>
   <HIGHPART>0x01DB5BD0</HIGHPART>
   <LOWPART>0x5FB40FD8</LOWPART>
  </LASTMODIFICATIONTIME>
  <WIMBOOT>0</WIMBOOT>
  <NAME>sysmon</NAME>
 </IMAGE>
 
 <IMAGE INDEX="2">
  <DIRCOUNT>0</DIRCOUNT>
  <FILECOUNT>1</FILECOUNT>
  <TOTALBYTES>68</TOTALBYTES>
  <HARDLINKBYTES>0</HARDLINKBYTES>
  <CREATIONTIME>
   <HIGHPART>0x01DB5BD0</HIGHPART>
   <LOWPART>0x6DB1F772</LOWPART>
  </CREATIONTIME>
  <LASTMODIFICATIONTIME>
   <HIGHPART>0x01DB5BD0</HIGHPART>
   <LOWPART>0x6DB6281B</LOWPART>
  </LASTMODIFICATIONTIME>
  <WIMBOOT>0</WIMBOOT>
  <NAME>eicar</NAME>
 </IMAGE>

 <IMAGE INDEX="3">
  <DIRCOUNT>0</DIRCOUNT>
  <FILECOUNT>4</FILECOUNT>
  <TOTALBYTES>1440600</TOTALBYTES>
  <HARDLINKBYTES>0</HARDLINKBYTES>
  <CREATIONTIME>
   <HIGHPART>0x01DB5BD0</HIGHPART>
   <LOWPART>0x6FE89BE7</LOWPART>
  </CREATIONTIME>
  <LASTMODIFICATIONTIME>
   <HIGHPART>0x01DB5BD0</HIGHPART>
   <LOWPART>0x6FEDA2E8</LOWPART>
  </LASTMODIFICATIONTIME>
  <WIMBOOT>0</WIMBOOT>
  <NAME>mimikatz</NAME>
 </IMAGE>
</WIM>

I was also curious how the file will be ‘seen’ by VT, so I submitted it here. To my surprise, we got multiple different detections, hitting on different internal images – either Eicar or Mimikatz (I was hoping that my first image, sysmon, will help to bypass most of the scans – I was wrong):

Coming back to the newly created file, newtest.wim, it’s important to mention that apart from the multiple images it can host, it can also be split into smaller chunks (same as 7z, zip, or rar archives).

Running the following command:

dism /split-image /imagefile:"newtest.wim" /SWMFile:"newtest.swm" /FileSize:1

will split our newtest.wim file into 3 swm files:

    4,435 newtest.swm
1,417,386 newtest2.swm
  704,883 newtest3.swm
    2,126,704 bytes

I am not sure I follow how the 1M boundary I asked for led to creation of these 3 files with file sizes looking quite random, but one way or another, an ability to split a WIM file into SWM file chunks may come handy.

They certainly come handy when it comes to bypassing VT detections:

The last bit I want to quickly cover here is the /EA command line argument that we can use during image creation (/Capture-Image). The default behavior for the /Capture-Image is to collect both files, and their Alternate Data Streams, but /EA options extends the collection to Extended Attributes as well. This enables us to ‘outsource’ hiding data and payloads (in either ADS or EAs) to dism.exe process, as all the mounting-related, but ‘dodgy’ file system ‘object creation’ activities will be associated with this process only.

I think dism.exe is a tool that ended up being overlooked by many of us, but I hope we will all pay more attention to it now… This Microsoft page describes this tool’s command line arguments in great detail.

Happy hunting!

Smuggling payloads and tools in, using WIM images

We often hear of attackers bringing in their payloads via virtual drive images (f.ex. vhd,vhdx) in an attempt to bypass security solutions. The WIM files can be used to smuggle in tools and payloads to the target, too. In my previous post I discussed the $WIMMOUNTDATA Alternate Data Stream that is created by dism.exe when we use it to mount a WIM image.

Now, the way the WIM images are mounted is interesting for many reasons:

  • they are mounted read-only, so once mounted, can’t delete files they provide access to
  • the files they expose to the OS are not ‘created’ in any telemetry sense, so there are no ‘File Created’ events for them – it’s just a file system tunnel
  • they are tiny, and can even be easily encrypted/decrypted using available lolbin tools, or powershell
  • the .wim files themselves, once mounted, can’t be deleted
  • interestingly, when you create .WIM files from sources that include Zone.Identifier ADS (typically after downloading the files from the internet), these ADS will make it to the WIM image as well; so, have to be mindful of it

Here’s an example mimikatz.wim (pass: mimi) WIM image (it actually has a decent detection rate on VT). Its file list indicates it was created from a directory that included old mimikatz files downloaded directly from github (hence, ADS are present):

mimidrv.sys
mimidrv.sys:Zone.Identifier
mimikatz.exe
mimikatz.exe:Zone.Identifier
mimilib.dll
mimilib.dll:Zone.Identifier
mimispool.dll
mimispool.dll:Zone.Identifier

The 7z listing of the archive looks as follows:

Listing archive: mimikatz.wim

--
Path = mimikatz.wim
Type = wim
Physical Size = 704059
Size = 1440688
Packed Size = 702019
Method = XPress:15
Cluster Size = 32768
Created = 2024-12-30 22:11:48.7166057
Modified = 2024-12-30 22:11:48.7385760
Comment = <WIM><TOTALBYTES>703241</TOTALBYTES><IMAGE INDEX="1"><DIRCOUNT>0</DIRCOUNT><FILECOUNT>4</FILECOUNT><TOTALBYTES>1440600</TOTALBYTES><HARDLINKBYTES>0</HARDLINKBYTES><CREATIONTIME><HIGHPART>0x01DB5B07</HIGHPART><LOWPART>0xD2354269</LOWPART></CREATIONTIME><LASTMODIFICATIONTIME><HIGHPART>0x01DB5B07</HIGHPART><LOWPART>0xD2389CA0</LOWPART></LASTMODIFICATIONTIME><WIMBOOT>0</WIMBOOT><NAME>mimi</NAME></IMAGE></WIM>
Version = 1.13
Multivolume = -
Volume = 1
Volumes = 1
Images = 1

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2013-01-22 16:50:12 ....A        37208        17078  mimidrv.sys
2022-09-19 15:44:01 ....A        37376        19303  mimilib.dll
2022-09-19 15:43:57 ....A        10752         4973  mimispool.dll
2022-09-19 15:44:39 ....A      1355264       660577  mimikatz.exe
------------------- ----- ------------ ------------  ------------------------
2022-09-19 15:44:39            1440600       701931  4 files
2022-09-19 15:44:39                352          352  4 alternate streams
2022-09-19 15:44:39            1440952       702283  8 streams

There are plenty of forensic artefacts present in that file, including the Comment field that 7z extracts:

<WIM>
 <TOTALBYTES>703241</TOTALBYTES>
 <IMAGE INDEX="1">
  <DIRCOUNT>0</DIRCOUNT>
  <FILECOUNT>4</FILECOUNT>
  <TOTALBYTES>1440600</TOTALBYTES>
  <HARDLINKBYTES>0</HARDLINKBYTES>
  <CREATIONTIME>
   <HIGHPART>0x01DB5B07</HIGHPART>
   <LOWPART>0xD2354269</LOWPART>
  </CREATIONTIME>
  <LASTMODIFICATIONTIME>
   <HIGHPART>0x01DB5B07</HIGHPART>
   <LOWPART>0xD2389CA0</LOWPART>
  </LASTMODIFICATIONTIME>
  <WIMBOOT>0</WIMBOOT>
  <NAME>mimi</NAME>
 </IMAGE>
</WIM>

Combining the knowledge from this and previous post, one can start wondering…

If we mount an innocent WIM image first, one that lists only good (or at the very least – dummy) files, and then, we export the mounted directory’s $WIMMOUNTDATA ADS, modify it to point to a different WIM file, the bad one, then we write it back to the directory’s ADS… what will the system see/do?

Turns out, that modifying the ADS alone is NOT ENOUGH to fool the OS to ‘redirect’ the tunnel to a different image 🙁

Looking for other angles, we can search the Registry and we can discover that this whole WIM mounting business is nicely documented here:

under the following key:

HKLM\SOFTWARE\Microsoft\WIMMount\Mounted Images\

So, what about we change the WIM Path value to point to the bad WIM image, and restart the system?

Nothing.

The ‘mounted’ directory will still list the files from the original ‘neutral’ WIM image only.

Okay, so it’s time we explore the actual $MFT of the C: drive where we mounted our WIM image to. To our surprise, the $MFT does include FILE records for every single file from our neutral WIM image!

Oops. Our original assumption that there are no ‘File Create’ events in our telemetry was wrong!

Literally, the dism.exe is reading the WIM image file and then it is recreating its codified directory structure by writing it to a destination folder, recursively; and for each directory or file, or even ADS, it is triggering the “File Create” events:

And there is one more wrong assumption we need to address:

  • the WIM images are mounted as read-only

The dism.exe program tells us it is not true when we try to remount the WIM image that is already mounted:

Exploring the mounted directories, you can easily delete files and directories.

Oops.

At this stage, you probably realize that this post is written from a perspective of an unreliable narrator…