Field Notes on Malware: The Evolution of C2 Evasion and What It Means for Detection
While malware developers continue to use BOFs, shellcode, and sleep obfuscation to prevent signaturing and collection of malware capabilities, a capability researched and published almost 2 years ago has surprisingly not gained traction.
Existing C2 Framework Design
Existing C2 frameworks (commercial/open-source/in-house) typically follow this design:
High-Level Flow:
- Stage 0: Loader (often dropped on disk)
- Stage 1: Retrieves Key + Next Stage (Embedded/Remote/Dynamic)
- Stage 2: Next Stage enters job polling cycle
Detailed Implementation:
- Produce Position Independent Code (PIC)/shellcode to stage the core C2
- Shellcode loader requirement - allocate RW+X memory, either in the current or remote process
- Trigger shellcode execution - in both local/remote scenarios, the end goal is to have the RIP point to the shellcode address
- Main execution loop:
- Poll the C2 address for jobs
- Execute the jobs and return results
- Apply sleep obfuscation to avoid memory scanning signatures
The previous modular plugin architecture of C2 frameworks like Winnti required downloading a DLL and either mapping it into memory or dropping it to disk and loading it. Existing implementations now exist completely in memory.
Why This Design?
The design of having staged payloads with Position Independent Code enables:
- Prevents static signatures
- Hampers static reverse engineering (to a degree)
- Complicates analysis of malware capabilities
Whether using BOF/DLL/.NET Assembly, these enable dynamic code execution that allows operators to omit core functionality from the main payload dropped on disk, avoiding:
- Static signatures
- Disclosing/burning capabilities
The BOF Problem
Example BOF loader: COFFLoader
All BOF loaders require memory allocation with Read Write + Read Executable pages.
TrustedSec have also published a set of BOF tools:
Red teams and adversaries commonly utilize situational awareness BOFs because they enable enumeration without executing command lines - as one thing EDRs do well, for all the wrong reasons, is detect/block "malicious" command line executions (PsSetCreateProcessNotifyRoutine). More details on Living Off The Land: LOLBAS Project
.NET loader - Weird Ways to Execute .NET but this carries a lot of signatures, and as a result the excellent team at MDSec researched and implemented evasion techniques, partially "documented" in their release post Three Wise Monkeys
Sleep Obfuscation prevents disclosing artifacts/detections from memory signaturing - or at least that was the original goal. An example implementation: Ekko
Cat-and-Mouse Game
These evasion techniques that enable functionality for operators become signatures themselves once disclosed/understood. This forces adversaries/Red Teams to research new techniques to compensate the detections in turn a never-ending cycle.
For example, when @C5pider published an implementation of sleep obfuscation, akin to the research done by MDSec in NightHawk - Joe Desimone (@dez_) quickly released a detection tool (Patriot). As a follow up, I published a PoC to evade those specific detections - QueueUserAPC-Trampoline
RISC-V VM: The Overlooked Capability
RISC-V VM is an open-source transpiler built on LLVM that would enable much stealthier operations. It virtualizes code rather than executing in the traditional sense, making it inherently less malicious-looking (no RW/X, or requirements for sleep obfuscation).
Despite being publicly available for 2 years, there's been little follow-on work from the security community outside of OALabs to investigate or signature this approach.
If adversaries begin building capabilities using this approach, existing tooling that looks for malicious runtime behavior and heuristics (like PE-sieve) would become redundant.
What Operators Actually Need
It's only a matter of time until the community/adversaries realizes the current state of C2 frameworks is heading in the wrong direction - in terms of evasion.
Instead of researching compensating controls and evasion techniques, they should be looking at capabilities holistically, understanding the benefits required, and building features based on that - rather than continuing to solve the wrong problems.
Ultimately, for malware to be useful it requires:
- Dynamic Code Execution
- And/Or SOCKS5 proxying (can be enabled through the dynamic code execution or not)
On previous Red Teams, the perimeter was often treated as the boundary at numerous organizations. SOCKS5 proxying enables hitting internal services, and outright avoiding most EDR detections - as the behaviour/detections that exist are often for endpoint behaviour (e.g. dumping LSASS/spawning weird processes).
Additionally, the visibility EDR products have into network traffic is often limited on the endpoints - hooking LDAP functionality (DLL), but this is more or less redundant if the traffic is proxied!
To have more comprehensive visibility would require on the domain controllers either:
- Audit Logging for LDAP
- Kernel Driver (WFP/NDIS):
- CrowdStrike previously used WinDivert (WFP)
- Microsoft Defender for Identity uses npcap + usermode process (for a deep dive on this, A Dive into Microsoft Defender for Identity)
- Injecting into LSASS and hooking ntdsai.dll (LDAPFW)
Existing EDRs are less focused on detections of the underlying behaviour or WHAT adversaries do once they gain access - what the operator does through the malware, and more so just the malware.
Instead of focusing efforts SOLELY on preventing malware, shifting to an assume breach mindset - and increasing the friction to walk the road to your Crown Jewel has asymmetric results.
Focusing on the general set of playbooks of how attackers compromise organizations. For example "Read Teaming" was a playbook known to Red Teamers and Adversaries alike, less so defenders - requires little to be rendered useful.
If I was on a Red Team, to "Read Team" an organization, all I'd need at a minimum from malware (if at all) would be SOCKS5 proxy, and a nice-to-have feature would be the ability to steal cookies from browsers/password managers.
With just those one (or two) primitives, that's more than enough to compromise mature organizations without worrying too much about an EDR.
Malware enables adversaries, it's rarely the end goal. There will always be new and old ways to evade static and runtime EDR detections. And more so when adversaries operationalize techniques to produce malware that is harder to signature/no anomalous runtime behaviour (weird memory signatures) like virtualization (RISCY Business).
The detection gap isn't going away. As adversaries move toward techniques that avoid traditional signatures entirely, defenders need to shift focus from detecting malware to detecting adversary behavior.
Want more insights like this?
Related Articles
Modern Adversary TTPs: The Rise of 'Read Teaming'
An insider's perspective on why current security products fail to stop modern red teams and sophisticated attackers, and what security teams need to know.
Threat Intelligence in Cyber Deception: A Planning Guide
How threat intelligence transforms cyber deception from guesswork into strategic planning - understanding what attackers actually do and why it matters.
Understanding Your Adversary: The Human Side of Threat Intelligence
How recognizing attackers as goal-driven individuals transforms defensive philosophy. Learn why simple, psychologically-grounded deceptions outperform technical complexity.