Windows Covert Compilers II.
UNDER THE HOOD
Dénes Olivér Óvári, Detection and Response Architect
CSIS Security Group A/S
A three-part blog series focusing on the Digital Forensics and Incident Response aspect of the compilers shipped with the Windows operating system and their usage from PowerShell.
- Part I: C#, Visual Basic and JScript – all within PowerShell
- Part II: Under the Hood
- Part III: DFIR Artifacts
The example shown in the previous post uses the
Add-Type cmdlet (implemented in
Microsoft.PowerShell.Commands.Utility.dll) to create an instance of a class from C# source code. A method of the instance is then callable from PowerShell to create a message box.
The sample attempts to perform process injection. The necessary functions from
Kernel32.dll are wrapped into a class made in C#. Another piece of PowerShell code is used to produce a .NET assembly (Figure 1), which in fact relies on the very same methods
Add-Type would call if it was used instead.
Figure 1: Obfuscated PowerShell code
Ultimately, the assembly is instantiated as an object, and attempts to inject a binary payload.
Implementation (.NET Framework)
As mentioned previously, PowerShell depends on external compiler executables if it needs to deal with objects implemented in other programming languages.
These compilers are stand-alone command line tools made to create binary files from source code according to the received parameters. A robust (though maybe somewhat old-fashioned) way of passing data to and from a subprocesses is done via temporary files, and it is just what PowerShell’s implementation does.
A class called
TempFileCollection is used to keep track of some of the files created in the
%TEMP% folder, and to eventually delete all of them once they are no longer needed.
The class generates a random 8-character alphanumeric string for each "collection" and uses that as a name for each of the files it contains, only the file extensions differ (Figure 2).
.NET Framework version 4.7.2. and higher includes measures to protect temporary files created by elevated processes. For this feature to work, the "collection" must have its own subdirectory, if the process using it is elevated.
TempFileCollection uses the same random string as the name of this subdirectory:
%TEMP%/xrplyns2 for example.
Figure 2: Temporary files created during compilation
PowerShell creates the following files:
Contains the source code itself
Contains the command line passed to the compiler
PowerShell then executes one of the compilers, for instance,
csc.exe to create a DLL from the dropped source code. During the linking phase,
csc.exe itself executes
cvtres.exe and creates two additional temporary files to pass data.
If the compilation is successful, the linked assembly is loaded from the newly created DLL file and is ready for use.
There is a simple "type cache", which means that
Add-Type will not compile the same piece of source code again, as long as a type created with it still exists in the particular PowerShell session.
The JScript implementation is different. Although there is a working
jsc.exe binary alongside the two other compilers, it is never executed by .NET when compiling JScript. Only
cvtres.exe is required.
jsc.exe in fact simply wraps the same
Add-Type uses too.
As we have seen above, quite a lot of file operations are done behind the scenes when assemblies are created using
There are plenty of parameters to choose from when this namespace is used directly.
GenerateInMemory seems particularly interesting from a DFIR viewpoint. It is in fact used by the sample shown previously too: it is set to
true (Figure 1). According to the documentation, the value of this property (unsurprisingly) controls whether the output of the compiler should be generated "in the memory".
This might infer that there won’t be any files created on the storage, but regardless of the setting, and if external compilers are needed to be called during the process,
TempFileCollection will generate its files, and the output of the compilation will nevertheless appear in the temporary folder for a short time.
Being phased out
.NET Core does not include compilers in its base installation, hence the behaviour described previously won’t be observed in PowerShell versions 6 or 7 either.
However, at the time of writing, PowerShell 5.1 still ships by default as a part of Windows, and its files remain even if the newer version gets installed. They run side-by-side.
It should also be emphasised, that being parts of the of the .NET Framework,
jsc.exe could be used without involving PowerShell at all!
Thanks to my colleague, Conor Kelly for his review.
"Jscript, Microsoft, PowerShell, Windows are trademarks of the Microsoft group of companies."
|||X. Mertens, New Tool to Add to Your LOLBAS List: cvtres.exe, 2021.|