CommieLoader: Nutzung von SUMATRA PDF für das Forward-Sideloading von DLLs

CommieLoader

Wichtigste Erkenntnisse

  • Im Rahmen eines Einsatzes zur Incident Response identifizierte das SECUINFRA Falcon Team eine interessante Malware-Probe mit dem Codenamen „CommieLoader“, die sich als Bewerbungsformular tarnt.
  • CommieLoader installierte einen Cobalt Strike Beacon, der vom Angreifer für die Command-and-Control-Kommunikation genutzt wurde

Überblick

Unserem Kunden wurde per E-Mail ein ZIP-Archiv zugespielt, welche drei Dateien enthielt. Diese enthielt eine bisher unbekannte Malware, die wir aufgrund gewisser Artefakte “CommieLoader” nennen, welche schlussendlich zu einer Datenexfiltration führte. Im folgenden untersuchen wir die komplette Angriffskette dieser Malware.

Zeitrahmen

Die initiale Infektion erfolgte in dem Download-Verzeichnis des Nutzers. Durch die Ausführung des Sumatra Installers, welche in dem ZIP-Archiv enthalten war, wurde die maliziöse dbgcore.dll des selben Verzeichnisses mithilfe DLL-ForwardSideloadings geladen und Schadroutinen ausgeführt.

Im Folgenden sehen wir uns gemeinsam die schadhafte Datei dbgcore.dll an.

Analyse der Schadsoftware

Dbgcore.dll : a9121e70c39de2c10e6790da4aa3a22079242a201da2c1aeeb4ed65070e68e93

SumatraPDF installer (“Version_Bewerbung_2.0_202566_Bewerbungsnummer_0234521870_Datum_0000000200.exe“):

cb1d73323d3d80004ada185844b0d461abd9ded736d5dc690607f935b4f2b58a

Settings.txt :

b9fac5fd68f333b9459fa4b0111da8fba64a20022df8ea8595eae6a2fc4b9d9d

Im Rahmen unserer Untersuchungen fanden wir auf einer der Systeme unserer Kunden ein ZIP-Archiv das vermeintlich Bewerbungsunterlagen beinhalten sollte, stattdessen aber ein Installationsprogramm des SumatraPDF Viewers, eine Textdatei mit dem Namen “Settings.txt” und eine Dymanic-Linked-Library (DLL) namens “dbgcore.dll” enthielt. Bei dem Installationsprogramm handelt es sich um eine legitime PE, welche eine Signatur von Krzysztof Kowalczyk (dem Entwickler von SumatraPDF) enthält und deren Hash mit dem offiziellen Installer von SumatraPDF Version 3.3.3 übereinstimmt, weshalb unser Fokus vorerst auf die dbgcore.dll fiel.

dbgcore.dll wäre, insofern legitim, eine Windows-Systemdatei für Diagnosefunktionen und Speicherabbilder, welche direkt von Microsoft als Teil der Entwicklertools bereitgestellt wird. Sie hält sich in der Regel allerdings nicht in Dokumentenverzeichnissen auf. Unsere DLL war zudem mit zwei ungültigen Zertifikaten ausgestattet, welche im folgenden gezeigt werden:

Abbildung 1 & 2: Ungültige Zertifikate von „ESET, spol. s.r.o.“

Wenn man sich die Tabelle der importierten DLLs des Sumatra-Installers ansieht, fällt auf, dass dbgcore.dll gar nicht direkt importiert wird. Unsere Hauptaufgabe in diesem Schritt der Analyse ist also zu klären, wie ein Aufruf des Sumatra-Installationsprogramms (des „Bewerbungsschreibens“) zur Ausführung der potenziell schadhaften dbgcore.dll führt.


Abbildung 3: Importierte DLLs des Sumatra Installers

Wenn wir jedoch den Installer in einem Disassembler öffnen, sehen wir, wie der Installer versucht, eine DLL namens “dbghelp.dll” in den Prozess zu laden.

Abbildung 4: Importierung der legitimen dbghelp.dll

Auf dem ersten Blick scheint der Installer die DLL dbghelp.dll sauber zu laden: der absolute Pfad der DLL wird mithilfe von PathAppendW() aus dem Rückgabewert von GetSystemDirectoryW() und ”dbghelp.dll” zusammengebaut und erst dann wird die DLL mithilfe von LoadLibraryW() importiert.

Wenn wir uns jedoch anschauen, wozu der Handle der importierten DLL verwendet wird, sehen wir einen Aufruf zu GetProcAddress() mit MiniDumpWriteDump als zweiten Parameter:

Abbildung 5: Auflösung der Adresse zu MiniDumpWriteDump

Wenn man nun einen Blick in die Export-Tabelle der dbghelp.dll wirft, sieht man, dass die Funktion MiniDumpWriteDump zu „dbgcore.dll“ geforwarded wird:

Abbildung 6: MiniDumpWriteDump als forwarded function, welche in der dbgcore.dll implementiert ist

Um herauszufinden, wie der Einstiegspunkt der dbgcore.dll aufgerufen wird, haben den Sumatra Installer mit einer legitimen dbgcore.dll im selben Verzeichnis, in einem Debugger geöffnet. Dabei haben wir einen Breakpoint auf GetProcAddress()gesetzt und nach einer Weile single stepping stellten wir fest, dass LdrpCallInitRoutine() mit dem Einstiegspunkt der aufzurufenden DLL aufgerufen wird.

Abbildung 7: Callstack von GetProcAddress zu LdrpCallInitRoutine

Wie man hier im Screenshot erkennen kann, wird hier LdrpCallInitRoutine() mit der Addresse des Einstiegspunktes im RCX Register (1. Parameter) aufgerufen.

Abbildung 8: Aufruf zu LdrpCallInitRoutine mit dem Einstiegspunkt der dbgcore.dll im RCX register (Erster Parameter bei __fastcall)

Dies bekräftigt unsere Vermutung,  dass sich der Schadcode im Einstiegspunkt der maliziösen dbgcore.dll befindet. Im Vergleich zu normalem DLL-Search-Order-Hijacking, wird in diesem das Laden, der maliziösen DLL von einer weiteren DLL übernommen, welche in diesem Fall die dbghelp.dll ist.

Exports

Wenn wir einen Blick in die Export-Tabelle unserer DLL werfen, erkennen wir, dass die vorliegende Datei dbgcore.dll insgesamt 38 Funktionen exportiert. Die echte dbgcore.dll Datei, welche von Microsoft herausgegeben wird, exportiert hingegen nur zwei Funktionen (MiniDumpReadDumpStream und MiniDumpWriteDump). Zudem sind aktuelle, von Microsoft bereitgestellte Versionen der DLL üblicherweise signiert, was hier nicht der Fall ist. Die folgenden exportierten Funktionsnamen in unserem dbgcore.dll Sample stechen besonders hervor:


ladeComm
antifa
kommEncoding
helloFriendssscommIsCom
stalin
SW3_HashSyscall
SC_Address
GetStalinNumber
GetStalin
AAAWriteaaaaaVirtualComm
ResumeComm
ProtectComm

Wir erkennen, dass die zwei originalen dbgcore.dll-Funktionen MiniDumpWriteDump und MiniDumpReadDumpStream nicht in dieser Liste vorzufinden sind. Stattdessen gibt es Verweise auf “stalin”, “getStalinNumber”, “SW3_HashSyscall”, welche eher unübliche Verweise für eine DLL sind. Somit kommen wir zu dem Schluss, dass es sich um eine schadhafte DLL ohne Bezug zur der legitimen dbgcore.dll von Microsoft handelt.

Deep Dive

Wenn wir die dbgcore.dll in einem Disassembler öffnen, und zur Hauptfunktion (DllMain) springen, sehen wir folgendes:

Abbildung 9: DllMain

Der Schadcode in der mw_Entrypoint() Funktion wird also nur ausgeführt, wenn die DLL mithilfe von LoadLibrary() oder einer ähnlichen API geladen wird und somit fdwReason auf DLL_PROCESS_ATTACH gesetzt wird.

In der mw_Entrypoint() Funktion, können wir einen groben Überblick über die Schadsoftware bekommen.

Abbildung 10: Einstiegspunkt der Dll (Schadcode)

Hier erkennt man drei Funktionsaufrufe welche überprüfen, ob das Programm innerhalb einer Sandbox oder Analyseumgebung ausgeführt wird, gefolgt von zwei weiteren Funktionsaufrufen, welche darüber entscheiden, ob der Schadcode ausgeführt wird oder nicht.

Anti-Sandbox Mechanismen

Die erste Funktion, mw_ramCheck(), fragt die Größe des Arbeitsspeichers ab. Nur wenn mehr als ~4.19 GB auf dem System installiert ist wird die Ausführung an den Aufrufer zurückgegeben. Falls dies nicht der Fall ist wird tooLittleRam() aufgerufen, welches das Programm stoppt. Ein mögliches Szenario, welches zum Abbruch führen würde, wäre zum Bespiel die Ausführung in einer Sandbox oder virtuellen Maschine, welche weniger als 4GB RAM hat.

Abbildung 11: Sandbox-Check via Abfrage des physischen Arbeitsspeichers

Die nächste Funktion mw_detectResolution() überprüft, ob die Position des rechten, unteren Pixels des Desktopfensters größer als 1023 ist. Falls dies nicht der Fall sein sollte, nimmt das Programm an, dass es unter einer kontrollierten Umgebung, welche oftmals Auflösungen unter 1280×1024 Pixeln verwenden, ausgeführt wird und stoppt die weitere Ausführung. Andernfalls wird die Ausführung an den Aufrufer zurückgegeben.

Abbildung 12: Sandbox-Check via Abfrage der Bildschirmauflösung des Desktops

Der dritte Sandbox-Check mw_cpuCount() bricht das Programm ab, falls die Anzahl der Prozessorkerne des Systems weniger als 2 ist.

Abbildung 13: Sandbox Check via Abfrage der Prozessorkerne

Der vierte Sandbox-Check  mw_timeDetect() überprüft, ob die Sleep API gehooked wird. Sandboxen, Analyse-Tools oder Endpoint Detection and Response Lösungen bedienen zum Teil diesem Trick, um längere Wartezeiten eines schadhaften Programms zu verkürzen. Es wird zuerst GetTickCount aufgerufen, gefolgt von einem Aufruf von Sleep() mit einer Sekunde. Danach wird GetTickCount() ein weiteres mal aufgerufen und die Differenz zwischen dem zweiten Tickcount mit dem initialen Tickcount gegen 0.9 Sekunden verglichen. Falls das Ergebnis niedriger ist als 0.9 Sekunden, wird die Sleep-Funktion gehooked und der Wert 1 zurückgegeben, wodurch das Programm dann abbricht.

Abbildung 14: Timing check ob Sleep gehooked wird

Der letzte Sandbox-Check, mw_hostnameCheck(), ist trivialer und vergleicht den Namen des Hosts gegen die Namen von populären Sandboxen. Nur wenn dabei kein Treffer erzielt wird, wird 0 zurückgegeben und die Ausführung des tatsächlichen Schadcodes beginnt, vorausgesetzt, nicht einer der vorherigen Checks hat angeschlagen.

Abbildung 15: Sandbox Check via Abfrage des Hostnames

Nur wenn alle fünf Sandbox Checks überstanden wurden, wird der restliche Code ausgeführt. Zuerst wird die globale Variable CmdLine mit der Zeichenkette “WmiPrvSE.exe”, d.h. der Executable des Windows Management Instrumentation (WMI) Provider Services, gefüllt. Danach wird die Funktion mw_read_settings() aufgerufen.

Abbildung 16: CommieLoaders’ Kernfunktionalitäten

mw_read_settings() setzt das aktuelle Verzeichnis auf das Verzeichnis, in dem sich der Installer befindet und liest den Inhalt der „Settings.txt“ Datei in die globale Variable my_payload.

Abbildung 17: Funktion, welche den Inhalt der Settings.txt in den my_payload buffer liest

Wenn wir die Datei „Settings.txt“ öffnen, erkennen wir unter anderem verschiedene Namen von Kommunisten, vielfältige MAC-Adressen und Ausschnitte aus dem komministischem Manifest (viele davon enthalten Schreibfehler). Demnach der Name “CommieLoader”. Bei genauerer Betrachtung scheint sich ein Muster zu ergeben, nach dem die Häufung der „trotzki“ Strings jeweils für ein Null-Byte (0x00) stehen könnte. Daher stellen wir die These auf, dass Settings.txt einen enkodierten Payload, ggf. die nächste Ausführungsstufe der Schadsoftware enthält. Das Enkodierungsverfahren wäre demnach eine Wörterbuch-Ersetzung, bei welcher für jeden Hexadezimalwert 0x00 bis 0xFF ein entsprechendes Wort als Übersetzung verwendet wird.

Abbildung 18: Ausschnitt des Inhalts der Settings.txt

Bei genauerer Betrachtung der Datei dbgcore.dll fällt auf, dass die Datei eine passende Datenstruktur mit 256 Einträgen enthält, welche sich als Wörterbuch eignet und wie vermutet auch mit „trotzki“ als Wert für 0x00 beginnt.

Abbildung 18: Ausschnitt des Wörterbuchs aus dbgcore.dll

Um das Wörtbuch aus dbgcore.dll zu exportieren, haben wir ein Python Skript entwickelt, welches die Datei Settings.txt dekodieren kann.

„`

#!/usr/bin/env python3

„““

Decoder for commieLoader Settings.txt payload.

dbgcore.dll contains a 256-entry wordlist stored as fixed-width (5000-byte) slots

starting at offset 0x8B40. Each word in Settings.txt maps to one byte of the

original binary payload.

„““

import sys

import argparse

DLL_PATH = „dbgcore.dll“

SETTINGS_PATH = „Settings.txt“

OUTPUT_PATH = „decoded_payload.bin“

WORDLIST_OFFSET = 0x8B40   # offset of first entry („trotzki“ = 0x00) in dbgcore.dll

WORDLIST_STRIDE = 5000     # each entry occupies a fixed 5000-byte slot

def load_wordlist(dll_path: str) -> dict[str, int]:

    with open(dll_path, „rb“) as f:

        data = f.read()

    word_to_byte: dict[str, int] = {}

    for i in range(256):

        offset = WORDLIST_OFFSET + i * WORDLIST_STRIDE

        chunk = data[offset:offset + WORDLIST_STRIDE]

        end = chunk.find(b“\x00″)

        word_bytes = chunk[:end] if end != -1 else chunk

        word = word_bytes.decode(„utf-8“)

        word_to_byte[word] = i

    return word_to_byte

def decode(settings_path: str, word_to_byte: dict[str, int]) -> bytearray:

    with open(settings_path, „r“, encoding=“utf-8″) as f:

        content = f.read()

    # Tokens are separated by „, “ strip only leading space to preserve

    # trailing spaces that are part of some token names (e.g. „Ricardo „).

    tokens = [t.lstrip(“ „) for t in content.split(„,“)]

    unknown = {t for t in tokens if t and t not in word_to_byte}

    if unknown:

        print(f“[!] Warning: {len(unknown)} unknown token(s): {unknown}“, file=sys.stderr)

    out = bytearray()

    for t in tokens:

        if t:

            out.append(word_to_byte.get(t, 0))

    return out

def main() -> None:

    parser = argparse.ArgumentParser(description=“Decode commieLoader Settings.txt payload“)

    parser.add_argument(„–dll“,    default=DLL_PATH,      help=f“Path to dbgcore.dll (default: {DLL_PATH})“)

    parser.add_argument(„–input“,  default=SETTINGS_PATH, help=f“Path to Settings.txt (default: {SETTINGS_PATH})“)

    parser.add_argument(„–output“, default=OUTPUT_PATH,   help=f“Output path for decoded binary (default: {OUTPUT_PATH})“)

    args = parser.parse_args()

    print(f“[*] Loading wordlist from {args.dll} …“)

    word_to_byte = load_wordlist(args.dll)

    print(f“[+] Loaded {len(word_to_byte)} wordlist entries“)

    print(f“[*] Decoding {args.input} …“)

    payload = decode(args.input, word_to_byte)

    print(f“[+] Decoded {len(payload)} bytes“)

    with open(args.output, „wb“) as f:

        f.write(payload)

    print(f“[+] Written to {args.output}“)

if __name__ == „__main__“:

    main()

„`

Die dekodierte Datei konnten wir durch YARA Regel-Treffer und manuelle Analyse als CobaltStrike Beacon Shellcode identifizieren. CobaltStrike ist ein für Red-Teaming konzipiertes Adversary Emulation Framework, welches sowohl für legitime Sicherheitstests, als auch für Command&Control Zwecke von Angreifern mit maliziösen Motiven eingesetzt wird. Um eine Einordnung zu treffen und näheres über die Funktionalität des CobaltStrike Beacons herauszufinden, haben wir die Konfiguration dessen extrahiert.

Der Cobalt Strike Payload enthält die folgende Konfiguration, welche die Funktionalität und das Verhalten bzw. die Tarnung der Schadsoftware festlegt. Kommuniziert wird über das HTTPS Protokoll auf Port 443/tcp. Der Teamserver (die Command&Control Infrastruktur des Angreifers) wird unter der Domain refugee-help[.]com erreicht und ist als Kontaktformular getarnt, damit der Web-Traffic in einer Analyse nicht allzu sehr heraussticht. Der Beacon tarnt sich als das Windows Systemprogramm wmiprvse.exe, um im Prozessbaum nicht aufzufallen. Die letzte, für uns besonders relevante Information, ist der Watermark-Wert „987654321“, welcher normalerweise pseudonym den Lizenznehmer der Software identifiziert. Die absteigende Zahlenfolge ist jedoch an Anzeichen dafür, dass es sich um eine unlizensierte Kopie von CobaltStrike handeln muss, welche gerne unter Cyberkriminellen in Online-Foren zum Tausch oder Verkauf angeboten werden.

BeaconType                       – HTTPS

Port                             – 443

SleepTime                        – 30000

MaxGetSize                       – 16798776

Jitter                           – 50

MaxDNS                           – Not Found

PublicKey_MD5                    – 1089d58afc804cfab88e6e2aca60e3f3

C2Server                         – refugee-help.com,/dpixel

UserAgent                        – Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.3600 (KHTML, like Gecko) Chrome/135.50.90.0 Safari/537.3600

HttpPostUri                      – /contact.php

Malleable_C2_Instructions        – Base64 decode

HttpGet_Metadata                 – Metadata

                                     base64

                                     header „Cookie“

HttpPost_Metadata                – ConstHeaders

                                     Content-Type: application/x-www-form-urlencoded

                                   ConstParams

                                     name=OSF

                                     [email protected]

                                     subject=Resource support

                                   SessionId

                                     header „Cookie“

                                   Output

                                     base64url

                                     prepend „message=“

                                     print

PipeName                         – Not Found

DNS_Idle                         – Not Found

DNS_Sleep                        – Not Found

SSH_Host                         – Not Found

SSH_Port                         – Not Found

SSH_Username                     – Not Found

SSH_Password_Plaintext           – Not Found

SSH_Password_Pubkey              – Not Found

SSH_Banner                       –

HttpGet_Verb                     – GET

HttpPost_Verb                    – POST

HttpPostChunk                    – 0

Spawnto_x86                      – %windir%\syswow64\wbem\wmiprvse.exe -Embedding

Spawnto_x64                      – %windir%\sysnative\wbem\wmiprvse.exe -Embedding

CryptoScheme                     – 0

Proxy_Config                     – Not Found

Proxy_User                       – Not Found

Proxy_Password                   – Not Found

Proxy_Behavior                   – Use IE settings

Watermark_Hash                   – NtZOV6JzDr9QkEnX6bobPg==

Watermark                        – 987654321

bStageCleanup                    – True

bCFGCaution                      – True

KillDate                         – 0

bProcInject_StartRWX             – False

bProcInject_UseRWX               – False

bProcInject_MinAllocSize         – 24576

ProcInject_PrependAppend_x86     – b’D@KCLH\x90f\x90\x0f\x1f\x00f\x0f\x1f\x04\x00\x0f\x1f\x04\x00\x0f\x1f\x00\x0f\x1f\x00′

                                   Empty

ProcInject_PrependAppend_x64     – b’D@KCLH\x90f\x90\x0f\x1f\x00f\x0f\x1f\x04\x00\x0f\x1f\x04\x00\x0f\x1f\x00\x0f\x1f\x00′

                                   Empty

ProcInject_Execute               – kernel32.dll:BaseThreadInitThunk

                                   NtQueueApcThread-s

                                   kernel32.dll:LoadLibraryA

                                   CreateRemoteThread

                                   RtlCreateUserThread

                                   SetThreadContext

ProcInject_AllocationMethod      – NtMapViewOfSection

bUsesCookies                     – True

HostHeader                       –

headersToRemove                  – Not Found

DNS_Beaconing                    – Not Found

DNS_get_TypeA                    – Not Found

DNS_get_TypeAAAA                 – Not Found

DNS_get_TypeTXT                  – Not Found

DNS_put_metadata                 – Not Found

DNS_put_output                   – Not Found

DNS_resolver                     – Not Found

DNS_strategy                     – round-robin

DNS_strategy_rotate_seconds      – -1

DNS_strategy_fail_x              – -1

DNS_strategy_fail_seconds        – -1

Retry_Max_Attempts               – 0

Retry_Increase_Attempts          – 0

Retry_Duration                   – 0

Sobald der Shellcode dekodiert ist, wird ein neuer Prozess erstellt. Wie oben erwähnt, wurde zuvor die Zeichenkette “WmiPrvSE.exe” in die globale Variable CmdLine kopiert. Und mit dem Aufruf zu CreateProcessA() wird ein Prozess davon im suspendierten Zustand erstellt.

Abbildung 19: Erstellung des suspendierten WmiPrvSE Prozesses

Sobald der Prozess erstellt ist, erkennen wir mehrere Syscall-Aufrufe, die auf klassische Process-Injection hindeuten. Der Aufruf ZwAllocateVirtualMemory() allokiert einen Buffer im neuerstellten Prozess, welche als Speicher für den Shellcode dient. Dieser wird mittels ZwWriteVirtualMemory() in den Addressspeicher des Prozesses geschrieben, welcher mit ZwProtectVirtualMemory() ausführbar gemacht wird.

Danach wird mithilfe von ZwQueueApcThread() der Shellcode an die APC Queue des Prozesses angehangen und mittels ZwResumeThread() wird der Shellcode schließlich auf dem Hauptthread ausgeführt.

Abbildung 20: Übertragung des Shellcodes in den Speicherbereich des WmiPrvSE Prozesses, gefolgt von der der Ausführung des Shellcodes

Nachdem der Shellcode in den Prozess geschrieben und ausgeführt wurde, beginnt das Programm Persistenz-Mechanismen durchzuführen. Diese finden sich in der Funktion copyfiles().

Es wird zuerst der Nutzername des aktuellen Nutzers abgefragt, um dessen Dokumenten-Verzeichnis zu finden. Nachdem die Zeichenkette des Pfads zum Dokumenten-Verzeichnis zusammengebaut wurde, werden alle drei Dateien „Settings.txt“, dbgcore.dll und der Sumatra Installer in das Verzeichnis kopiert.

Abbildung 21: Kopierung des Sumatra-Installers, dbgcore.dll und der Settings.txt in den Documents Ordner des Nutzers

Zuletzt wird die write_auto-Funktion aufgerufen, welche einen Autorun Registry Key getarnt als „Firefox_Updater_Version_2.3.1000“ für den Installer erstellt. Dieser führt dazu, dass der Installer bei jedem Systemstart ausgeführt wird, sodass der Angreifer den Zugang auf das System nicht nach einem Neustart verliert.

Abbildung 22: Eintragung des Autorun-Keys im Registry

Abbildung 23: Registry Eintrag nach Ausführung der Schadsoftware

Kleiner Bonus: Wir konnten Artefakte eines Vectored-Exception-Handlers in der DLL finden, welche einen Hook auf die EtwEventWrite-Funktion setzen. Jedoch konnten wir keine Anzeichen dazu finden, dass dieser Handler registriert wird.

Abbildung 24: Ein Vectored-Exception-Handler, welches EtwEventWrite mit einer ret Instruktion überschreibt (Jedoch wird er nie registriert)

Noteworthy

Zu bemerken ist vielleicht noch, dass unser Sample nur bedingt Anti-AV Maßnahmen ergriffen hat. Auf dem System unseres Kunden lief Microsoft Defender, der, soweit wir sehen konnten, während des gesamten Infektionszeitraumes aktiv war und das Sample mehrfach scannte. Zu bemängel hatte er am Ende nichts, auch wenn er an der dbgcore.dll durchschnittlich am längsten scannte.

In zwei Fällen identifizierte er jedoch maliziöse Aktivitäten („VirTool:MSIL/Deimos.A!MTB“ und „Trojan:Win32/Sabsik.EN.B!ml“) im Zusammenhang mit WmiPrvSE.exe:

Abbildung 25: Detektionen des Windows Defenders

In beiden Fällen wurde die WmiPrvSE.exe vom Windows Defender in die Quarantäne gesteckt.

Wir konnten den „CommieLoader“ keiner bereits bekannten Malware-Familie zuordnen. Bei der Suche danach in Plattformen zur Analyse und zum Austausch von Malware-Samplen konnten wir durch eine YARA-Regel ein weiteres Sample (SHA256: 127c525b0107045c39d4c956d51a16aba6b28e8a08cb1687e3fe7fc1f16e0de5)finden, das sich ebenfalls in einem ZIP-Archiv („bewerbung_gesamt.zip“) mit einer Settings.txt und dem SumatraPDF Installers befand. Der Payload dieses Samples ist nahezu identisch mit unserem Sample bis darauf, dass statt ein WmiPrvSE.exe ein msedge.exe-Prozess für die APC-Injection genutzt wird. Dies deutet darauf hin, dass es sich bei diesem Sample um ein Testsample handeln könnte, um die Detektion durch AVs und EDRs zu testen. Nach Angaben des vermeintlichen Angreifers hätte wohl auch der Elastic EDR sein Sample detektiert. Ob es sich bei unseren Angriff um einen gezielten Angriff gegen unseren Kunden handelt, oder das Sample in Zukunft noch öfter auftauchen wird, wird die Zukunft sagen.

Anhang

Host-based Indicators

FilenameSHA256Description
dbgcore.dlla9121e70c39de2c10e6790da4aa3a22079242a201da2c1aeeb4ed65070e68e93Malicious dll loaded by the SumatraPDF installer via Dll ForwardSideloading
Version_Bewerbung_2.0_202566_Bewerbungsnummer_0234521870_Datum_0000000200cb1d73323d3d80004ada185844b0d461abd9ded736d5dc690607f935b4f2b58aLegitimate SumatraPDF installer
Settings.txtb9fac5fd68f333b9459fa4b0111da8fba64a20022df8ea8595eae6a2fc4b9d9dText file containing an encoded Cobalt Strike beacon

Zusätzliche Host-Based IoC’s:

Software\Microsoft\Windows\CurrentVersion\Run\Firefox_Updater_Version_2.3.1000:

„C:\Users\[Username]\Documents\Version_Bewerbung_2.0_202566_Bewerbungsnummer_0234521870_Datum_0000000200.exe

Network-based Indicators

TypeIndicator
C2refugee-help[.]com
URIs/dpixel, /contact.php

Detection Rule

rule SI_MAL_LDR_CommieLoader_Apr13 {

 meta:

  version = „1.0“

  date = „2026-04-13“

  modified = „2026-04-14“

  status = „RELEASED“

  sharing = „TLP:CLEAR“

  source = „SECUINFRA Falcon Team“

  description = „Detects the dbgcore.dll used in the CommieLoader campaign“

  category = „malware“

  mitre_att = „T1129, T1055, T1112“

  // SHA-256 hashes of our observed samples

  hash1 = „a9121e70c39de2c10e6790da4aa3a22079242a201da2c1aeeb4ed65070e68e93“

  hash2 = „127c525b0107045c39d4c956d51a16aba6b28e8a08cb1687e3fe7fc1f16e0de5“

 strings:

  $mz = { 4d 5a }

  // Below are code snippets used in CommieLoaders Sandbox/VM check.

  $a1 =

  {

    // Stub for retrieving the computer name.

   48 81 ec 30 01 00 00   // sub rsp, 0x130

   48 8d ac 24 80 00 00 00  // lea rbp, [rsp+0x80]

   c7 45 ac 00 01 00 00   // mov dword [rbp-0x54], 0x100

   48 8d 55 ac     // lea rdx, [rbp-0x54]

   48 8d 45 b0     // lea rax, [rbp-0x50]

   48 89 c1      // mov rcx, rax

   48 ?? ?? ?? ?? ?? ??   // mov rax, ?

   ff d0 85 c0     // call rax

  }

   $a2 =

   {

   // Stub for comparing the computer name against common sandbox hostnames.

    48 8d 45 b0     // lea rax, [rbp-0x50]

    48 ?? ?? ?? ?? ?? ??   // lea rdx, ?

    48 89 c1      // mov rcx, rax

    e8 ?? ?? ?? ??     // call ?

    85 c0       // test eax, eax

   }

   // SysWhispers3 function names that are observed in the samples.

   $b1 = „SW3_GetSyscallAddress“ ascii

   $b2 = „SW3_HashSyscall“ ascii

   $b3 = „SW3_PopulateSyscallList“ ascii

   // Strings related to the Autorun key, CommieLoader sets for purpose of persistence.

   $c1 = „Software\\Microsoft\\Windows\\CurrentVersion\\Run“ ascii

   $c2 = „Firefox_Updater_Version_2.3.1000“ ascii

 condition:

  $mz at 0 and

  filesize < 3MB and

  all of ($a*) and

  all of ($b*) and

  all of ($c*)

}

References:

hexacorn

github

Beitrag teilen auf:

XING
Twitter
LinkedIn

SECUINFRA Falcon Team • Autor

Digital Forensics & Incident Response Experten

Neben den Tätigkeiten, die im Rahmen von Kundenaufträgen zu verantworten sind, kümmert sich das Falcon Team um den Betrieb, die Weiterentwicklung und die Forschung zu diversen Projekten und Themen im DF/IR Bereich.

> alle Artikel