Navigate back to the homepage

Implant Roulette Part 1: Nimplant

NotoriousRebel
November 20th, 2020 · 7 min read

Introduction

When creating an implant what language first comes to mind? Of course, the answer is it depends; however, typically it is one of the following languages: C, C++, C#, Rust, Go, or maybe even Python. While those languages are all dandy, they are not the only programming languages that exist. Throughout this series, I will be exploring different languages and seeing what they bring to the table for implant & offensive tool development. The first language that will be examined is Nim.

What is Nim

If we visit Nim’s Github page there is a concise definition of what Nim is. “Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.” Some of the “big” benefits of Nim are:

1* Cross-Platform: Generated executables support Windows, Linux, BSD, and macOS.
2* Nim can compile to C, C++, Objective-C, and JavaScript.
3* Nim binaries are small and the compiler toolchain allows for a lot of optimizations
4* Nim can interface with other languages through Nim's foreign function interface (FFI) [1]
5* Nim syntax is very quick to pick up and is reminiscent of Python.

[1]: https://nim-lang.org/docs/backends.html#interfacing-nim-code-calling-the-backend

Of course, these are not the only benefits of the language. More will reveal themself as time goes on.

A Simple Injector

Let’s get hands-on with Nim and see how to develop a simple injector. Below is an implementation of a typical CreateRemoteThread injector: Instead of having to manually interop with the Windows API via the FFI there are Nimble packages that contain some Windows API definitions. The one I tend to use is Winim. Winim can also interop with COM.

crt_injector

Let’s compile, run the program:

1nim c --run crtinjection.nim

and make sure everything works. By default when you compile Nim code it generates C code. In the latter section “Integrating into Existing Tooling” verification shall ensue.

If you do not want to use third party packages that is fine if you examine what Winim does under the hood for example when calling CreateRemoteThread. It is simply importing the function for us as denoted by the importc pragma as that allows us to import a proc or a variable from C. Also, available for importing from foreign languages are: .importcpp, .importobjc, and importjs.

CreateRemoteThread proc:

proc CreateRemoteThread*(hProcess: HANDLE, lpThreadAttributes: LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: LPVOID, dwCreationFlags: DWORD, lpThreadId: LPDWORD): HANDLE {.winapi, stdcall, dynlib: "kernel32", importc.}

Peeking Under the Hood

What is going on under the hood is quite interesting. If we inspect the executable’s IAT we see there are no static imports for OpenProcess, VirualAllocEx, WriteProcessMemory, and CreateRemoteThread. That is because Nim is dynamically loading the required libraries with LoadLibraryA and GetProcAddress. Furthermore, when compiled Nim will place the source code of the language you are targeting in a folder named nimcache. You can also specify the nimcache path for generated files with the compiler flag --nimcache:PATH.

Below is a snippet of generated code for inject.nim To make this easier to read I have compiled the program with an additional flag --embedsrc:on. This flag tells the Nim compiler to embed the source code as comments in the generated output. For compilation:

1nim c --run --hints:on --d:release --embedsrc:on inject.nim

A snippet of generated C source code that performs injection:

Inject_Snippet

Here we can inspect what our function calls are doing under the hood. The Windows API calls done by winim rely on the dynlib module to dynamically invoke calls. If we inspect that source code we can see the LoadLibraryA and GetProcAddress combination.

dynlib_code

Nim and Donut

Coming from C# one of my favorite tools for crafting payloads is Donut. One of the great features with Donut is that it not only works with .NET assemblies but also works with native DLLs and executables. Since Nim can generate both C and C++ source code and link it to a native executable or DLL Donut is an amazing utility that can transform our output into shellcode. Since shellcode is language-agnostic we can easily integrate it into other tooling.

Integrating into Existing Tooling

With shellcode you can easily integrate it into your own custom injectors in the language of your choice. Of course, since these are native executables and DLL’s we can also perform manual mapping and load them into memory. Coming from C# my preferred tools to perform the manual mapping are Lunar and the D/Invoke tooling in SharpSploit.

Let’s compile the injector code as a DLL and export an inject function. To export the function we need to tell Nim to export it by adding the following pragmas: exportc, dynlib, and cdecl. or stdcall. The resulting inject proc is as follows: proc inject(pid: int): bool {.stdcall, exportc, dynlib.} =
Let’s compile the DLL as follows:

1nim c --d:release --app:lib --opt:size --inject.nim

With the DLL let’s manually map it using SharpSploit’s D/Invoke tooling. If you specifically just want the D/Invoke part you can also use the repository or nuget package for solely D/Invoke.

Manual Map Code:

dinvokemanmapcode

Let’s run it and make sure our crt injector proc is executed properly.

manmepdemo

Embedding NimScript with Nimscripter

Within Nim, there is an evolving scripting language known as NimScript. In the NimScript documentation it is defined as, “the subset of Nim that can be evaluated by Nim’s built-in virtual machine (VM).” NimScript is typically used as a configuration language in a devops role. An example would be to run tests and build projects. However, one of the biggest caveats with NimScript as stated within the documentation, Nim’s FFI is not available in NimScript. This means that any stdlib module which relies on importc cannot be used in the VM.” Nim’s FFI is how Nim can call other functions written in other programming languages, which is basically Nim’s cross-platform version P/Invoke. A possible solution is to write the FFI functionality in Nim and expose it to NimScript.

Does that mean NimScript is not viable? Absolutely not, especially with the slow shift in which especially for implant development it’s not always ideal to statically embed everything. Instead leaning towards a lightweight implant that can dynamically resolve commands within memory so even if an analyst obtains the binary, not everything is revealed.

There are already a couple open source projects that show how to use NimScript as an embedded scripting language. https://github.com/komerdoor/nim-embedded-nimscript https://github.com/Serenitor/embeddedNimscript

However, those repositories while noble do not easily allow the user to expose Nim code to NimScript and are simplistic. Luckily, a new repository called Nimscripter allows for easy interoperability between Nim and NimScript.

Below is an example in which the FFI specific code that calls MessageBoxA is exposed to NimScript. This allows us to pass in a string of NimScript to invoke the Nim proc.

nimscript_embeddedmessage

In a nutshell, what is happening under the hood is the script is loaded and the main body is evaluated which allows us to access the symbols it exposes. A small caveat is that you need to include Nim’s stdlib as an external folder. Luckily, depending on what modules you are you using from stdlib you may be able to remove those unused modules. If you would like to read more about embedding NimScript as well as learn more about NimScript, I encourage you to check out this wonderful series: https://peterme.net/using-nimscript-as-a-configuration-language-embedding-nimscript-pt-1.html.

Putting the Pieces Together

While a simple injector is cool let’s see how hard it is to develop an implant in Nim. Luckily, just within Nim’s cross-platform stdlib is everything needed to create a basic implant from the FFI for API calls, httpclient for http requests, and stellar json support as a medium for data. The C2 I chose to develop the implant for is Mythic, Mythic is very sophisticated and well structured. Furthermore, implants are essentially plug and play. Everything is dockerized and your agent just needs to communicate back and forth via certain rest API endpoints.

Sprouting a few weeks later, Nimplant is born.

nimplant_logo

Nimplant is a cross-platform Linux and Windows implant for Mythic C2 that is fully asynchronous. The agent is able to generate both C and C++ source code. Currently, it just has basic functionality. However, it will be updated over time and become much more sophisticated. If you would like to learn more about Nimplant feel free to read the project’s documentation.

Example Nimplant check in:

nimplant_demo

Obfuscation

With Nim, there are ample opportunities throughout the development cycle to introduce obfuscation. Three key areas that I will highlight are:

Note: These aren not the only forms of obfuscation you can do. They are just a small subset.

1. Code Obfuscation

With Nim opportunities for code obfuscation are ripe. Two quick and easy nimble packages you can utilize are nim-strenc and nuglifier. Let’s apply nim-strenc to the string literal and view the resulting C file in our Nimcache.

Before:

beforenimhello

After:

afternimhellostrenc

When compiling the creator of nim-strenc recommends turning hints off and stripping the binary so we shall do that.

1nim c --run --hints:off --d:release --passL:-s

--passL will pass an option to the linker and we are passing -s to the GCC linker to perform stripping. Within the Nimcache folder, go to hello_r and view the file @mhello.nim.c. Inside that file we can see both string literals after they have been xor encrypted:

1STRING_LITERAL(TM__xLHv575t3PG1lB5wK05Xqg_3, "\207\251\241\236\234\320\246\231\205\230\221\333", 12);
2STRING_LITERAL(TM__xLHv575t3PG1lB5wK05Xqg_4, "\360\275\246", 3);

With nim-strenc if you look at the codebase it is less than 30 lines. It’s applying a compile-time macro that xor encrypts every string literal. For more information about compile-time macros and how they can be leveraged for obfuscation read this forum post (you will most likely need Google Translate for it.)

2. LLVM Obfuscation

For more in-depth obfuscation LLVM obfuscation can be utilized. There are two routes you can take:

The easier of the two routes is to use nlvm to generate the intermediate representation (IR). Although, currently nlvm has some limitations. Keep that in mind when trying to convert your program to IR. With the IR, you can do in-tree or out-of-tree LLVM obfuscation with tools such as YANSOllvm and dumb-obfuscator. A note with YANSOllvm, since it uses in-tree obfuscation passes and does deeper changes you will need to build the entire LLVM framework which may take upwards of an hour.

The other route is to take the generated C/C++ code transform it to IR with clang and perform the in-tree or out-of-tree obfuscation and then link it all back together. Depending on the complexity of your program you may end up working with multiple files and have to make sure they are properly linked.

I will leave this to the reader to experiment with

3. IAT Manipulation

If we throw the executable into a PE analyzer such as pe-studio or PE-bear we can see some imports that could raise some flags for static analysis. Especially the imports “LoadLibraryA and GetProcAddress”, to help bypass static analysis we can use tools such as CallObfuscator. For example, you can manipulate the IAT so the thunk that points to LoadLibraryA will point to NotLoadLibrarayA and the thunk for GetProcAddress to NotGetProcAddress.

Looking Towards the Future

While I covered a fair amount of things you can do with Nim, this is far from everything. Some things down the road that I plan to research are:

  • Nim’s interoperability with Go such as with golib-nim
  • Adding more features to Nimplant and making it fully cross-platform with an option to compile to Objective-C for a macOS agent
  • Researching & Creating alternative methods for dynamic invocation of Windows APIs without using LoadLibrary and GetProcAddress

Conclusion

Overall, Nim is an interesting language that can jumpstart development without having to be bogged down by the interwoven intricacies of languages such as C++ and Rust. Nim is a jumper cable that allows you to be versatile with the benefit of being cross-platform with a minimal final binary size. Where obfuscation is ripe as well, Nim may slowly rise in popularity over the next few years; although, APTs are already using it. All code snippets except for the nim-strenc section and auto-generated C code can be found here.

Special Thanks

This post wouldn’t be possible without the help of a few amazing people.

  • @bohops Thank you for the motivation and for providing interesting insights to fuel implant development as well as Nim tradecraft.

  • @theWover Thank you for inspiring Nimplant and for some amazing talks bouncing some really fun ideas to advance Nim tradecraft.

  • @its_a_feature_ Thank you for all the help with Nimplant’s development, answering any question along the way and helping with late-night debugging sessions.

  • @discoverscripts Thanks for providing a technical review of the post.

References

https://nim-lang.org/docs/manual.html

https://nim-lang.org/docs/nims.html

https://peterme.net/using-nimscript-as-a-configuration-language-embedding-nimscript-pt-1.html

https://thewover.github.io/Dynamic-Invoke/

More articles from SecBytes

Configuring our Machine for Persistence

CLR Hooking for Persistence via Config Files

February 24th, 2020 · 5 min read

Hiding Your Tracks Bash History

Tricks to hide bash history.

January 20th, 2020 · 2 min read
© 2019–2020 SecBytes
Link to $https://twitter.com/0xBad3rLink to $https://github.com/Bad3r/