Every operating system lets a process load shared code at run time. T1129, Shared Modules, covers adversaries using that feature to execute payloads. It belongs to the execution tactic and applies to Linux, macOS and Windows.
Shared modules are executable files loaded into a process to give it reusable code, such as custom functions or calls into OS APIs. Legitimate software does this all day. The question for defenders is which module, from which path, loaded into which process.
How attackers use it
MITRE describes the pattern at a high level. Adversaries can modularize malware into shared objects that each do one job, such as managing command and control traffic or carrying out actions on the objective. The main program then loads them when needed.
The loaders differ by platform:
- Linux and macOS. The module loader can load shared objects from arbitrary local paths, through functions such as
dlopenanddlsymindlfcn.h. macOS can execute.sofiles, but.dylibis the common form. - Windows. The loader can be told to load DLLs from arbitrary local paths and from UNC network paths. This lives in
NTDLL.dlland is reached through calls such asLoadLibrary.
The MITRE page lists many malware families that use this approach, including Astaroth, Bumblebee and Ebury, which shows how common it is.
How to detect it
MITRE gives one behavior-chain strategy with a variant for each platform.
- Windows. A process, often a living-off-the-land binary or a user-launched program, loads a DLL from a user-writable, UNC or temp path, or one with an unsigned or invalid signer. Shortly after, the DLL is newly written to disk, spawns follow-on execution such as rundll32 or regsvr32, or opens outbound command and control.
- Linux. A process loads a
.sothroughdlopen,LD_PRELOADor similar from a non-standard or temporary location such as/tmpor/dev/shm, especially soon after the file was written or fetched, or via alteredLD_PRELOADorLD_LIBRARY_PATH. - macOS. A process loads a non-system
.dylibor.sothrough dyld from a user-writable location such as~/Libraryor/tmp, or right after the library was created or downloaded, often followed by network traffic or persistence.
On Windows, Sysmon event ID 7 (image loaded) is the natural source, with event ID 11 (file created) to see when the DLL appeared. On Linux, auditd file and exec rules cover the temp-directory cases.
How to reduce the risk
MITRE lists one mitigation, execution prevention. Use application control that can stop unknown modules from being loaded. For Windows that means allow-listing by publisher or hash for DLLs as well as executables. Pair it with the habit of keeping user-writable paths out of any library search path you control.
Check your own environment
Linux, read-only:
cat /etc/ld.so.preload
env | grep -E 'LD_PRELOAD|LD_LIBRARY_PATH'
ls -la /tmp /dev/shm
The first file normally does not exist or is empty on most systems. Anything listed there is loaded into every process, so confirm you know why. Look in the temp listing for shared object files you did not expect.
On Windows, if you run Sysmon, review image load events where the loaded path sits under a user profile or temp folder.
Try it yourself
Shared modules are also how third-party code enters your own software. This demo shows a dependency inventory so you can see what your applications pull in.
Click through it at your own pace, or open it full screen.
For your own projects, the free in-browser dependency scan at gold.safeguard.sh/scan needs no login, keeps the file on your machine and covers up to 100 dependencies.
Quick check
Which Windows function family is named for loading DLLs from arbitrary paths?
LoadLibrary and related calls, handled by the Windows module loader in NTDLL.dll.
Why are /tmp and /dev/shm interesting on Linux?
They are writable temporary locations. A shared object loaded from there, especially right after it was written or fetched, matches MITRE's detection pattern.
Sources
- MITRE ATT&CK, T1129 Shared Modules