CSIS RESEARCH - Windows Covert Compilers II

Tech Blog
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.


In-the-Wild sample

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.

Using Add-Type is not a requirement though, as we can see in a malicious PowerShell script, described by Xavier Mertens [1].

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.

Obfuscated PowerShell codeFigure 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.

Dropped files

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.

Temporary files created during compilationFigure 2: Temporary files created during compilation

PowerShell creates the following files:

Extension

Description

.cs, .vb, .js

Contains the source code itself

.cmdline

Contains the command line passed to the compiler

.out

 


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 Microsoft.JScript.JSInProcCompiler which Add-Type uses too.

GenerateInMemory

As we have seen above, quite a lot of file operations are done behind the scenes when assemblies are created using System.CodeDom.Compiler.

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, csc.exe, vbc.exe and 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."

References

[1] X. Mertens, New Tool to Add to Your LOLBAS List: cvtres.exe, 2021.