
So hello everyone. I'm Pag Fiskilis and today we're going to talk about EDR evasion for fun and profit. Well, it's mostly about profit at the end of the day, right? I think so. So, some information about me. I am a senior penetration tester with quite some good red teaming experience at Envis. I have some industry-leading certifications including OCP, EWPT, CRTO and currently I'm going through OSWA and OSWP. They're like still on the bakery working on them. Up next we have some as previously mentioned industryleading trainings including Maldiv Academy, anti-ciphon training or Black Hills uh altered security or pentest academy for those who know and finally sector 7 we mostly going to use like Maldde Academy and sector 7 content in the
current presentation. A small note, we're going to use the holy bible of edr evasion, the evading edr books from Madhand. Personally, I think that's the best cyber security book that exists like right now. Until uh Chris releases her books, it will be the second best. Let's have a look in our agenda. First things first, we're going to talk about EDR internals. How a Windows system boots when we have an EDR. Then we're going to talk about hookings. It will be our main pain point and the main evasion point. Then we have EDRs and ETW. How EDRs communicate with ETW and how we're going to bypass both of them in the next content. EDR and ETW. And finally, we
have some, you know, cute fun facts that I thought they were kind of cool regarding EDRs. Let's start. First things first, how an ADR works. Uh, do we all know like what is an EDR? Oo, nice. Okay, this is going to be fun. So some like more hard parts of the EDR we have the sensor, the agent, the cloud console or sim and finally we have the human factor which is the analyst. Let's talk about the sensor. A sensor is basically a service running on Windows when basically the user starts to log in and does some userland things. The sensor, the thing that does is basically consume logs, read logs from [ __ ] everywhere. Then the sensor send them to
the agent also running on the endpoint. Uh the endpoint can be like a server, a laptop, a desktop or generally a system that has an EDR attached to it. The agent does some uh correlations and may trigger some lowhanging fruit alerts. For example, default cobble strike beacon. The agent gathers all the information, the telemetry and send them to the cloud console and the CM. Finally, we have the analyst who like flags information as non-issue, true attacks, false positives, etc. Now, [Music] oh, way too many technical issues today. I don't know why. So let's talk about the more soft parts of an EDR. We will only talk about three of them. There are multiple. We have
detections, hookings, and telemetry. Detections are basically the rules. We will focus on detections a bit later where we will talk about brittle and robust also mentioned on the EDR evasion book. As I said, best cyber security book ever. Then we have the hookings that the EDR puts on some functions system calls in general. And finally we have a telemetry which is basically just a simple value that says okay we have an alert. There is a threshold for telemetry. If that that threshold is passed we have an alert. Let's talk about brittle and uh robust detections. I think it's a very nice touch from the book from Matt Hans's book. Brittle detections are like more casual detections. They're mostly
like signature based, entropy based and generally hbased something that also an antiirus can do. But the cool part of the EDR is the robust detection. The robust detections are the dynamic detections or the behavioral detections. It also includes memory scanning. What do we mean when we say behavioral detection? If we have an application that for some reason opens Notepad and Notepad starts talking with the internet. Okay, it's weird. Something goes wrong. Uh this behavior can be flagged by an EDR with a dynamic or behavioral detection. you can basically write something like a playbook uh for your EDR to catch this type of behavior. Then let's see the the EDR execution. We have the kernel driver in
ring one. Back in the day, this could be ring zero, but there were some changes from Microsoft with uh for kernel patching that we're going to see later. Then when we have the driver initialized for the EDR, we have the next step that starts the EDR service. The EDR service includes both the sensors. There could be multiple sensors, for example, for multiple log sources and mostly one agent per EDR instance. Finally, we have the not finally we have the cloud console where the the agent as we already mentioned sends information regarding issues, correlations etc. And the best and worst part of the EDR world is when we have the user processes because the entire like package if we
can say well basically the driver does it injects the edrdll on every user process which means that every user process is controlled by the EDR. Yes, this includes our malware. So I have some examples here. I have installed uh Crowd Strike Falcon sensor on a personal home lab that I had. As you can see, we can see the CrowdStrike Falcon driver. A smaller remark. This talk uh is not specific to Crowdstrike. It's generic. I chose CrowdStrike because it was hot after the CrowdStrike generic incident that happened. I think that we all remember that. As we can see, we see the Crowd Strike driver. Next, we have the Falcon sensor, which is basically the sensor that
Crowdstrike uses in order to communicate with agents, SIMS, etc. Once again, the Falcon sensor from Crowdstrike in a different view of uh I think it's proherent and the service for the EDR. Basically, the EDR service includes both the agent and the sensored as we have already mentioned. Let's see how we are able to boot a Windows system with an EDR attached. First things first, we have the bootloadader. No OS there. We don't know what an ADR is. We don't have Windows. We don't have Linux. We don't have like anything. It's basically the base bootloadader. Then we have the bootloader fetching the init function for the kernel initialization. And indeed we have the kernel initialization process for let's say Windows because
this talk focuses only on Windows EDA. Then we have the EDR driver initialization and at the same time the OS initialization. Most drivers start at the same time almost with the operating system Windows in our case. Then we have the active EDR driver and also active edr service that can interact with the operating system because as you can see here the operating system has already initialized. Next uh we have the in the system initialization. All like the system processes and processes that Windows uses and run as any authority system are started initialized and they do the things they do. It also includes Windows updates. Then we have the user who logs in and starts spawning user processes in
the userland ring uh 3. And finally, the EDR driver once again injects the EDRDL on every single process or service including our malware. Let's talk about hookings. We have basically three main types of hookings. We have the inline API hookings. Basically, it's the most known type of uh hookings. We're going to talk about CN C++ functions. So we're also going to talk about Windows native API and Windows API. Basically Windows API is an abstraction of the native API. What do we mean with inline API hooking for example when we call from our malware in our case a function such as virtual lock on or anti allocate virtual memory which is like the equivalent for native API. The EDR will
have it hooked. the EDR will view like the function call and then we have a hook. Next, we have the import address table hookings. It's basically for the EDR to hook NDLL and prevent arbitrary functions and general calls from NDLL from being executed around uh right and left. Finally, we have an older technique which is the SSDT hooking system service descriptor table hooking. I managed to say it correctly. Nice. Uh basically SSDT hooking is literally hooking on the kernel. You hook sys calls when you are in ADR. Uh now this technique cannot be used because Microsoft has introduced uh patard because earlier versions of Windows didn't have patard and edr were like quote unquote free uh to patch the
Windows kernel to place their hooks. Uh let's see an example of a process for example notepad.exe running with an ADR and without an ADR. Uh first things first we're going to talk about Notepad running without an ADR. We have notepad.exe running normally. Then we have the create file w call from kernel 32. Then we have the kernel base API call. Basically uh create file w is an abstraction of the function with the same name from kernel base. We prefer using more highle functions when we write for example C or C++ code because it makes our life like way way easier. And uh the last step before we go to the low-level chaos is the end create file
which is stored on ndll. As a small note all like these calls are on ring 3. As you can see ring 3 is the userland is where the user the user processes the user files everything lives. And then we have the ring zero chaos. Next. So in general uh when we perform a a native API call in Windows, we will use these four golden instructions. These four golden instructions, we're also going to use them later when we develop our malware. What are we doing? We basically store RCX to R10. Nothing special. Put the uh the SSN the service number of the sysol on EAX. call sys call return to normal flow. Easy, right? I think
so. And then we have ssdt that takes the number from eax and actually checks in which sys call it goes and okay we have the completion of the sysol with notepad for example to create a file. Let's get on the ed chaos now. And also in this part, I really hope that you will understand what a hooking is. Basically from that cute image right there. Once again, we have notepad.exe. Our favorite process, favorite for malware, favorite for reverse engineering. I [ __ ] love Notepad. Why not? So once again we have the K 32DL being executed and in our example the EDR has placed a hook on the NDLL only not on the kernel based
DL. We want to call the native API function entrite file. What do you think when we have an ADR and we have a hook? Will we be able directly to call that function? Yes or no? I heard some someone say yes. Okay. Uh the correct answer is no. By the way, what an ADR hook does? Basically, when we place an ADR hook somewhere, not us, the EDR and the quote unquote bad guys that don't want our malware to be executed. For us red teamers, the blue team are very bad guys. Basically, when we try to call such function, the EDR will place the hook. It's like a break point and then the system will not redirect us to the
actual function call on NDLL kernel base uh or whatever DL. It will redirect us to the EDR. The EDR then will check all the parameters of the function and be like okay uh what has executed previously okay uh this this and this uh what are the parameters okay for example for virtual lock okay I see a large buffer I see weird permissions rwx that's malware and then the EDR can either literally kill the process and all its children or it can allow the execution We hopefully want to allow execution even if the EDR like detects something weird or anomalous. If we manage to pass the EDR check, it's basically the same thing. We have like the execution of the system
call SSD and finally the completion of the sys call. Sounds good, right? Now let's talk for uh for another pain point. EDRs and ETW. Uh do we know what ETW is? Okay. So EDR communicate with uh ETW. Many EDRs especially the Microsoft ones for example ATP uh prefer to use ETW and partially some hookings. Uh for example if we want to go more deep dive and technical Crowdstrike uses mostly hooks everywhere. uh ATP uses hooks on some specific NDLL functions and relies mostly on ATW. That's why it's a big pain point to bypass EDW when you deal with ATP. So, uh the EDR will communicate with ETW via kernel drivers. We know it right using two types of like function calls,
callbacks and notifications. For this talk, you don't need to know what like a notification or a call back is. We're going to talk a bit about callbacks later, but not something special. It will be like a nice remark, let's say. Let's have an example. ETW uh will call the EDR uh when something happens. That something can be an object creation for example a file could be a new connection to the internet via conhostexe for example uh a new handle is created especially handles edr love and hate at the same time handles especially if they are on ndll and ETW will callr to investigate like the event of the creation deletion etc. So uh about callbacks I promise
that uh it will be kind of short because it's not that important for us. We are going to talk uh we could talk like for callbacks and uh call back hacking in like another conference or something because it's a an advanced like type of attack. There are two callbacks. They are uh pre-operation post operation. Post operation are basically we wait the malware to execute and then we investigate. That's wrong. Modern EDRs use pre-operation callbacks where basically if we want to step on a function on NDLL for example, the ADR will say stop. We investigate and then you run. Now we have uh seen how an NDR works. We have seen everything. We not really everything but still we
have a nice background. Let's break some EDRs. It will be fun. I promise. So uh these are the main evasion techniques. We have EDR unhooking which is like a very broad category. The most known technique there is uh DL refresh for NDLL mostly. That's our main pain point. We have direct indirect sys calls. We have dynamic Cisco resolution for example with sys whispers. Do we all know sys whispers? Nice. We have debugging with uh seage or structure exception handler and vage virtual exception handler. Well, it's basically software break points. Uh then we have the hardware break points which trust me it's way stronger and way more evasive than software break points. And finally we have the cool
ways to bypass it W. First things first DL refresh for NDLL because that's what we want to have. Right. Right. Here for example is um yeah sorry there is our malware in memory. We have a copy of NDLL in memory but what's the problem? It's hooked. We can't like directly access NDLL. We're going to access DDR and then NDLL. So what we're going to do? Well basically we will create a new process in suspended mode. So the EDR does not apply hookings or uses ETW to like gather information because it is suspended. It will just like spend resources if they if it place hookings. We will create a suspended process and then the refresh part we're going to
override the copy of entdll from the suspended process and then we have a clean copy literally from the disk. No hookings, no break points, no debugging, just plain NDLL. In some EDRs it may work, in others it may not because it's a very known technique and it also requires us to open a handle at the end of the day to DDL. Damn. Let's say for example uh we have some practical examples here where we basically like I'll be fast I promise we basically create a handle to
entlers and information and all sections from NDLL to us. Uh the talk will probably be on YouTube so I'm not going to explain every single line of code. You can just rewatch it later at your own pace. Uh also you can find multiple websites that use not the exact same code some paradigm of uh this code for this specific technique the DL refresh. Then we have software debugging basically with structured and virtual exception handler. We put some software break points or on our malware and we basically check the next address. It's not that that evasive and it also can be circumvented by the ADR. An ADR can basic full control over our malware and can basically delete like uh break
points for fun I guess. Now uh the hardware breakpoints actually that's a quite new technique. I think it uh started on like late 2024, early 2025 or something. Let's see. We have our normal malware flow where we for example allocate some memory or get our variables ready include like our cell code from a header file. Nothing special or like calculate sizes, right? Nothing too spooky for the EDR. Next we check if the imported function is hooked. We basically can uh check the original address of NDLL and compare it to the address of the EDR. Or a way easier way is to like we can get the address of the EDR and if we see an
address that looks like an ADR address, it is an ADR address. What are we going to do next is use the DR7 register with the instruction int3 or CC in hex and place basically a break point. it is a hardware break point and it cannot be circumvented like now later I'm not sure then we're going to find an unhooked version of the NDLL DL for example with DL refresh or any other technique that we want to use and finally we're going to replace R IP the instruction pointer with the address of the unhooked address for from like the new NDLL. And finally, we have function execution as intended. And this method actually is way too evasive and it will
do such quite nice work, but it will uh probably get caught by ETW because in some applications you don't need to like put a strong hardware break point on execution. It may look weird. When we use hardware break points, we mostly use these three functions. We basically use get thread context because we're going to execute our style code on a thread. We can have the maximum number of uh four break points per thread. Uh when we exceed this number, we're not going to like set a break point and in general things will go wrong. Then we use set thread context to basically create a new breakpoint. as we already mentioned with the limitation of uh a maximum of four and finally and
continue when we are able to like change all the required like addresses find the required DLS etc. direct sys calls. Uh if you have played even a little bit with cobalt strike, you probably have seen uh this option. Do you want your implant to have direct sys calls? Basically no because this is a very old technique and it's not evasive at all. I just put it here for like culture I guess. As we have already mentioned, we have the four magic instructions where we store RCX to R10. for example, uh it's not mandatory to use R10. It's mostly like a common paradigm to play with. Then we store the uh SSN, the Cisco service number. Yes, I
remembered it to the EAX perform the Cisco return to normal malware execution. Also, we have this uh very cute I would say diagram where it is basically the flow of a malware. We're going to allocate some space right on this virtual memory. We're going to write our cell code which is basically uh cell code for cobalt strike sliver or any kind of C2 reversal that we use. And if you're way too advanced, we can write like custom cell code. Why not? Then we're going to create thread X executable and create a remote thread to another process or even thread. We can like have threads on threads. And finally we have okay and we wait for
single object to like return to our main program and malware. The EDR will catch us not EDR basically DW. Why though? Here we have a simple dropper with uh Windows win API not API and uh we're not going to use like direct cllos or anything fancy. What do you see here? Everything moves and comes from normal DLS. This can be kernel base or entll. That's the intended way for an application to execute. But we have our chaos now with the direct sys calls. What do you see here? Everything is inside our malware because we use the golden instructions. And that's why oh we also have this example of execution where in notepad once again I named it malware for no
apparent reason. Uh in notepad we directly execute the golden instructions and literally bypass all the other parts of like importing a DLL calling a function and then it makes the call stack look really bad. Can we like fix this situation? Of course. We have indirect sys calls the Toyota Corolla of malware development. It is old, stable, reliable, and it basically works. It gets you from no access to an initial beacon. What's the cool uh part about indirect sys calls? Basically, we do the exact same process, but instead of using directly the four golden instructions, what are we going to do? We're going to move RCX to R10 like put the SQL number to EAX. But see a small
difference. We're not going to perform a sysol. We're going to jump directly into NDLL. Probably a clean copy of NDLL. For example, we can use DL ndl refresh let's say and we're going to call sys call and red from the part of the function that belongs to ndll. So our call stack will look way better in it's like rope return oriented programming. Does anyone here play binary exploitation or pawn one two? Wow. Well, basically in binary exploitation you can use like rob chains and uh rob or job return orient programming jump oriented programming and move on the binary however you want. That's what we basically do here where we have everything ready and then we literally
jump to Cisco and red from NDLL. Let's have a small comparison here. Uh this like diagram uses Windows API and the other one uses indirect SQLs. We're literally doing the exact same thing but the only difference is how we call it. This one will get caught probably at virtual lock on the first call. The indirect SQL one may or may not get called uh get caught. Uh okay. We're going to talk now about indirect sys calls versus ASLR. Do we all know what ASLR is? I see very few people. So basically ASLR what it does it takes a DL and it like scramles the address that all the functions are imported. So we cannot like directly communicate with the DLL.
to evade not evade to avoid such issues we're going to get the base address of NDLL that's like our crown jeul in malware development then we're going to add the offset of the function and sys call that we want to call as a gadget of some sort and voila we have our sysll completed now in this example we have the chaos of direct sys calls as you see everything is inside the malware And then we have the beauty of indirect sys calls. Everything is as intended. We have ndll kernel base everything perfect. Right now we're going to talk about the mainetw bypasses. The first two I think are related to the driver trick. Well, I'm going to explain what a driver trick
is. First things first, the driver trick is loading a vulnerable driver to the system and then basically hijack some DLS. It will literally blind DDR on reboot because our malicious DL is going to execute on ring one or ring 2 depending on the driver where the EDR has like limited visibility there because okay we cannot like patch drivers and kernels etc using uh we can also use tools like uh C lighter to spoof a Microsoft signature for our malicious and vulnerable driver. Finally, we can uh disable TWW's local admin. Don't do it. It's not evasive at all. Then we can unhook DLL to make logging more obscure. But once again, we will have issues with uh like the sock.
And my favorite part, this technique, the patching. It's also quite new, but it may or may not get detected depending on the EDR. We're going to patch in memory the entire EDW library. Let's talk about memory patching and the chaos that it's well basically it's not chaos, it's quite interesting. We have once again the normal malware flow. We set variables. We include headers. Everything good, right? I hope so. Then the next step is to check if an imported function is an ETW function. ETW functions are known anyone can use them because multiple application in Windows can like have some logging functionality via ATW uh which is which can be used not only for like malware
detection. It can also be used for debugging purposes. We check basically if the next function is an ETW function. Then we're going to change the memory protection of the next function, the ETW function, and replace the first few bytes with a red instruction. Well, basically the ETW function is called, nothing is executed. We have just a red vector malware. And finally, okay, we have the execution of our intended function, for example, virtual lock or anti-allocate virtual memory. We use once again these three functions. Get process address to like get the uh address of the TWW processes in order to check the next call dynamically. By the way, we change the memory permissions with anti protect. And finally, we have
anti- write virtual memory to replace the first few bytes with the red instruction. Uh someone may say that okay, this is going to corrupt like the entire CL. No, it may corrupt it, but we actually don't care. We are literally on a red. Red is executed. We're back on our malware. We're happy. So, uh, we're going to have some EDR fun facts where basically we can spawn a suspended process and the EDR will not place hooks. We have already talked about it on the DL refresh section. And finally, some ADRs may be blinded with the driver trick. Well, it was nice to be here and I'm sure that you are probably tired from listening to me saying about C++ CDRs
and all that geeky nerdy stuff, right? And I'm sure that you will probably enjoy some dwarf metal I really want to do. So, are there any questions?