Praetorian has developed a custom YAML-based domain-specific language (DSL) to allow operators to specify red team dropper behavior. The YAML-based DSL works with a custom compiler we've named Janus, which handles converting the YAML representation to a fully-functioning dropper. The dropper generator's purpose is to provide operators more control over the runtime behavior of payloads at execution time. For example, a red team operator may wish to swap out the code injection mechanism used to dynamically increase or decrease the sophistication of the payload depending on the target environment's maturity level.
The motivation for Janus stems from the GRASSHOPPER payload generator described within the Vault 7 leaks , which detailed a modular payload generator that provided operators with extensive control over the resulting payload generated by the tool. The GRASSHOPPER tool detailed within these documents allowed pre-execution checks to be evaluated before performing a malicious action.Additionally, the tool supported swapping out tactics, techniques, and procedures (TTPs)  that could trigger detection by a security product on a host (e.g. persistence).
The payload generation process is performed to generate a payload consisting of several components. For example, this could be a completed macro-enabled document containing an embedded dropper responsible for loading an implant. Typically when we think of and describe a payload, we like to break it down into three distinct parts detailed below:
An analogy in this scenario might be similar to that of an operating system’s boot process. The artifact is analogous to the system bootloader. Its purpose is to perform the minimum steps required to load the operating system kernel (analogously, the dropper). Finally, the operating system kernel is responsible for performing the appropriate pre-boot initialization checks and startup tasks before launching the system initialization process (similar to the implant).
We’ve found that logically decomposing the payload generation process into three separate components provides us with a strong mental model for building flexible modular dropper and artifact generators. In some instances, the dropper and artifact are the same. For instance, we might perform lateral movement by dropping a DLL to disk and leveraging DLL hijacking to execute code on a remote host. In this case, the dropper and the artifact would be the same file. In another scenario, we might utilize a macro-enabled document to execute MSBuild.exe. In this case, the dropper would be the MSBuild XML file, and the artifact would be the macro-enabled document. Internally, we decompose the dropper, implants, and artifacts to further sub-components to support additional customization.
Janus is an internal tool we have developed to generate DotNet droppers through a custom YAML-based domain-specific language. The Janus tool handles taking in a user-specified YAML file and set of modules or plugins as input to generate a final payload consisting of the requisite modules merged with code inserted to perform the user-specified logic.
In its current state, Janus only supports generating DotNet payloads. The reasoning behind this is that the DotNet intermediate language (IL) provides an extensive amount of metadata and other information to manipulate and merge assemblies. This is by design, as the intermediate language must include enough detail to allow the DotNet runtime to convert the IL to native code. Furthermore, DotNet provides a comparatively powerful degree of control over the generated output file types, including support for mixed-mode assemblies used to generate DLL exports that can be called from native code. Additionally, an extensive set of third-party libraries exist to support the modification of DotNet assemblies such as Mono.Cecil, allowing for rapid development and prototyping.
In its current state, improvements to DotNet runtime instrumentation and the increasing focus on DotNet by defenders have not meaningfully impacted our ability to conduct operations successfully. However, in the future, we plan to diversify our payload generation tooling to include native code generation capabilities in anticipation of further improvements of defensive capabilities. This topic is further discussed under “Future Work”.
The Janus compiler features an extensible modular architecture allowing operators to drop in a new module to, for instance, add support for a new type of code injection method within the dropper generator. Operators with the plugin installed can then reference that plugin within their YAML markdown file (known as the Janus Descriptor Language file or JDL file).
The Janus compilation process is broken up into distinct steps including lexical analysis, parsing, semantic analysis, dependency resolution, assembly merging, code generation, and obfuscation. These steps are described in-depth below:
Janus supports a wide variety of module types to support flexible customization of the dropper. These module types are described below:
In this section, we walk through an example Janus Descriptor Language (JDL) file used by an operator during a red team operation. The JDL file consists of several subsections, including the attributes, files, entry, metadata, and rules sections. A summary of these sections is given below:
To start, we begin by examining the attributes section of an example JDL file, as shown in the snippet given below. The primary two items within this section are the format and architecture attributes. The format attribute is used to specify the required output format of the module. In this example, we are using the comikaze module, an output format that implements compatibility with LOLBINS such as RegAsm.exe and RegSvr32.exe, which expect a valid COM object to successfully execute the payload.
-- CODE lang-xml --attributes:
obfuscate: true # (optional attribute)
The architecture attribute specifies the required architecture of the generated assembly. While it is true that DotNet assemblies are compiled to an intermediate language, in a similar way to Java, DotNet supports the concept of mixed-mode assemblies, which can be used to embed native code alongside a generated assembly. This allows DotNet to support exporting functions through the export address table which can be invoked by native code. In Janus, we use the dnlib library to implement function exports through the generation of mixed-mode assemblies. In this JDL example, we are specifying the x86_64 value meaning that the generated DLL for the COM object should be 64-bit. This is required because the COM object must support exporting the DllRegisterServer and DllUnregisterServer functions to support being loaded as a COM object by native utilities.
The files section defines all files referenced by the dropper that need to be bundled within the generated dropper payload. This is where the operator would specify how an embedded implant loaded by the dropper should be stored, and what input transforms should be performed against the file during the embedding process.
-- CODE lang-xml --files:
- host: somecdn.com
- domain_sid: S-1-5-21-012345678-0123456789-0123456789
In this example, we are specifying a file containing shellcode called implant.bin with a storage method of “array”. This means that the file will be embedded as a byte array within the generated dropper. A series of transforms are specified by the user on how the file should be encrypted. First, the data is encrypted with a randomly generated AES key embedded within the dropper. Next, the payload is encrypted using the domain SID of the Active Directory domain associated with the target environment, a key retrieved from a remote keyserver via domain fronting, and then finally encrypted with a final random AES key. In future posts, we plan to discuss how we leverage environmental keying along with our keyserver during red team operations.
The transform parameters are used to pass information to the input and output transforms. For instance, in the snippet below, we specify that the keyserver module should perform domain fronting to retrieve the key and specify the domain SID the key should be encrypted with. The domain SID string is not hardcoded into the binary but derived from the environment during runtime.
The entry section specifies the entrypoint function of the JDL file. In this case, we are using a single-statement which specifies that the code embedded within the dropper should be injected into the calc.exe process with a spoofed parent process of explorer.exe along with the BlockDlls mitigation enabled on Windows 10 systems. At compile time, the compiler inserts code that invokes the output transforms in the inverse order of the input transforms (this is done because the input and output transforms represent a pair of invertible functions). The code is then passed to the injection module which invokes the CreateRemoteThread module which is responsible for handling the code injection.
-- CODE lang-xml --entry:
The metadata section is responsible for modifying the attributes embedded within the resource section VERSIONINFO of the generated assembly.
-- CODE lang-xml --metadata:
- FileDescription: Distributed COM Services
- InternalName: rpcss.dll
- OriginalFilename: rpcss.dll
The rules section allows us to specify runtime checks which can be performed because the entrypoint function is executed. In this case, we configure the dropper to check the number of processors, ensure that Wireshark is not running, and that the current user is not “administrator”.
-- CODE lang-xml --rules:
- at_least: 2
- does_not_exist: wireshark
- does_not_match: administrator
Future work will be focused on adding support for native code generation in a similar manner to what is currently implemented within DotNet code. Unfortunately, native code generation is a bit more complicated as compiled object files typically lack much of the metadata included in DotNet assemblies. Additionally, there is a dearth of tooling available for performing such in-depth modification of compiled object (COFF) files. While we believe that developing such a capability is possible, the time requirement would be significant due to the development overhead of building out the supporting tooling (which already exists for DotNet).
We are also developing enhanced capabilities for dropper telemetry, runtime error handling, and recovery we plan to cover in more depth in future articles. For instance, we are implementing broker processes that monitor the payload during execution to detect if the injection process was terminated by security software. Additionally, we are working to develop an emergency response capability into our droppers which provides a rudimentary shell to remotely debug a failed implant along with a “crash reporter”, to collect system information for diagnostics and debugging purposes. This functionality will increase the probability of success within future droppers when an execution unexpectedly fails or even launch an alternative dropper via an emergency shell.
Janus provides a flexible system for generating dropper payloads that allow operators to exercise a high degree of control over the payload’s behavior. It allows operators to modify the behavior of a dropper without performing extensive code modifications or even opening Visual Studio. This allows operators to create highly sophisticated droppers in minutes as opposed to hours or days. Furthermore, operators specializing in network operations who lack the requisite development skills can easily create highly sophisticated payloads independent of the capabilities development team. The modular architecture makes it easy for developers to quickly add modules to, for example, support a new code injection technique while eliminating redundancy from the codebase.
We are constantly working on improving and tweaking red team payloads to keep pace with the defensive security landscape. As new technologies are developed or encountered during operations we are continually upgrading payloads to keep pace with modern security technologies.