Netwalker: from Powershell reflective loader to injected dll
Hi! I have lately started delving into maliious powershell payloads and came across a really intriguing powershell loader for “Netwalker ransomware”, performing fileless attack. Fileless techniques enable attackers to directly load and execute malicious binary in memory without actually storing it on disk by abusing available legitimate tools on victim machine. Such threats leave no trace of execution and are capable of evading any traditional security tools. This post thoroughly discusses how first stage powershell script filelessly loads and executes embedded payload through reflective Dll injection.
SHA-256 hash of the sample being analyzed: f4656a9af30e98ed2103194f798fa00fd1686618e3e62fba6b15c9959135b7be
Prior knowledge required:
- Basic Powershell understanding
- using .NET reflection to access Windows API in PowerShell
- Windows APIs for Process/Dll injection
This is around ~5 MBs of powershell script using three layers of encoding, encryption and obfuscation respectively to hide ransomware dll and supporting powershell commands for reflective Dll injection. The uppermost layer executes very long base64 encoded command (screenshot covers only a small portion of this command)
Processing Base64 encoded layer 1
In order to get decoded output from initial script, I shall run powershell script into my VM’s Powershell ISE but as the Invoke-Expression cmdlet will process base64-encoded payload and execute the ransomware therefore, I’ll modify the script for debugging by replacing this comdlet with a variable to store result of base64 decoded command and dump output in a file as shown in the figure below
Processing Encrypted layer 2
base64 decoded second layer once again contains a very long bytearray in hex format which is processed in two steps
1) bytearray contents are decrypted in a for loop with 1 byte hardcoded xor key
2) decrypted contents are stored as ASCII string in another variable in order to be able to create scriptblock for decrypted contents and execute it using Invoke-Command cmdlet
but I shall also modify second layer to get decrypted layer three contents and dump result into another output file as shown in the figure below
decryptedlayer3.ps1 now contains the obfuscated layer three powershell script embedding ransomware dlls in bytearrays and other commands to process the malicious payload
Processing Obfuscated layer 3
Let’s start digging into layer three powershell script which is quite obfuscated having lengthy and random string variable and routine names responsible to drop final payload. It is required to perform following steps in order to execute Netwalker ransomware on victim’s machine
- define variables to invoke in-memory Windows API function calls without compilation
- define routines to load dll without using Windows loader
- detect environment
- get PID of a legitimate process from a list of running processes and inject payload via custom loader
- delete shadow copies
First off, it defines required variables and routines:
to invoke in-memory Windows API function calls without compilation, C# code to declare structs and enums for memory manipulation is defined inside a variable as shown below
and to invoke kernell32.dll APIs using wrapper .Net methods available in powershell
final command in this case will let us instantiate objects by making Microsoft .Net core classes available in our powershell session and ensure ransomware’s true memory residence through reflection.
Following set of routines help correctly compute required memory addresses and relocations by casting integer datatypes (signed integers to Unsigned integers and vice versa) so that the script could act as its own custom loader and load dll without using Windows loader
Finally it defines a bunch of routines to write embedded malicious binary into another process’s memory and execute it.
Script starts its execution by detecting underlying processor’s architecture to know whether it is running on x86 or amd64 and to prepare 32-bit or 64-bit dll accordingly using following if-else block
later it allocates memory in current process’s address space and starts writing dll on the allocated memory using following for loop
snapshot of object containig dll that gets written into current process’s memory
after that it calls following routine with certain parameters to inject payload by specifying a legitimate target process which is ‘explorer.exe’ in this case along with memory location pointer for buffer containg Dll and size of the buffer containing dll
this routine finds PID of explorer.exe form a list of running processes and passes obtained PID to final routine
which first reflectively injects ransomware dll into explorer.exe by allocating a chunk of memory of specified size into its address space and writing ransomware dll on the allocated memory and then executes it by creating a thread that runs in the virtual address space of Explorer.exe process
and in the end deletes shadow copies of the data being held on the system at that particular time to completely eliminate any possibility of recovering it and performs required memory cleanup using following set of commands
as soon as script exits, FE026B-Readme.txt window appears on the system with ransom message and all encrypted files with fe026b extension are no longer accessible
Note: Ransomware dll being injected can be dumped into a binary file in powershell script, which has SHA-256 302ff75667460accbbd909275cf912f4543c4fb4ea9f0d0bad2f4d5e6225837b hash but it can be seen that it is 64-bit PE file and first two bytes in this case have wrong hex value 0xDEAD
replacng first two bytes 0xDEAD with 0x4D5A in DOS header in HxD editor would result in Netwalker ransomware dll with f93209fccd0c452b8b5dc9db46341281344156bbedd23a47d2d551f80f460534 SHA-256 hash.
Deciphering Netwalker x86-64 DLL
Let’s load final dll in IDA and perform basic static analysis first, I’ll start by looking up for strings, but they are mostly useless, moreover, it has only one export i.e., main entry which seems to implement all its functionality
second important thing to note here is that it has no
If I randomly pick a CRC32 reference and look it up in dll, it is found in sub_180005D60 routine being used in a loop
do-while loop in decompiled routine shows CRC32 division flow
let’s rename this routine to crc32_checksum and look for its cross references, result shows it is cross referenced two times in sub_180001000, if this routine is subsequently checked for further cross references, it shows ~165 references
we can assume here that the routine sub_180001000 being cross referenced ~165 times is possibly decrypting strings, I’ll rename it to decrypt_strings
now let’s take a closer look at sub_180001490 routine which almost has all the Xrefs to decrypt_strings, following code shows it is taking two arguments v1, which is being used in all of its calls and a 4-byte hex value which seems to be CRC32 hash and retrun value is being stored to different offsets of an array
this routine has multiple similar code blocks but with different hash values, here it can be assumed that it is decrypting APIs from different libraries, let’s rename it to resolve_imports and look for its Xrefs which leads to DLL’s main DllEntryPoint routine - now it’s time to look into it dynamically.
First routine that is being called by DLL is resolve_imports, which in turn calls sub_180001310 routine, it is taking 0x84C05E40 hash value as parameter, a quick Google search shows it is for “ntdll.dll” which can also be verified with Python
this routine returns handle for ntdll.dll library, later it takes another hash value 0xA1D45974 which is resolved to RtlAllocateHeap API, it is first called to allocate a block of memory on heap to later store resolved addresses there on different array indexes
this routine decrypts and resolves serveral APIs from ntdll.dll, kernel32.dll, advapi32.dll, use32.dll, mpr.dll, shell32.dll, netapi32.dll, ole32.dll, oleaut32.dll and psapi.dll libraries. I wrote a simple IDAPython script here which resolves CRC32 hashes and adds resolved value in comment
after resolving imports, it continues to check for stomped MZ header 0xDEAD by first copying header value 0xDEAD in eax, setting up rbx with a certain address and later subtracting 0x400 from rbx in each iteration to reach image’s base address as shown by the loop in figure below
if 0xDEAD header value is intact (i.e., making sure DLL is being run injected in explorer.exe), it continues further to fix MZ header in memory and read image’s resources - otherwise it’ll throw ACCESS_VIOLATION exception and exits
after required resource has been loaded in memory, sub_18000EAF0 routine processes it by first extracting first 4 bytes of data which is probably length of key, next 7 bytes (cZu-H!<) are extracted as RC4 key which is being used to decrypt rest of the payload - following code from sub_18000EAF0 routine implemets 3 recognizable RC4 loops 1. Initialization (creating Substitution Box) 2. Scrambling Substitution box with key to generate a pseudo-random keystream 3. xoring keystream with rest of the data
decrypted data seems to be malware’s embedded configuration in json format
this can also be verified by copying resource as hex string along with 7-byte hex key on Cyberchef
next routine sub_180004600 parses configuration to get list of file extensions which needs to be encrypted, default paths and files that should be whitelisted, attacker’s ToR info and ransomware note along with ransomware note file name and format, subsequent routines decrypt ransom note with AES decryption algorithm by using 256-bit hardcoded key, checks running processes to kill any blacklisted process and eventually performs ransomware activity.
That’s it. See you next time.
Sources:
- https://blog.trendmicro.com/trendlabs-security-intelligence/netwalker-fileless-ransomware-injected-via-reflective-loading/
- https://any.run/report/f4656a9af30e98ed2103194f798fa00fd1686618e3e62fba6b15c9959135b7be/ca44ad38-0e46-455e-8cfd-42fb53d41a1d