Defeating EDRs with Office Products


Removing an EDR's hooks from a process is not a foreign concept these days; it’s become a common technique deployed by adversaries to remain undetected while circumventing anti-malware controls. Defenders have tried to combat these attacks but ultimately fall short as most of the effort rests on ensuring that malicious executables can’t run on endpoints (typically through whitelisting or other access control lists). This technique, combined with intensive logging, is often deployed to detect these attacks, preventing any further actions, and stopping the attack chain.


Unfortunately, adversaries are constantly adapting to defensive controls by inventing novel approaches to perform these techniques. As EDR products started augmenting their detection controls with Event Tracing for Windows (ETW), adversaries started tampering with these functions to prevent ETW events from being generated. When it comes to circumventing access controls, adversaries often rely on trusted applications or fileless attacks. These types of attacks are harder to stop or detect because they use legitimate applications to execute malicious actions.


This article will cover topics like the effectiveness of fileless attacks, including their use cases. We’ll also discuss Ivy, a new payload creation framework that utilizes Microsoft's Office VBA environment to programmatically unhook EDRs from processes. The framework then loads, decrypts and executes shellcode while remaining undetected by standard signature-based rules for Visual Basic for Applications (VBA) macro attacks. Ivy techniques are all fileless-based attacks that rely on VBA code (like typical Office macro payloads), however, these are not hampered by the deployment of the security control "Disable Macro Functions" built into Office products. Throughout this article, we’ll discuss the inner workings of the techniques in detail, as well as what defenders can do to help detect these types of attacks inside their networks.



Fileless Malware

When we talk about fileless malware, which has come and gone over the years, there is often a misunderstanding of what that means. Fileless malware techniques often utilize legitimate processes to load code or scripting functions to perform malicious activities such as executing shellcode inside the context of said legitimate process's memory. As a result, there are next to zero artifacts to detect and investigate. Modern sophisticated adversaries use these types of attacks not only to circumvent any anti-malware controls such as EDRs, but also to evade access controls like whitelisting mechanisms.


The most notable example of fileless malware is the PowerShell scripting attack. For a long period of time, this was used by adversaries as a way of executing malicious code on an endpoint without detection or leaving artifacts on disk. This is mainly a result of PowerShell’s ability to import PowerShell scripts into memory from a remote location, such as a website hosting the script (e.g., an HTTP request). Once loaded, the module is called and the script will run, executing malicious code in the context of that PowerShell process’s memory.




Figure 1: PowerShell Fileless Scripted Delivery Walkthrough



Another great example is the research done by Casey Smith (@subTee). Casey’s technique utilizes the native wmic.exe binary and stylesheets to execute shellcode in memory. This is accomplished by embedding VBScript into stylesheets (.xsl) to perform an action by calling an object in the Component Object Model (COM). This technique was successful due to several command-line arguments in wmic.exe that accept the “/format” option. This option can retrieve a remote stylesheet that contains embedded VBScript code and load it into memory.




Figure 2: Calc.xsl File




Figure 3: WMIC In Memory Attack Spawning Calc.exe



This technique can be taken a step further by combining with other techniques, such as DotNetToJScript to load .Net Assemblies in memory and deserialize them to run. These attacks are predominantly "fileless," but when tested against a more sophisticated EDR product, such as Microsoft Defender, we can see there are WMIC stylesheet attacks and how this resulted in a temp cache.




Figure 4: WMIC Web Cache Sample Submission



As a result, these attacks are not truly fileless, but due to their characteristics, they are often harder to detect by EDRs and subsequently to forensically analyze. To be successful, they need to take advantage of components that already exist on the endpoint. Let’s discuss one that’s native to all companies using Microsoft Office suite. Macros are written in VBA and saved in macro-enabled Office documents. They’re often created for legitimate reasons, such as automation or process login within documents, but they can also be written by adversaries to gain access or to bypass other security controls.


VBA is a little different than the more commonly known scripting subset, VBScript (VBS). VBS can create small-scale applications, automation, etc., and have historically used as independent scripts to deliver malware outside the context of Office-based applications. VBA and macro-based payloads are not new.



What Are Some of the Characteristics (or Signatures) of a Macro?

While there are many different techniques to execute a macro-based payload, and most share the following common attributes:


  • They exist in macro-enabled Office documents
  • The documents must be on disk to run (not in memory)
  • They can only be executed in “Macro-Enabled Documents”
  • They call or load external resources outside of Office to perform actions on the endpoint


With these in mind, it can be easy to detect and prevent the execution with the use of some common controls, like "Disable Macro Functions". This feature prevents all macros from executing on the endpoint and can be set by the administrator. While this limits macro document-based payloads, it doesn’t prevent malware using the VBA object model. Specifically, objects inside Office's VBA environment can be called from external resources.


Introducing Ivy

Ivy is a payload creation framework for the execution of arbitrary VBA (macro) source code directly in memory. Ivy’s loader does this by utilizing programmatical access in the VBA object environment to load, decrypt and execute shellcode. This technique is as close as possible to being truly fileless, as most fileless attacks require some form of files being placed on disk (such as temp files), as a result bypassing standard signature-based rules for detecting VBA code.


Ivy provides two different loader types to carry out these actions. The first, Inject, performs a process injection attack where a new process is spawned in a suspended state and the shellcode is injected into the process before it’s resumed. While injection generates a non-Excel process, EDRs are adept at detecting the creation of a suspended process for injection.


The second option is Thread,which loads the shellcode directly into the current Excel process, spawning a new thread in the process for the shellcode to run. The Thread option also comes with additional features to avoid detection, utilizing direct calls to some Windows syscalls. This is due to the VBA environment allowing us to define and call the exact function (provided we’ve aligned all the correct registers beforehand) based on the stack. Finally, Ivy’s loader in this payload type has an undocumented call to execute shellcode, making it harder to catch.



If Macros Are Disabled by Default, Does This Attack Still Work?

Yes. Ivy doesn’t rely on macro execution in the traditional sense. To execute the shellcode, we need to programmatically enable "Trust access to the Microsoft Visual Basic for Applications (VBA) project object model." This option allows Office applications programmatic access to manipulate the objects or code in Office's VBA environment. Without this setting, it’s more difficult for unauthorized programs to build "self-replicating" code that could compromise an endpoint. As result, this option isn’t affected by setting the “Disable all Macros" options in Microsoft Office. It can be modified by users without administrative permissions on the system, which means any user process operating under any integrity level on the endpoint can edit this value without restrictions.




Figure 5: Macro Settings



The following block of code creates a COM object to Wscript.Shell, which provides access to the Windows OS shell functions. The OS shell allows adversaries to interact with the Windows registry. By doing so, the loader checks the version of Microsoft Office installed on the system, then creates the registry key associated with trusted access to the VBA environment (or modifies the key if it already exists but is set to zero). As a result, programmatic access is enabled to any Microsoft Office product that has access to VBA.


var Shell = new ActiveXObject("WScript.Shell");
var strRegPath = "HKEY_CURRENT_USER\\Software\\Microsoft\\Office\\<Office Version>\\Office Application>\\Security\\
Shell.RegWrite(strRegPath, 1, "REG_DWORD")




Figure 6: Macro Security Settings - Allow Access to VBA Object Model



Microsoft released a group policy to help mitigate the programmatic abuse of AccessVBOM a few years back. By enabling this group policy, the option becomes unavailable (even local administrative privileges cannot enable this setting). This is Microsoft’s way of preventing malware from self-replicating and executing malicious code.




Figure 7: Disable User Mode Access to AccessVBOM



However, this does not entirely prevent the activation of this setting. By using another alternative registry key, we can enable "Trust access to the VBA Project object model" resulting in the same level of access. This is accomplished by creating the “Security” folder under:


HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\<Office Version>\<Office Application name>\Security


While effective, there’s a caveat. To modify anything in the registry under HKEY_LOCAL_MACHINE, the process requires elevated privileges.




Figure 8: Local Machine Registry Bypass



Once AccessVBOM is enabled on the endpoint, the loader can create a COM instance to an Office process, by calling "CreateObject(Excel.Application)" for example. The Excel.Application COM object represents the entire Excel application, but in an automated form, and allows for programmatical interaction with it. Another great feature of Excel.Application is that it visibly spawns under a Service Host process (Svchost.exe) instead of the parent process.




Figure 9: Excel Spawning Under Svchost.exe



Upon completion, the loader will spawn a hidden Office process and load the encrypted strings into a VBA function. Once in memory, the malicious code is ready to be executed. This is accomplished by using ActiveX to simulate equivalent GUI actions. ActiveX provides functionality to scripting engines like JScript and VBScript, giving them the ability to interact with Windows or third-party applications.


This bypass a lot of traditional controls that monitor for execution. As a result, the decrypt function and shellcode are moved from one memory buffer to another, never touching the disk. Finally, the loader uses command-GUI calls and executes the run function, which simulates the act of clicking on the run macro button in the GUI panel of VBA. This begins the decryption function, followed by the actual execution of the shellcode.



Unhooking EDRs Using VBA Code

While the way Ivy executes shellcode on an endpoint is quite novel, it has another unique way of unhooking an EDR's hooks from within the Office VBA object environment. Due to the nature of EDRs relying on user-mode hooks, any effective loader needs to perform some type of EDR unhooking. Since the hooks exist in user mode and hook into our processes, we have control over them. And because the process runs within the user’s context, everything that’s loaded into our process can be manipulated by the user in this manner. It’s important to note that some sensitive regions of memory are set to Execute, Read (ER-), which prevents the modification of these regions. We will discuss some techniques to navigate around this, but if you’re curious and want to learn more about userland EDR unhooking, please reference a related article, Endpoint Detection and Response: How Hackers Have Evolved or framework ScareCrow.


While these articles talk about using binaries and DLLs to unhook and load shellcode, Ivy comes with the ability to unhook EDRs from the process it's running as, or from another process using the VBA scripting engine, all while in memory. This allows Ivy to use low-level system calls to declare its own version of the Windows function WriteProcessMemory in VBA.


Ivy can overwrite sections of memory that aren’t writable without calling any of the memory change API functions. This is done using WriteProcessMemory, which temporarily modifies the permissions of the region of memory to writeable (that is, if you have sufficient privileges, which we do, since we own the process). It writes the value and restores the original permissions without calling the VirtualProtect function; instead, it automatically calls the associated Syscall (NtProtectVirtualMemory).


As a result, Ivy doesn’t need to utilize a custom syscall to NtWriteVirtualMemory, because this built-in function of WriteProcessMemory allows for temporary modification of memory regions. This has been classified as a feature by Microsoft to make debuggers more stable. If debuggers want to modify memory on the fly, they can simply modify a section without having to perform multiple tasks. (For more information about this feature, please reference Microsoft’s article.)


Rather than reloading the entire .text section of a hooked DLL, Ivy targets the specific API functions that are hooked. By reviewing and monitoring where EDRs hook from previous research, Ivy unhooks specific APIs across numerous system-loaded DLLs. From a process perspective, the unhooking occurs as follows:


  • Ivy creates a reference to the WriteProcessMemory API function, using the declare feature in VBA with the proper arguments and argument types that WriteProcessMemory normally needs. This function calls the exact memory address where WriteProcessMemory is stored. (It would look like a call to the register RAX, rather than calling kernel32.dll's WriteProcessMemory function.) This means we’re not directly calling WriteProcessMemory while still utilizing all the features.



Figure 10: Declaring WriteProcessMemory in VBA



  • An EDR would only see a string of assembly that doesn’t match any malicious indicators to a memory address. This memory address is the start of a function, but the function address is unique because of ASLR. A lookup of every function would need to be performed.
  • Prior to the Write action being performed, the syscall ZWQueryVirtualMemory is executed to view the protections on the memory region. If this memory is not set to writeable, NtProtectVirtualMemory is called to change the permissions.
  • 8 bytes of assembly are written to the specific memory address. NtProtectVirtualMemory is called again to restore the original protection value. This results in the removal of the assembly jump call, or "hook," and the original value is restored to the API.



Figure 11: Excel Process Hooked



Figure 12: Excel Process Unhooked


Ivy targets many APIs that EDR products could potentially hook. EDRs don’t just hook Ntdll.dll. This is done because unhooking Ntdll.dll isn't sufficient in removing the telemetry that EDRs receive. If other user mode hooks exist in the same chain, the telemetry is still sent to the EDR.


For example, if you unhook NTAllocateVirtualMemory in Ntdll.dll while VirtualAllocate (in Kernelbase.dll) is also hooked, then the latter needs to pass to the former so it gets the values it needs.


This means that anything passing through VirtualAllocate is still read by the EDR. Once all the EDR hooks have been flushed out, the loader performs its normal action to execute the shellcode. Depending on whether the Thread or Inject method was chosen, Ivy will use a specific function to allocate memory space before using WriteProcessMemory again to copy the shellcode into memory and finally transferring execution control to the shellcode through an API.




Figure 13: Remote Connection Established


These techniques were responsibly disclosed to Microsoft Security Response Center through their provided process. Microsoft reviewed the provided information and deemed current security controls as sufficient to prevent the identified techniques. As a result, we have provided initial defensive controls to aid in the detection of these techniques.



OpSec Considerations

Ivy is highly dependent on the presence of Microsoft's Office suite in order to execute. This makes it hard to exploit on systems that don’t have Office, such as servers.


When generating files with Ivy, you need to input both 64- and 32-bit shellcode. This is because the operating system may be 64-bit, but the version of Office that’s running may actually be 32-bit. As a result, Ivy will detect the suitable architecture to use before loading the payload.



Defensive Controls

Currently, Ivy relies on the registry key AccessVBOM being set to “1”. Without this setting, most of Ivy will fail. Ivy attempts to write the correct value to (or create) that key on execution. By default, the AccessVBOM key is set to “0” when present. Since this key is not accessed as a normal course of execution for most processes, detecting changes to it is a reasonably high-fidelity method for spotting Ivy-like processes in the wild.


For instance, the following EDR searches will show Ivy executions:


CarbonBlack: “(regmod_name:*\\Security\\AccessVBOM)”


CrowdStrike: “RegValueName=AccessVBOM RegSystemConfigValueUpdate RegObjectName="*\\Security"


Alerting (or blocking) on this event may help mitigate risk of Ivy execution in an environment. Of course, this is only helpful if the AccessVBOM registry key is normally set to “0”, so alerting coupled with the group policy fix mentioned above is recommended. It’s important to note that there may be valid business cases for enabling AccessVBOM, and review of business operations should be done to ensure no errant or false positives are generated.


If an active Ivy-like attack is suspected in an environment, positive affirmation (i.e., go check) that the AccessVBOM registry key is set correctly may also be a good practice. The EDR unhooking process done by Ivy currently happens after AccessVBOM modification, but the process could be easily modified to have EDR unhooking occur before the registry mod. If a periodic check of the AccessVBOM registry key shows systems that have a setting of “1” when the rest of the environment is set by policy to “0”, it’s a cause for further investigation.


In addition to direct detection of Ivy’s execution, it’s important to consider the context of the tool’s use, as it is only one link in the overall kill chain. Like other malware payloads that commonly target user workstations, Ivy’s code can be delivered via several different logical file formats and is not limited to document attachments. For example, usage of this tool would be preceded by initial access techniques, such as spear phishing a user to deliver a weaponized document, or potentially downloading the code from a remote host via PowerShell command. Therefore, additional system hardening to restrict usage of scripts, or allow-listing controls such as AppLocker or Windows Defender Application Control, can further augment prevention controls for code execution.


The projects can be found on Optiv’s GitHub.




03/20/2019 Research developed and article written.
10/28/2021 Provided Microsoft a preliminary disclosure document outlining identified issues.
11/02/2021 Microsoft informed Optiv that the existing and in place security controls should be effective in preventing the identified attacks.




Matthew Eidelberg
Engineering Fellow | Optiv
Matthew Eidelberg is an Engineering Fellow in Optiv’s Threat Management Team (Attack and Penetration specialization). His primary role focuses on leading Threat Management’s Adversary Simulation Services, which address physical, red/purple team, and other advanced assessments. Matthew’s expertise also involves research development, focusing on developing new techniques and tooling for endpoint security bypass and evasion.

Optiv Security: Secure greatness.®

Optiv is the cyber advisory and solutions leader, delivering strategic and technical expertise to nearly 6,000 companies across every major industry. We partner with organizations to advise, deploy and operate complete cybersecurity programs from strategy and managed security services to risk, integration and technology solutions. With clients at the center of our unmatched ecosystem of people, products, partners and programs, we accelerate business progress like no other company can. At Optiv, we manage cyber risk so you can secure your full potential. For more information, visit