 Hello everyone and welcome to my talk container self-staying undetected using the windows containers isolation framework The use of containers became an integral part of any resource efficient and secure environment starting from Edo server 2016 Microsoft released its version of this solution called Windows containers Today we will break down the containers file system isolation framework and see how it can be abused to bypass security products file system based malware protection File write restrictions and ETW based correlations Before we get a bit about myself. My name is Daniela Vinon. I'm a security researcher at deep instinct for the past two years It's my work. I focus mainly on windows internals and low-level programming And I'm also a bigger motorsport fan. I'll even admit to you that I have a driving simulator at home Which costed me way too much Okay today's agenda We will start by Overviewing the basics of the windows containers framework and get to know some of its main concepts We then continue to analyze the WCFS driver Which is the main driver the framework uses for file system isolation and see how our findings can then be utilized to bypass security products Finally, we will summarize and provide ways to effectively mitigate the bypasses Okay, let's start with the basics of windows containers job objects Job objects have been around since the days of Windows server 2003 These objects are designed to group several processes and manage them as one unit This allows the system to control the attributes of all processes associated with a job like limiting their CPU usage IO band with virtual memory usage and network activity and Multi-processed applications often use these objects to manage your child processes more easily These are also known as nested job. For example, this Google drive processes uses a nested job to manage its child's processes Although they make a good start jobs themselves are not enough to provide the isolation needed for a container Which is why Microsoft created silos Silo's can be considered an extension of jobs kind of super jobs Similarly to traditional jobs these objects are used for process grouping, but with additional features Containers use a type of silo called server silo, which besides providing the basic job capabilities It also provides redirection of various system resources like the registry networking and the object manager The windows kernel detects processes assigned to silos using API like ps is currently in server silo and ps is Processing silo Looking at the IO unload driver snippet taken from NTS kernel the windows kernel checks Whether the process that attempted to unload the driver is within a server silo And if it is it locks the operation and blocks it by returning status privilege not held So silos do provide a good foundation to a container, but file system separation is still needed Microsoft implemented this concept using repos points. So what is a repos point? Repos point is an MFT attribute and it can be given to files or directories It's data type sorry it's a repos point data is it's typed meaning a registered register tag identifier or the Rippers tag Determines the data structure of the repos point data itself It can be given to files or directories using the device IO control function Alongside the set repos point file system control code noted write primitive is needed for this to succeed Now when a file with a repos point is being opened It is handled by a file system mini filter driver according to its repos type a Good example of these attributes can be seen in symbolic links Directories that functions as a symbolic link to another Contains behind the scene a repos point with the path to the correct destination Using the junction 64 to from system tunnel so you can see that the see users all users folder redirects using a symbolic link to see program data this redirection again is Implemented by a repos points So we mentioned at the previous slide many filter drivers. So let's give a bit of a background about them as well Many filter drivers were designed to make the IO filtering process much easier for developers Since implementing a legacy filter driver from scratch is difficult Microsoft provided a solution in the form of its mil filter manager The filter manager is a legacy filter that manages other mini filter drivers according Sorry and takes care of all the heavy liftings for them for example They're insertion to the device tech ignoring any relevant requests and the support for multiple platforms among other It also exposes a mini filter dedicated API that implements the common operations used by these drivers This is also known as the FLT API will soon see some functions starting with the FLT prefix Okay, each mini filter can be attached by the manager to one or more volumes creating what is called the mini filter instance and A volume is simply a logical storage unit that is implemented Sorry that is presented to the file system as a disk for example the C drive or in its anti-device path the hard disk volume 3 Similarly to legacy filters and mini filter instances can intercept the pre and post operations of numerous IO functions like create read and write And another important concept the filter manager implements is the mini filter altitude system Each mini filter should specify an altitude which is a numeric value upon its registration to the manager The filter manager invokes its mini filter operations callbacks according to their altitude a Higher altitude mini filter will handle the pre operation before the ones below it and the post operation after them So I know that this was a long slide To make things a bit clearer Let's see demonstration of how many filters and riposte points work together When you call to any create file is made with a file containing a point the IO manager will be in will build an IRP an Interrupt request packet for this operation This IRP will then be sent down the device stack of the storage device the C drive in our case until it will reach the filter manager as stated The filter manager organizes its mini filters in a stack based on their aside altitudes It will then invoke the pre-create operation callbacks of its mini filters according to their altitudes from top to bottom And after all the pre operation callbacks were invoked The IRP will then be returned to the filter manager and then be sent down the storage device stack until it will reach the actual storage device When it will reach the device the file will be read and the IRP will then go back up the device stack this time at the opposite direction And when it will reach the NTFS driver it will recognize a file containing a point was being opened So it will change the status of this IRP to status repulse The filter manager then will receive the IRP again And this time it will invoke the post operation callbacks of its mini filters again according to their altitudes this time at the Opposite direction from bottom to top Now when a mini filter that should handle a riposte point gets an IRP with the status repulse status code It first needs to get the actual content of the riposte point from the MFT attribute itself And he does so by calling FLTFS control file with the get riposte point file system control code Again, this will read the actual content of the MFT attribute itself the riposte point data Now here the mini filter faces two options The first option is if the riposte tag that is located in the riposte data header is not associated with it In this case it will ignore the request and will leave it to the drivers above it The second option is if the tag is associated with it the driver then will know how to read and read the riposte point buffer and extract the correct destination path from the riposte point itself and And in this case the mini filter usually replaces the request file object using IO replace file object name with the FLTFS set callback data dirty the combination of these two functions will cause the IO manager to then repulse the name in the file object and then pass the request back down this time with the correct path so eventually the Destination path will be the destination file. So we will be returned to the caller Okay, so if we go back to containers In order to avoid an additional copy of OS files each container is using a dynamically generated image Which points to the original using riposte points The result is images that contain ghost files, which basically store no actual data, but point to a different volume on the system When looking at a mounted container volume We see that the windows folder contains filing the size of 1.2 gigabyte But has the actual size on disk of only 57 megabytes again This is happening because files on the containers volume doesn't contain the actual doesn't store the actual content There are simply links to the original files on the OS the OS file system And as stated the these links are implemented by riposte points Okay, so it was at this point that the idea struck me What if I could use this redirection mechanism to obfuscate my file system operations and confuse security products? The road that I chose was not to escape the container from within but intentionally use this feature while executing on the host Okay, let's start talking about the WCIFS driver The Windows container isolation file system or WCIFS Is the minifilter driver which is responsible for the file system separation between Windows containers and their host will soon see exactly how During my research, I was surprised to find that this driver is loaded on every Windows OS Starting from Windows 10 including servers by default This is true even with the containers option is turned off in the Windows features menu Meaning any potential abuse of this driver would affect most of today's modern system without relying on any third party files or packages installed We saw that riposte points are parsed and handled by minifilters based on their tag WCIFS parses riposte points as well, and this is how it performs the ghost files redirection process The main riposte tags associated with this driver are IO replace tag WCI sorry IO riposte tag WCI 1 And IO riposte tag WCI link 1 which according to the Windows documentation are used by the Windows container isolation filter Server interpretation only not meaningful over the wire. So Microsoft doesn't give us anything here Due to the due to the significance and frequent mention to our the remainder of this presentation I will now reference these tags as WCI 1 and link 1 Okay, so after some basic reverse engineering I managed to map both of the points internal buffers which were the same by the way The buffer itself is pretty straightforward. It contains contains a GUID which is a hard-coded value I found I found on any of the WCI tags and the path to the destination file. So nothing special here and At this point all that there was left for me to do was to attach a WCFS rather into a to a volume debug it using a debugger and Place a break point on its post operation call back to see how the riposte points are being handled And how can I potentially abuse it? So I've placed a break point and I've waited and waited and waited and nothing happened So it turns out that for the post operation call back to invoke the pre operation call back Must return either FLT pre-op success with call back or FLT pre-op synchronized which didn't happen So looking at the pre create Function of the WC driver. I saw that there was a function blocking me this function called WC unions exist for instance Stepping into this function. I saw that there were two conditions that had to be met The first condition is a check of whether the process that originated the create call is not within the host the host silo Host silo is the equivalent to the host OS or in other words if the originated process was not inside a silo at all Simply being inside the silo is not enough because the second condition is that the silo that originated request will have a registered context within the drivers internal collections Context management is another feature provided by the filter manager a Mini filter can create custom defined data also known as union context and link it to objects like files instances and silos using the FLT API So to pass the pre create checks of the WC driver. I had to do the following First create a silo and insert my process into it Then some of inform my the driver my silo is representing a container So it will create a union context for it and handle it accordingly The first requirement is not a difficult to fulfill because in order to create a silo first need to Create a job using create job object a and give it a name Then convert it to a silo using set information job object with the job object a silo flag This flag is undocumented by the way And finally assign our process into this new silo. So overall three API functions pretty easy The second requirement however is a bit trickier to communicate with the minifilter driver the Filter manager offers the FLT send message function This function receives a buffer without a specific structure and simply passes it to a handler function within the driver for further processing In order to be the valley structure the driver will accept I had to reverse engineer to get message handler function in the driver's binary So I've examined the function and with this I managed to map both of the sorry I managed I managed to map the Structure that will register a silo as a container It turns out that there are several functionalities the driver offers to its user mode clients With the effort is a message function the one that interests us is the code one message or the set union message It's that I will contain the following fields the notable ones are the number of unions where each union is Representing a volume the container will work with the name of the drivers instance Both of the ripar stuck and ripar stuck links and a handle to our silo At the end of the stock there are two extra structures again one for each volume the container will work with The first one is the volume union structure. It has a A g oid stores a g oid which is the same g oid found on the ripar's points buffer a Boolean that will represent if this volume is the source volume or destination volume and an offset to the second structure Which is container root id which will store the Name of the volume container root id is just a different name. I founded the driver symbols to a simple unicorn string So it's just a simple string To simplify this let's see an example of a valley structure the driver will accept and will register our silo as a container again We have the set union message a code one message The internal buffer will have two volumes the source and destination volume and both of the WCI ripar stocks a Handle to our silo and both the source and target volume unions and source and target container root IDs Something interesting to note here. I haven't seen any validation of whether the source and target volumes are the same This means that in theory it is possible to create a container that will redirect to the same volume now This is interesting and remember this because we'll use this at Later Okay, so now that we have fulfilled both requirements of the pre create function our post-grid will invoke allowing us to analyze How the driver handles its ripar's points The first time we look at is the link one tag And as you might guess this tag acts as a regular link between two files It is usually placed on files that should be open with three primitives only for example system files The driver will read the destination file form the ripar's point buffer and will redirect the call to the volume the Container direct to using I replace file object name Let's see an example Let's say we have an open process that is inside a container This container redirects IO calls from the containers volume how this volume five in our case to the host file system the host volume The noble process opens a file with three primitives and The WC driver will see the status repos on the IRP and will see the link one ripar's tag Which it's responsible for so it will read the content of the ripar's point from the MFT attribute And will extract the relative path of the destination file Then it will open a handle to the destination file on the destination volume again the Host volume and Then it will return this handle to the notebook process now note that in this case the notebook process got a handle to a file That is inside that is outside the container dynamic images inside on the host volume now this is safe because the The handle that is given to the processes with read primitives only so it cannot change it that cannot and cannot affect The files outside of its container the second tag will look at is far more interesting According to the driver symbols the WCI tag is responsible for file and directory expansion Expansion is this driver's definition to copy an open protection This tag is placed on files that can be open with write primitives and can be edited When encountering the WCI one tag the driver saves the ripar's data in the files object context and launches a work item that further handles the request This work item copies the file into the source volume the containers ghost image So it will edit a copy of the file instead of the original using the FLT read file and write file functions So going back to the same example as before this time the notebook process opens a file with write primitives meaning it wants to edit its content The driver again will see the status repulse and WCI one tag Get the relative path of the destination file from the ripar's point itself This time it will read the content of the destination file from the host volume Note that the file must exist and then it will write this content to the Containers volume the original file that had the ripar's points and return this handle To the notebook process now this time the notebook process got a handle to the file It was originally requested But the file is not a ghost file anymore It has the actual content of the file the destination file the file that it was Directing to so the notebook process can edit it without affecting the original file on the host volume Now I've stated that the destination file must exist in the case It doesn't exist the original file that contained the ripar's points will be deleted Let's see this in this in the driver's decompiled code It tries to open the ripar's point and if it gets back status object name not found meaning the destination file doesn't exist It will then allocate a new IRP using FLT allocate callback data and will set the major function code of this IRP to set Information alongside if I just position delete flag This will cause the original file to be deleted and it will invoke the this new RP using FLT performance in Cornus IO Again same example note the process opens the file with right primitives WCFS gets the relative path of the destination file and this time when it reads the content of the destination file It gets back status object name not found because the file doesn't exist So it will delete the original file On the containers volume and will return to the notebook process error invalid handle because the file it was requested Doesn't exist anymore was deleted an additional functionality provided by destroy There is the ability to copy and paste files without having to bother with entering a container or dealing with ripar's points This is used when files are needed to be transferred between the container and host volume So let's say we have a file on the containers dynamic image and we want to save it on our host volume You can do you can use these from this functionality by the WCFS driver to do so the copy and paste operations are done using again the FLT read file and write file functions The same function as the WCFS one ripar's tag To do this we again send a message to the driver using FLT send message this time with message code form the content of the The message data will contain the source and target file paths and source and target volumes Pretty simple nothing interesting here Okay, let's summarize what we've learned from reverse engineer the WCFS driver We managed to create a silo insert our process into it and register it as a fabricate as a fake container by communicating with the WCFS driver This allowed us to resolve how the driver handles its ripar's points using the link one tag We're able to open one file and receive a handle of another using the WCI one tag we're able to Overwrite a file with the content of another or delete a file and By directly communicating with the driver who also able to copy and paste files So let's see how what we've seen can be utilized File system protection is an essential feature any idea must provide and security vendors tend to use their own Minifilterizers to monitor the system's IO activity. We'll soon see exactly how In the previous section we saw that the WC driver uses FLT Read file write file and performs the conus IO to perform IO operations from within the kernel Now there is something special about these functions looking at the MSD end of communication of all of these Three functions will see the following remark The function will cause the request to be sent to the minifilter driver instances attached below the initiating instance and to the file system The specified instance and the instance as attached above it do not receive the request We know that Minifilters are attached and ordered by their altitude values This altitude range is split into groups and each group is associated with with a certain type of minifilters for example 320 to 330 thousand is the range for security vendor strivers and Going back to where WC driver is loaded. It is lower at 190,000 and can be even lower if we attach it manually Which will cause EDR minifilters to simply not be notified about any of our IO operations Done using these three functions from within the kernel Let's visualize this by using the same example as the beginning of the talk this time The file that is being opened has the WCR one repost point So again the IRP will create So the IR manager will create an IRP for this operation Which will go down the storage device stack until it will reach the filter manager The filter manager will then invoke the pre-create operation callbacks of all of its minifilter instances Note that this includes any EDR minifilter So it will receive a notification of our process attempting to open a file with a repost point Which we don't really care about we'll soon see why The IRP again will then be returned to the filter manager the file will be read NTFS driver will change the status of this IRP to status repost and the request will eventually reach the filter manager again Where it will invoke the post-create operation callbacks of the WC driver In this function the driver will notice the WCR one tag Which is responsible for and as we've seen it will either override the source file with the content of the target file Or it will delete the source file in the case the target file doesn't exist These functions will cause a new IRP to be created and go down the same path down the storage device stack until it will reach the filter manager again the same as any IRP This time over if we will go back to the remark from the previous slide Any instances attached above the originating instance our WC driver Including any EDR minifilter instances will not receive a notification about this operation Now the IRP again will be a go Sent and back down go back up and when it will reach the filter manager again The remark will apply to the post create callbacks as well Causing the post create of any instances attached above the WC driver to not be notified Including any EDR minifilters Eventually the call will be returned to the WC driver when it will return FLT post-top finished processing This will cause the entire operation to be completed Causing the file to be repulsed and as we've seen any internal operations done using the doubt that the WC driver will not be notified EDR minifilters will not be notified about about it Okay, so in order to know how this behavior can be abused we first need to understand how security products implement their protection To combat file system based malware these products leverage advanced algorithm that analyze minifilter Iologs actively searching for specific patterns to detect and prevent any potentially reversible damage Most EDRs rely on on a standard set of principles to identify processes associated with around some error wipers These principles include monitoring processes that open a significant number of file handles and exhibit behaviors Such as reading data from a file and overriding it making the original data inaccessible Knowing this let's create an undetectable wiper and ransomware using the WC driver. We'll start with the wiper because it's a bit simpler We'll first need to create an empty file that will be our target file and then write a buffer of zeros or random data to it It doesn't matter Then we'll traverse each file on the system and for each file We'll set an IO WCR one repos point that will point to the target file the file that contains the zero dot buffer Then we'll create a silo assign our current process into it and register it as a fabricated container Where both the source and target volumes are the same one the host volume We'll then traverse each file on the system again This time we'll simply try to open the file using create file now as we've seen this will cross the WC driver to read the Content of the destination file and write it for us on the file that we want to wipe Without regaining any security minifigure drivers callback functions Let's visualize this Again, if we try to directly wipe the file EDR algorithms will eventually detect and prevent us instead We'll set the WCR one repos point that will point To the only source files that want to wipe that will point to the target file that contains the zero dot buffer And we'll insert our wiper into a container Then we'll try to open the file that want to wipe the WC driver will then read the content of the destination file and write it for Us thus wiping the file without the detection of EDRs Okay, the ransomware algorithm is Pretty much the same It we first need to traverse each file on the system and for each one will read its content and encrypt it in memory We'll then create a target file and write the encrypted data to it Note that security minifigures will be notified about the file right, but we'll ignore it because the encrypted content is Rented to is written to a new file on the system and an existing file is not being overridden Then we'll set again a WCR one repos point that will point to the target file that contains the encrypted data Get a silo assign our process into it and register it as a fake container Traverse each file on the system again and try to open it using create file causing the encrypted data to be written by the WC driver Let's see this on some in real time Here we have two folders containing the exact same files and has the same size on this We have the secrets folder and the secrets copy folder We will first try to encrypt the first folder the secrets the secrets folder with a traditional ransomware That simply opens encrypt and write the encrypted data to the same file the ransomware starts to encrypt some of the files, but Eventually getting detected and terminated by the edr installed on the system Looking at the secrets folder the one that we wanted to encrypt You can see that some of the files were encrypted, but not all of them For example this test file was not encrypted. So the ransomware was prevented on time We'll then execute our ransomware that uses the WC driver encrypting the second folder the secrets copy folder So it first sets a repos on the files Then encrypting them using the WC driver and as you can see the process was not terminated and looking at the secrets copy folder You can see that all of the files were encrypted including our test file Another notable feature of security vendors products is the ability to block file system write operations This can be utilized by organizations to either enforce a read-only policy for removal devices Thus effectively mitigating the risk of data exfiltration Or to block file writes to folders housing sensitive data, which will add an extra layer of protection against unauthorized modifications This protection is implemented by you guessed it a minifilter diver, which we can bypass as well Let's see another demo This time we have a different idea that before a Usb stick is connected to the system and when trying to copy a file to the usb stick The operation is being blocked by the edr due to a read-only policy But when copying the file using the copy and paste feature of the WC driver So we copy the file to the e volume the usb stick The operation is completed successfully and the file can be extracted from the system And this is how you can actually take the most precious file of all the calculator Okay, itw Etw or event testing for windows is a powerful and efficient logging mechanism built into the windows operating system The windows kernel serves as a crucial lock provider that captures a wide range of system operations Including those related to the file system Security vendors leverage these events to analyze and identify potential threats often creating attack flows by cross-referencing Now if you recall the WCI one tag override process the read and write operations occur within a kernel work item Executing former work item, which is a kernel thread will cause the Etw log to attribute these actions to the system process instead of the actual process responsible This will lead to misinformation for any vendor consuming events number 15 and 16 of read and write from the kernel file Provider bypassing any threat hunting correlation based on these events an example to a built-in windows tool that is Etw based is s a cl Windows offers the capability to establish an auditing policy for file system objects knowing known as system access control list or s a cl this allows for extensive logging of all I operations perform on the specified objects Etw based windows tools Intentionally designed to disregard logs originating from the system. This approach guarantees that such logs Which are typically relevant to the user monitoring the system are not included to avoid unnecessary overhead The result of all of this is that our I request would be absent from the logs altogether Time for another demo Here we have a secret file that has a read and execute auditing rule which will log any access to the file So looking at the auditing policy of this file. You can see that we have a read and execute rule on any user on the system Opening this file with note before example will cause a log to be generated by the auditing policy So looking at the event view or the auditing logs You can see that logs were created stating that the secrets file were accessed by the Norbert process However, if you will create an empty file that will store the content of the secret that we want to steal Then set the WCR1 tag that will point to the actual secret file and Overwrite it using the WC driver So we set the reverse point and then overwrite it The kernel work item will read and write the files content for us without any log being created by the auditing policy So the content of the secret was stolen, but looking at the auditing policy You can see that no logs were creating stating that the secrets the original secret was accessed Okay, let's summarize We've learned that The windows container frameworks the windows containers framework provides a file system isolation solution that is implemented by reverse points and many filter drivers Through reverse engineering of the main driver the main driver of the framework The WCFS driver were able to create a counterfeit container and successfully insert our process into it This allowed us to leverage the frameworks IOE direction mechanism causing files to be overridden deleted or copied without the detection of security products As a result, we have identified new bypasses of five system-based model protection file write restrictions and EW based correlations without relying on any other known bypass techniques So how to mitigate the bypasses? There are numerous routes you can take I've found that the easiest ways by detecting the suspicious activity in the user mode and not the kernel mode for example detecting calls to device IO control with the set reverse point five system control code alongside the Any of the WCI one tags from what I've seen this activity as Should it should not happen under a normal container operation is pretty much an anomaly. So it looks suspicious You can also check whether the WC communication port was open or a silo is created by non-sister process Or to check if the WC driver is attached to a volume while the containers features on off again keep in mind These are just a few ways to detect the bypasses. There are plenty of other methods you can take as well for example from the kernel itself So these are just a few Okay further research The WCFS driver is just one of many many filters out there I'm sure that there are plenty of other many filters that can be abused in a similar way to perform some system operations from the kernel itself It is also possible to set reverse points on directories The driver symbols reference director expansion and possible redirection handling. So this is an interesting route you can take and most importantly Minifilter and ETW based protection is everywhere and I'm sure that there are more bypasses that can be achieved using the methods shown today You can contact me on if you have any questions you can contact me on Twitter or X or whatever it is now or you can scan the QR code to access the POC toll of the Shown on the demos today. Thank you