Endpoint detection and response (EDR) solutions offer a wealth of telemetry for Portable Executable (PE) files. It is common for these products to capture both the version information properties and code signature information of PE files. This information allows a defender to more confidently ascertain a base level of trust for the executable in question. How reliable is that level of trust, though?
For example, if kernel32.dll
loaded into a process, how can we be sure that it was the expected, legitimate kernel32.dll
that was loaded? For starters, we can consider the version info, code signature, and its file path:
- File path:
%windir%\System32\kernel32.dll
- Original filename:
kernel32.dll
- Leaf certificate subject name:
Microsoft Windows
- Issuer certificate subject name:
Microsoft Windows Production PCA 2011
- Signature status:
Valid
One could reasonably assume that the kernel32.dll
with the above properties is the legitimate kernel32.dll
, right? To answer that question, we would have to consider the extent to which the above metadata can be controlled by an adversary. With some notable exceptions, an adversary has a considerable amount of control over the “look” of their malicious executables, resulting in an opportunity to evade naive detection logic. For example, an adversary may relocate and rename legitimate tools in an attempt to evade detection which is why it is important to baseline the expected file paths of system utilities.
One of the things Red Canary’s Threat Research team strives to do when assessing the efficacy of vendor telemetry is to investigate the extent to which an adversary has control over the fields of a collected event. Knowing which fields are controllable serves to highlight the data with which we can place the most trust when developing detector logic. In the case of vendor-collected PE metadata, we developed New-ATHPortableExecutableRunner, which is included in our Atomic Test Harnesses module to challenge such assumptions. For more on Atomic Test Harnesses (and more adorable pups!), read these two blogs.
New-ATHPortableExecutableRunner
allows a user to build an EXE or DLL without needing to write any code and with full, granular control over version-info properties and signature information. It can also be used to clone these attributes from an existing PE file.
Using New-ATHPortableExecutableRunner
While a user can supply custom PowerShell code to the executables generated by New-ATHPortableExecutableRunner
, this is not a requirement. Supplying custom PowerShell code will spawn a sample powershell.exe
child process with a unique GUID in the command-line for testing and validation purposes. For the following examples, however, custom code that pops up a message box will be used for improved illustration purposes.
The following example demonstrates the creation of an EXE that pops up a message box, explicitly specifying version info and certificate properties. We uploaded an example of this generated executable to VirusTotal.
$Arguments = @{
FilePath = 'runner.exe'
OriginalFilename = 'foo.exe'
InternalName = 'bar.exe'
CompanyName = 'Contoso Inc.'
ProductVersion = '1.2.3.4'
FileDescription = 'Message box popup utility'
SignFile = $True
CertSigner = 'Contoso Inc.'
CertIssuer = 'Contoso Root Certification Agency (DO NOT TRUST)'
ScriptBlock = ({ [Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');[Windows.Forms.MessageBox]::Show('Hello!','WARNING') })
}
New-ATHPortableExecutableRunner @Arguments
A DLL is just as easy to create. The only difference is that you have to specify an unmanaged export function name. Here is a generated sample we uploaded to VirusTotal.
$Arguments = @{
FilePath = 'runner.dll'
Dll = $True
ExportFunctionName = 'RunMe'
OriginalFilename = 'foo.dll'
InternalName = 'bar.dll'
CompanyName = 'Contoso Inc.'
ProductVersion = '1.2.3.4'
FileDescription = 'Message box popup utility'
SignFile = $True
CertSigner = 'Contoso Inc.'
CertIssuer = 'Contoso Root Certification Agency (DO NOT TRUST)'
ScriptBlock = ({ [Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');[Windows.Forms.MessageBox]::Show('Hello!','WARNING') })
}
New-ATHPortableExecutableRunner @Arguments
rundll32 runner.dll,RunMe
Lastly, New-ATHPortableExecutableRunner
makes it easy to clone properties from existing executables. Here are uploaded VirusTotal samples of explorer.exe and kernel32.dll, respectively.
Get-Item C:\Windows\explorer.exe | New-ATHPortableExecutableRunner -FilePath explorer.exe -SignFile
Get-Item C:\Windows\System32\kernel32.dll | New-ATHPortableExecutableRunner -FilePath kernel32.dll -Dll -SignFile
How does New-ATHPortableExecutableRunner
work?
There are three primary components that constitute New-ATHPortableExecutableRunner
:
- Creation of a version info resource
- Creation of a self-signed certificate chain
- Assembly of a .NET EXE or DLL on the fly
Version info resource creation
Version info comprises a binary resource that is embedded within a PE file. It is an optional component that is useful for indicating file properties that are independent of the PE file format, including original filename, file version, and description. These fields may also be used as the basis for application control enforcement as is the case with Windows Defender Application Control (WDAC).
Typically, in order to generate a version info resource, an IDE like Visual Studio or a developer tool like Resource Compiler, rc.exe
, is required. We didn’t want to have a dependency on developer tools that are unlikely to be installed, however. Additionally, there is no built-in .NET class that can build PE resources on the fly. These constraints resulted in the decision to build the binary bit by bit, manually. Fortunately, with the help of a hex editor and good documentation, building the binary resource was accomplished without any hurdles that couldn’t be cleared.
Self-signed certificate generation
New-ATHPortableExecutableRunner
creates and replicates the fields of existing code signatures by using the certificate property cloning features of the New-SelfSignedCertificate
cmdlet. By default, the certificate chain that New-ATHPortableExecutableRunner
uses to sign the new executable will not result in a valid signature because self-signed certificates are not trusted as root certificates. Non-privileged users can, however, add certificates to their user-specific root chain, which can trick EDR solutions into reporting that it is a valid, trusted certificate chain. New-ATHPortableExecutableRunner
does not automate trusting the created certificate chain, as that is out of scope of the MITRE ATTACK® technique it aims to replicate: T1204.002: User Execution: Malicious File.
When New-ATHPortableExecutableRunner
is used to clone existing signature properties, the following fields are cloned:
- Subject name: Issuing certificate
- Serial number: Issuing certificate
- Certificate creation datetime: Issuing certificate
- Certificate expiration datetime: Issuing certificate
- Subject name: Leaf certificate
- Serial number: Leaf certificate
- Certificate creation datetime: Leaf certificate
- Certificate expiration datetime: Leaf certificate
.NET EXE and DLL assembly
We created New-ATHPortableExecutableRunner
with hopes that it could easily build either an EXE or a DLL with a specified export function. PowerShell makes it easy to build an EXE or a DLL on the fly with a cmdlet like Add-Type, but unfortunately there is no built-in method of specifying an export function. It is possible, however, to specify a .NET method as an unmanaged export function in intermediate language (IL) bytecode using the .export directive.
In order to get unmanaged export functions working, New-ATHPortableExecutableRunner
embeds pre-assembled .NET bytecode where it is updated based on the specified export function name and ordinal. Then, it drops the generated IL code to disk, along with the generated resource (.res
) file, and generates the desired executable using the built-in IL assembler utility, ilasm.exe
.
Conclusion
Hopefully, now you have a better understanding of the extent to which an adversary has control over the PE metadata logged by many EDR solutions. We encourage you to approach using PE metadata as a basis for trust with additional skepticism. Robust, durable detections focus more on behavior and do not have an overreliance upon PE metadata. Where New-ATHPortableExecutableRunner
replicates the “look” of an executable, it cannot, by default, replicate the “feel,” i.e., the behavior of an executable. However, since it allows for a user to supply custom PowerShell scriptblock code, behaviors can be replicated when PowerShell code is developed to do so.