What is a Shellcode Loader?
A shellcode loader is a program intended to inject and run a snippet of shellcode within a target process. Typical shellcode comes in the form of a small block of code, exploiting vulnerabilities to execute another code, normally used for engagements like penetration testing, red teaming, or an actual attack.
Loader Basics:
- Allocating Memory: The shellcode loader uses VirtualAlloc to allocate memory in the target process for the shellcode.
- Copying Shellcode: Functions like memcpy or WriteProcessMemory are used to copy the shellcode into the allocated memory.
- Executing the Payload: Finally, the loader executes the payload via techniques such as creating a thread or directly jumping to the memory location.
EDR Evasion Techniques:
Most modern security solutions, especially EDR, monitor process execution, memory changes, and network activity. To bypass EDR detection, an attacker will have to keep the telemetry footprint as low as possible. Given below are some of the major evasion techniques covered by Dobin:
1. Usermode Hook Patching: Most EDRs do hooking of generic Windows API functions in ntdll.dll, like VirtualAllocEx and WriteProcessMemory. The shellcode loader patches these hooks to prevent EDRs from intercepting function calls.
2. Encryption of Memory: The memory regions of RWX, which are not backed, may trigger EDR alerts. Encrypting the payload of shellcode supports bypassing memory scans conducted through EDR.
3. Callstack Spoofing: EDR systems depend on callstack analysis to carry out anomaly detection of processes. The return addresses in the process stack are tampered with in callstack spoofing to mask traces of malicious activities.
4. Execution Guardrails: Execution guardrails ensure that the shellcode runs only in particular environments, say, for example, the one the attacker has targeted. In this respect, the shellcode can look out for specific domain names, usernames, or installed software to confirm it's inside a correct environment before it actually executes.
The SuperMega Loader:
Dobin introduced the "SuperMega Loader," a proof-of-concept loader designed to avoid EDR detection. Key Points about SuperMega Loader:
- Use of Backed Memory: Only using backed memory-previously scanned by AV; loader will not create suspicious unbacked RWX regions.
- No RWX Memory: Instead of allocating executable RWX memory, the loader simply allocates RW memory and writes the shellcode into it; then sets the memory to RX.
- Minimum Telemetry: The loader does not perform direct networking or even access the file system, and hence performs minimum activities that could raise/create telemetry that is detected by EDR solutions.
Cordyceps Technique : Shellcode Injection with a Twist
The Cordyceps technique took the shellcode injection to the next level, as it made the injected shellcode appear like any other legitimate process. The two main novelties are:
- IAT Reuse: Instead of using the walk Process Environment Block (PEB) to resolve function addresses, the shellcode directly uses the Import Address Table (IAT), much like legitimate code.
- .rdata Reuse: Instead of interleaving data with the code-something that looks suspicious-the loader injects the shellcode's data into the targeted executable's .rdata section, making it look like it's a part of the original process.
Avoiding EDR Triggers: Deconditioning the System
Steer clear of the EDR Triggers-Deconditioning the System. One of the novelty ideas was EDR deconditioning process of "training" the EDR system to ignore suspicious behavior. Several benign memory allocations and changes are issued, which exactly imitate the actual attack but harmless. Over time, the EDR gets desensitized to such a change and ignores the real attack.
Conclusion: A New Era of Loader Evasion
According to Dobin, the EDR and red teaming have changed over the years. Red teamers can now incorporate these advanced tactics and keep frustrating the defenders. Although all defenses have their shortcomings, knowing what EDR is and trying to minimize telemetry can vastly help in the ostensible failure of your shellcode loaders.
This presentation continues the theme that there should always be an equilibrium — the burden of employing original or effective functional methods, such as memory shielding or execution guardrails, should not be greater than the risk of using the loader.
Special thanks to SWZHOUU for their valuable insights and support in writing this article.