Bypassing AV/EDR with Nim


Nim is a not too well known language that has interesting features that make it very appealing in attack scenarios. Here is a demonstration of its capabilities to bypass AV/EDRs and a journey into learning the language.


The knights who say Nim

For quite some time now there had been a strange talk around in the cybersec community that often reminded me of the scene at the Monty Python and the Holy Grial movie. For whatever reason, these cybersec knights kept saying “Nim” all the time. When I finally found the time I took a deep look at this weird talk to try to decipher it’s meaning. And to find out whether or not this hype held the key to any kind of Holy Grial. Once I did, I can say that I do believe it does. Now, I myself have joined these knight’s peculiar order’s ranks. Out of that trip an interesting tool was born, and here’s what I found out in that journey.

Figure 1: Monty Python and the Holy Grail, The knights who say Ni scene

What and Why

Nim is a programming language, simple as that. Rivers of text bytes have already been written regarding language benefits towards the cibersec operations, so best refer to the links below for an extended grasp on this subject.

Let’s just say, for the purpose of this article that Nim has a python-like syntax that compiles directly to C, C++, Objective-C and Javascript for major platforms, and it’s easy to cross-compile it.

If you add an extended support for Foreign Function Interface (FFI) to that, that makes it very interesting for us Redteamers, as it allows to make quick offensive tools that can be a little bit stealthier.

The language

Diving into Nim’s programming right away, even if you are an experience Python programmer, can be a bit frustrating at first. Even though the language feels like strong typed Python, it does have it quirks.

I spent some time reading the official tutorial at and it really helped to get a grasp of the language, and also as a quick reference guide to have around.

Here’s a quick gettaround for starters.

Constant and Variable definition

Besides const, which works as you may expect there are “var” and “let”:

var a:string=”you will be able to change this text later”
let b:string=”you cant change this text at all”


Here’s what a function looks like:

proc add(a: int,b:int): int =
result = a+b

This can be used in either these two ways:


Importing modules

Modules can be added to you OS using the nimble command. Then you can using in your code with the import keyword:

import strutils



The basics comands you’ll need to get around will be these:

Instalation for arch or debian based distros:

sudo apt install nim
sudo pacman -S nim

In Debian, the Nim package also installs nimble, which can use to install your needed modules. In arch, however, it needs a separate package:

pacman -S nimble

Then you use it to install the modules you might need.

nimble install byteutils

And in Linux, crosscompiling for the windows platform would require this:

nim c -d:mingw –cpu:amd64 myProgram.nim


As always, it’s best to try to use your favorite IDE with the specific language syntax support. In my case, I love sublime and good old vim:

Offensive Nim Examples

Byt3bl33der’s repository is a must, here are plenty of code examples which you can use for your own projects:

It holds a wealth of knowledge on the application of Nim to cybersec techniques. This was the starting point of my project and a good one if you are planning on starting your own journey in Nim. Have a look at those to get an initial feeling of what’s possible, what has been done, and what you can use for yourself.

But my main source of inspiration for my tool came greatly from s3cur3th1ssh1t’s awesome blog on Nim:

The NimLoader Tool

Taking a good look around to see what was already out there, a new tool started to take form in my mind. Seeing that encryption, AMSI bypass, and memory loading was possible, it occured to me to gather all that add some optional encryption and the functionality to import different files from drive or url, ‘netloader/SharpLoader style’, and see how good that worked against a modern up to date AV/EDR.


For my experiment to be a success, I wanted to be able to run Covenant Grunts and c-sharp tools flawlessly in a Windows10 with an updated Defender running with both runtime protection and cloud protection running.


So basically I needed:

  • A modified tool conversion powershell script from s3cur3th1ssh1t’s blog.
  • An encrypting tool
  • Our tool to
    • AMSI bypass
    • Load files from disk or url
    • Optionally decrypt the file (it’s one less step if you dont need to encrypt)
    • Optional password for decryption, otherwise I’d use the default
    • Run the byte array into mem

I’ll be describing every code part. Sure some improvements could have been made, like more error handling, but for a quick test project, this was enough.

Here’s what the tool’s help looks like:

Figure 2: NimLoader help menu

AMSI bypass

There are a couple working examples at byt3bl33der’s repository, so I just grabbed the first one of those, which worked like charm:


This could be achieved with whatever language and tool, but for the purpose of practicing, I also made it with Nim, just a simple tool that both encrypts and decrypts an input file and writes the result into another. I also based this one of byt3bl33der’s examples:

Tool convertion to bytes

Though this can be accomplished many different ways, you can find a handy powershell script at s3cur3th1ssh1t’s blog that will dump any csharp executable to bytes:

Figure 3: Dumping of Rubeus bytes to a text file

S3cur3th1ssh1t’s script generates a file that looks like this:

Figure 4: Content of the dumped file

So, it dumps the variable definition into the file. That’s useful if you want to paste the array definition into your tool, but since I wanted to leave the text there and load it dynamically, I would have to get rid of the definition.

In the end I did some modifications to this script as to remove the variable definition creation so that this files could be directly imported.

There was another issue. Files created were DOS style, not Unix, and for me it was simpler to import them Unix style. This forced me to always run dos2unix command on these files, as to import them, which made it just another step in the process. This, though scriptable, was not compliant with my beloved KISS (Keep It Simple, Stupid) philosophy. So, I also added a change to the ps1 script as to avoid using that step.

Here is the commented-out code from the variable definition and the utf-8 dumping of the bytes:

Figure 5: Modifications to the CsharpToNimByteArray.ps1 tool (1/2)

And at the end, the dos2unix conversion:

Figure 6: Modifications to the CsharpToNimByteArray.ps1 tool (2/2)

This, sadly, added a DOS “\r\n” at the very end, but that could easily be handled by the nim code.

nimCrypter: Tool Dump Encryption/Decryption

This is how my encryption tool is used:

Figure 7: NimCrypter.exe help menu

It has a basic functionality of encrypting, decrypting and optionally accepting a key to encrypt the file, otherwise using the default one.

This tool also leaves the file in the source tool directory. This is useful as the dumped file and the encrypted file stay with the tool and can travel with it if necessary for reuse.

Figure 8: Size of the SharpBypassUAC tool dumped to text

Let’s take a peek at the code that performs this operation (click on it to see full size):

Figure 9: NimCrypter code, modules and constants

At the very top you have the import modules, then constant definitions for the iv vector and our secret key, both of which will have to be copied into our nimLoader tool in order to decrypt the encrypted dumped tool file. Later on, in the option parser I also accept the key as a parameter. Next is function toByteSeq, which converts a string into its byte sequence, and then finally you can find the main code.

To parse the command line options I needed a safe option parser. Nim provides a usable one that you can find here: It’s easy to use once you take a good look at it.

What follows is the default variable definition and the option parser:

Figure 10: NimCrypter code, variables and option parser

Next, the cryptographic vars needed for the operations are defined. Then I set up the plaintext (original text) and key in the correct format, and depending on the operation, it decrypts or encrypts:

Figure 11: NimCrypter code, encryption

nimLoader download function code

This is a very simple download function that will return the response for a simple GET request:

Figure 12: NimLoader code, download function

nimLoader AMSI bypass function

As you can see here, it’s a simple memory patch function:

Figure 13: NimLoader code, AMSI bypass function

nimLoader main code

These are the modules that I ended up using:

Figure 14: NimLoader code, imports

Here’s what my option parser looked like, offering options and parameters to decrypt, debug, optional key, help and file input.

Figure 15: NimLoader code, main variables and option parser

First after the option parser is the AMSI bypass call:

Figure 16: NimLoader code, AMSI bypass call

In case you run into this error, it’ll probably mean that you were detected:

Error: unhandled exception: unable to invoke specified member: Load (0x80131604) [CLRError]

Getting the dumped tool’s bytes is performed in two ways depending of the file argument. If it starts as an url, it is downloaded, otherwise it is loaded from disk.

Our decrypt function here is similar to the code in the nimCrypter tool.

Figure 17: NimLoader code, getting the payload

At this point, regardless of whether the incoming file/url is encrypted or not, tool bytes are finaly in our “contents” variable and set to the payloadStr.

Next, I added some debugging lines, the splitting of the string in bytes, and adding them to my buffer variable:

Figure 18: NimLoader code, setting up the buffer

This last part is straightforward:

Figure 19: NimLoader code, launching the assembly

First it enables the debug option to show the existing CRL versions. Load and dump are needed to setup the assembly as usual, then, finally, I set up the parameters for the assembly.

Proof of Concept

Finally, here is the test run against Windows Defender (click on it to see full size):

Figure 20: Bypassing Defender and running SharpBypassUAC.exe


At time of this writing, an updated Defender with all protections enabled has not been able to detect tools launched this way. I have performed other tests of this tools with other AV/EDR, and so far, no detections, but I am not so sure about how updated these other EDRs where and therefore I will not point them out.

I believe modern AV/EDR will hook API calls and detect this, so, on a future phase, I will also have to take a good look into this.


So, now I am a believer. In the world of Offensive Security, we always take good note of versatile stuff and this language definitely is. This test already is promising, and I am looking forward to trying a couple more tricks with this language. Hope you enjoyed the trip!

Nim to all of you!