// Cybersecurity clarified.

Malicious PowerShell Script Executes Shellcode

2022-06-05   Dawid Farbaniec
Sally (Artificial Intelligence)
Sample with SHA-256 checksum:
has visited the laboratory.

When writing this the sample was detectable by 34 of all 57 security providers and 0 sandboxes. Signatures appearing during the scan indicate that the sample is associated with the software Cobalt Strike | Adversary Simulation and Red Team Operations.

Windows PowerShell Trojan Dropper (Loader)

The sample is PowerShell Script (.ps1 file extension) and is not obfuscated.

The script contains payload which is coded with Base64 algorithm.

Bytes after decoding are in addition XORed with the key 35.

There is a memory buffer allocated for payload bytes with function VirtualAlloc. The buffer is zeroed and the payload bytes are copied to new memory buffer.

The raw payload bytes are then executed in memory.

One can dump easily the payload bytes with this sample code in C# (.NET).
// See for more information

byte[] payload = Convert.FromBase64String("payload cutted for safety and brevity.");

for(int i = 0; i < payload.Length; i++)
    payload[i] ^= 35;

File.WriteAllBytes(@"C:\Users\x\Desktop\dump1.bin", payload);


Payload is dumped. It's worth to calculate hashes for better identification of sample.
Calculating hashes... (dump1.bin)
Calculating deprecated hashes...
The algorithms MD5 and SHA-1 are no longer considered secure. These algorithms should only be used for simple modification checks and should not be used to create hash values used for tampering checks.

Reading the payload code in disassembler one can see that this is shellcode for Windows x64 (64-bit). One can run this shellcode in debugger for analysis for example with BytesLauncher Solution. This quick and dirty solution reads the payload bytes from file and executes in memory for analysis.

Behavior analysis shows that the dumped shellcode sends GET request through HTTP protocol. Of course there will be no reply. The sample is in isolated environment. I do not have the missed part to simulate full behavior.


User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; BOIE9;ENXA)
Proxy-Connection: Keep-Alive
Pragma: no-cache

But this is not the end of analysis.

Under debugger one can see instructions reading memory at specified offsets from GS segment register. It is common to shellcodes.
6548:8B52 60 | mov rdx, qword ptr gs:[rdx+60]     |

48:8B52 18   | mov rdx, qword ptr ds:[rdx+18]     |
48:8B52 20   | mov rdx, qword ptr ds:[rdx+20]     |
48:8B72 50   | mov rsi, qword ptr ds:[rdx+50]     |
48:0FB74A 4A | movzx rcx, word ptr ds:[rdx+4A]    | rcx:"wininet"
4D:31C9      | xor r9, r9                         |
48:31C0      | xor rax, rax                       | rax:payload
AC           | lodsb                              |
3C 61        | cmp al, 61                         | 61:'a'
7C 02        | jl byteslauncher_x64.7FF6BA903037  |

Dynamic analysis shows which API functions are used for network communication.

First there is LoadLibraryA call to load the wininet library. Next the InternetOpenA and InternetConnectA functions are trying to connect to specified IP address.

Function HttpSendRequestA is sending the request. One can cheat the malware in debugger to continue execution by modifying the function result value. This shows that there is new memory buffer allocated with VirtualAlloc and the next stage payload is read with InternetReadFile API function.

Configuration information from sample can be read for example with this code in C# (.NET).
// See for more information

using System.Text;

var payload = File.ReadAllBytes(@"C:\Users\x\Desktop\dump1.bin");

if (payload == null)
    Console.WriteLine("Error reading payload.");

if(payload.Length < 0 || payload.Length > 4096)
    Console.WriteLine("Payload length is probably incorrect.");

List<byte> bytes = new(payload);

string IPAddress = Encoding.ASCII.GetString(
    bytes.GetRange(0x36A, 0x11).ToArray());
string command = Encoding.ASCII.GetString(
    bytes.GetRange(0x186, 0x50).ToArray());
string userAgent = Encoding.ASCII.GetString(
    bytes.GetRange(0x1D6, 0x12F).ToArray());

Console.WriteLine("http://" + IPAddress.TrimEnd((char)0x00) + command);

Sample YARA Rules

Rule for Malicious Loader in PowerShell

rule powershell_shellcode_loader {

        author = ""
        filetype = "PowerShell Script"
        date = "2022-05-19"
        $suspicious_base64 = "[System.Convert]::FromBase64String"
        $delegate_from_func_ptr = "[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer"
        $execute_func = ".Invoke([IntPtr]::Zero)"
        all of them

Rule for Windows x64 Shellcode

rule win64_PEB_traverser {

        author = ""
        filetype = "Binary File"
        date = "2022-05-19"
        /* 6548:8B52 60 | mov rdx,qword ptr gs:[rdx+60] */
        $read_GS_register = { 65 48 8B ?? 60 }
        /* 48:8B52 18 | mov rdx,qword ptr ds:[rdx+18] */
        $read_18_offset = { 48 8B ?? 18 }
        /* 48:8B52 20 | mov rdx,qword ptr ds:[rdx+20] */
        $read_20_offset = { 48 8B ?? 20 }
        all of them

Cobalt Strike HTTP Stager x64

The shellcode from investigated sample and binary file httpstager64.bin from Cobalt Strike sample are like two peas in a pod. This allows to successfully identify and classify the malware sample.

Bibliography [access: 2022-05-19]