Nikhil "Kaido" Hegde

M&M: Malware and Musings

View on GitHub

Source

Analysis

Functionality and Obfuscation

The payload launcher script had primarily two forms of obfuscation:

[void]([System.Text.Encoding]::UTF8.GetBytes("XaaJYkDrACzA"))
...
$YHlxhIabnbiAggZZ = @(4623,9444,6378,6182,3132,5090,2265,9832,2541,9313,8790,8274,8374,3964,1472,9474,4825,5789,2665,3346,9380,3162,5690,9550,2954)
foreach ($fkegcfDWcAcyhwZs in $YHlxhIabnbiAggZZ) { $null = [Math]::Sqrt($fkegcfDWcAcyhwZs) }
function PxDAgSfVrK {
    $vHGBTQcQhWihkOpP = @(1,2,3,4,5)
    foreach ($XmInOoVJszWTbpKf in $vHGBTQcQhWihkOpP) {
        $result = $XmInOoVJszWTbpKf * 2
    }
}
PxDAgSfVrK
...
foreach ($gIYjeRkcYgUZhHJd in $qrdtCMfWpbqJidck) { $null = $gIYjeRkcYgUZhHJd.ToLower() }
if (205 -eq 205) { $jHypmcWMPaQtBXp = "FrsnYjHYk" }
if (263 -eq 263) { $dkQpjcPAYBTmbLJ = "oiPmLfdYw" }

Payload Launcher Deobfuscation

Utilities: https://github.com/nikhilh-20/re_tools/tree/main/powershell

> .\PsUnwrap-TrueIf.ps1 -InputFile C:\Users\Ashura\Desktop\payload.ps1 -OutputFile C:\Users\Ashura\Desktop\payload_pass1.ps1                                   {"changed":4,"output_path":"C:\\Users\\Ashura\\Desktop\\payload_pass1.ps1","output_bytes":1826435,"input_bytes":1826519}

> .\PsRemove-DeadCode.ps1 -InputFile C:\Users\Ashura\Desktop\payload_pass1.ps1 -OutputFile C:\Users\Ashura\Desktop\payload_pass2.ps1 -aggressive
{"input_bytes":1826435,"output_path":"C:\\Users\\Ashura\\Desktop\\payload_pass2.ps1","by_reason":"4x no-op function, 1x pure statement, 20x dead store, 5x non-functional loop","changed":30,"output_bytes":1825024,"aggressive":true}

After deobfuscation, the resultant payload launcher script was ~1.75 MB.

Although the payload launcher script is the same for the initial 32-bit or 64-bit Powershell process, it was embedded as-is twice in the Powershell script. Whether this was an intentional effort to artificially increase the Powershell script size is not known, but it can serve as an evasionary measure.

Payload

The resulting deobfuscated launcher script embeds the main payload in base64 + xor-encrypted form. After decryption, the main payload pointed to a .NET executable which was invoked through reflection by the launcher.

[System.Reflection.Assembly]::Load($payload)
$entry = $assembly.EntryPoint
$entry.Invoke($null, @($null))