Route of Least Detection
An easy way to hinder an EDR and move silently during your next red team exercise using the routing table.
Motivation: EDR Evasion On A Budget
In a recent red team exercise I was tasked to build an implant that would have to coexist with one of the major EDR platforms available today. It was imperative to me that during the exercise not only would I be able to execute malicious code without it being killed by the EDR agent, but also be able to operate effectively without it sending any alerts back to the defending SOC. On top of these constraints, I also had a limited time frame (about a month) to fully develop the infrastructure, implant, and C2 for my operation. Given all of this, I started to think of how I could craft an EDR evasion technique that I could easily implement without having to write shell code, unhook the EDR, or find / buy a 0 day.
Research Phase: Understanding What To Evade
In previous attempts I had used injection techniques, namley relfective dll injection (rdi), but this was noisy and will trip most EDRs hueristic scans. I decided to do some OSINT and really understand how these EDR platforms work. Most major EDRs that I have looked at have 2 main engines: the static scan engine and a dynamic huerisitics detection engine. What I learned from my previous malware development is that sometimes doing too much can hinder you, so I wanted to create something with multiple stages where the first few stages behaved as "normally" as possible in the eyes of the EDR that would not trip either of the detection engines.
The second part of evasion is not leaving IOCs within the logs. Even if you evade the EDR's detection engines, advanced SOCs have their own detection stacks that use telemetry from OS native logging sources along side the EDR logs. The EDR agent will send the logs to the EDR console or backend, which is usually hosted by a cloud compute provider, and then the SOC's SIEM will ingest the logs from there. This made me think, What if I could stop the agent from communicating to the backend? This is where I thought of my idea to make a 3-step process to go completely invisible: 1. Create a PE loader to load later stages in memory to bypass EDR scans. 2. Look for any SIEM agents and stop OS logging. 3. Create a sinkhole to the EDR backend by updating the routing table.
Stage 1: A Normal Process
As I said earlier, I wanted early stages to look as normal as possible and have super simple functionality. Using my knowledge of the target, I disguised my program as something you might expect to see on a target machine. The stage 1 binary only does one thing: look for an executable on the C2 server and load it into memory. I decided that in order to move more stealthily, stage 1 should have no interactive session but rather pull down commands hidden in HTML webpages that I control. To further obfuscate my intent, URLs to binaries also had their own disguise web pages until a specific user agent was used. Binaries were encrypted server side using shared keys derived from the Libsodium Key Exchange Function.
Once stage 1 is run, it computes the shared key, pulls down the binary, and loads it into memory using my custom PE loader. I won't go into the loader as there are plenty of resources on how to build them. I learned from one of my college professors who has a great github repo for learning about Windows malware, shout out Kai. Finally, after the proceeding stages are finished executing, all memory is zeroed out using the SecureZeroMemory function to ensure that even with compiler optimizations, all possible memory forensics IOCs are wiped out. Since the first stage does not do anything inherently malicious, it easily passes both EDR detection engines.
Logging Off: Killing The SIEM
The next two stages were the most crucial part to the success of the red team. Because I knew my target had a more advanced SOC, I needed to make sure that I did not trip anything on their custom detection stack. To accomplish this, the second stage binary was pulled down after a short sleep and was made to specifically enumerate services and look for SIEM agents that would forward logs back to the SOC. I got a match on a service used by one of the major SIEMs the implant was looking for. At this point I had already privilege escalated as the user I targeted had a local administrator account. Because my process was running in a privileged context, I was able to use OpenSCManager to get a handle to the service manager and ControlService after getting a handle to the SIEM agent service to stop it. With the SOC half blind, my stage 3 kicked in after a short sleep so as to not trip any hueristic detections from the EDR.
Blackout: Killing the EDR
The third stage alters the routing table to sinkhole any traffic outbound to the EDR's backend using CreateIpForwardEntry. This function allows you to add a destination with a netmask and specify an exit gateway. To get the IP range needed to disconnect the EDR, I did some OSINT and looked into the cloud providers that the major EDR companies used. As you could expect, most use AWS, which conveniently has all of their IP ranges listed in a JSON file. If I were to do this again, I would expand my stage 3 to first enumerate security software and watch connections from that process to find the proper IP range. However, with limited time and solid intelligence of what my target's EDR platform was, I instead crossrefenced which IP ranges were specifically EC2 instances in my target's availability zone.
I came up with three /16 networks that I would sinkhole which is, admittedly, noisy and terrible opsec although it did not end up mattering. In order for the routes to take effect, I had to delete the routing table entry 0.0.0.0 so that there was no route for a backup gateway. As a result, I had to reconstruct the routing table to allow internet traffic to flow normally. Without any indication of anomalous activity, the agent could no longer connect back to the console and had dire consequences. Not only was the defending SOC now completely blind logging wise, if they were to be alerted of something wrong, there would be no way to take any response action. All of the necessary tools to deal with the attack such as the ability to kill and quarantine a threat, network contain the machine, and remote shell in, were rendered useless. Now with almost all defenses gone, I was a lot more free in what I could do during the next phases of the attack.
Final Thoughts
When researching various ways of bypassing advanced EDR solutions, I often saw quite complex approaches that looked too noisey or were not proven methods. I am very much under the belief that the best approach to defense evasion is simplicity, and I think I proved that. Creating something too far out of the ordinary is an easy way to get flagged as anomalous by an EDR, which is what I learned in the past. Since so many networking tools and VPN services alter the routing table, catching this activity will continue to be impossible if EDR companies do not address the problem.