This is Jonathan Lim and Ateeq Sharfuddin, and in this blog we share a project we are open sourcing: SCYTHE's standard Windows C In-memory Module Loader. We provide a brief overview of loaders and share how we load our capabilities with the community.
There are three well-known mechanisms a program can choose to use other software [3]: static linking, dynamic linking, and dynamic loading. In Windows, dynamic linking and dynamic loading are handled by the Windows loader, and are done at load time and runtime, respectively.
For dynamic loading, loaders take a shared library and import it into a program’s memory, allowing the program to invoke capabilities available inside the library. When a program is done using these capabilities, the loader can be asked to unload the library from memory.
We’re most interested in dynamic loading, which allows a program to load and use a shared library at run time because it leaves a smaller footprint.
SCYTHE emulates different classes of threat-actors. Different classes of threat-actors have different motivations, and therefore, produce and utilize different types of malware to complete their goals. For example, Nation-states are the most sophisticated threat-actors and go through great lengths to ensure the end user is not alerted to their intrusions. As a consequence, whereas cyber-criminals and less sophisticated threat-actors might not be so concerned about leaving artifacts of compromise behind, malware produced by nation-states aim to leave no artifacts behind and have built-in security that hinders forensic analysis. One method nation-state threat-actors use is to keep much of the running malware only in volatile memory, for example, a system’s RAM. This way, when the system shuts down or is restarted, there is no disk artifact of the intrusion as state is lost in volatile memory when power is removed from the system. Dynamic loading capabilities aid adversaries in this scenario.
Shared libraries such as Dynamic-Link Libraries (.dll files) on Windows or Shared Object Libraries (.so files) on Linux can be dynamically loaded at runtime. As a simplification, Windows API provides a C function LoadLibrary(FilePath) to load a shared library into a process’s address space. The C function GetProcAddress(ModuleHandle, FunctionName) can then be used to retrieve the address of an exported function (or variable) in the shared library. If the function is found, the application code can now call this function. Finally, the application can call FreeLibrary(ModuleHandle) to unload the shared library from the process’s address space when no longer in use [1]. In the POSIX standard API, the equivalent C functions are dlopen, dlsym, and dlclose [3], respectively.
Although official documentation exposes APIs where the shared libraries must be files on disk, research has been published [2] that demonstrate that shared libraries can in fact be dynamically loaded directly from memory and reference implementations exist. These research provide details necessary to reimplement the operating system’s loader and offer three replacement C functions: LoadLibraryFromMemory(ModuleBytes), GetMemoryProcAddress(Handle, FunctionName), and FreeInMemoryLibrary(Handle). With such a reimplementation of the operating system’s loader we can load the shared library directly into a process’s address space.
In an adversarial scenario, the shared library would be transmitted over the wire into memory and a loader similar to what we are sharing would load this shared library from memory without any need to write it to disk first.
The in-memory loader goes through a series of steps that emulates a Windows loader except it allows libraries from the network:
Source code is available in GitHub under: https://github.com/scythe-io/memory-module-loader.
Notable features of this loader:
This loader is derived from John Levine's Loaders and Linkers (ISBN: 1558604960).
This particular loader is a fork from https://github.com/fancycode/MemoryModule circa summer 2016.
The solution contains source code for the loader. The source code is compiled into a static library (.lib). A sample DLL is provided, as well as an executable, which when invoked reads this sample DLL into memory and then loads it and calls a function inside it.
Loading is a key concept in allowing programs to use other software as shared libraries. We explored how loading capabilities dynamically can be done from memory alone, without leaving behind disk artifacts. SCYTHE emulates various types of threat-actors and some such as nation-state actors require having a small footprint to avoid traceability. There are many other methods that are used by threat-actors to hide their presence but hopefully this gives you an idea of how dynamic loading can be used for unintended purposes.
[2] skape and Jarkko Turkulainen. Remote Library Injection, 2004.
[3] David A. Wheeler. Program Library HOWTO, 2003.
SCYTHE provides an advanced attack emulation platform for the enterprise and cybersecurity consulting market. The SCYTHE platform enables Red, Blue, and Purple teams to build and emulate real-world adversarial campaigns in a matter of minutes. Customers are in turn enabled to validate the risk posture and exposure of their business and employees and the performance of enterprise security teams and existing security solutions. Based in Arlington, VA, the company is privately held and is funded by Gula Tech Adventures, Paladin Capital, Evolution Equity, and private industry investors.