Demontage von KillDisk: Reverse Engineering der destruktiven Komponente von BlackEnergy

[post-views]
Februar 29, 2016 · 11 min zu lesen
Demontage von KillDisk: Reverse Engineering der destruktiven Komponente von BlackEnergy

Lassen Sie uns die lange Einführung in die BlackEnergy-Bedrohung überspringen und direkt zur Untersuchung der Malware-Komponente namens „ololo.exe“ gehen, auch bekannt als KillDisk. KillDisk ist ein Modul des BlackEnergy-Frameworks, das auf die Zerstörung von Daten abzielt und während der APT-Operationen Chaos und Ablenkung schafft.

https://www.virustotal.com/en/file/11b7b8a7965b52ebb213b023b6772dd2c76c66893fc96a18a9a33c8cf125af80/analysis/Die Hauptwerkzeuge, die wir heute in unserer Analyse verwenden, sind Process Monitor das Teil der SysInternals-Utilities von Mark Russinovich ist und IDA Pro Disassembler. Alle Manipulationen werden in einer virtuellen Umgebung basierend auf dem Betriebssystem Windows XP durchgeführt. Wir beginnen mit der schnellen Grundkonfiguration der Test-VM, schalten die Maschine ein und erstellen einen Snapshot namens „Vor der Infektion“. Lasst uns beginnen!

Um alle Ereignisse im Zusammenhang mit unserem Untersuchungsgegenstand zu verfolgen, starten wir Process Monitor und stellen sicher, dass es sichtbar bleibt:BlackEnergy-killdisk-1Und dass es den benötigten Prozess verfolgt:

BlackEnergy-killdisk-2Als nächstes laden wir das Virus in IDA Pro Disassembler und sehen folgendes Bild.Dies ist die WinMain-Funktion, d.h. die Hauptfunktion, also beginnen wir mit ihrer Analyse und sehen, dass das erste (und einzige) was sie tut, ein Prozeduraufruf namens sub_40E070:

 BlackEnergy-killdisk-3
ist. Lassen Sie uns direkt zu dieser Prozedur gehen. Hier finden wir Prozeduren, die für die Extraktion von Dateierweiterungen in den Speicher verantwortlich sind, und direkt danach wird eine interessante Sache namens sub_40E080:

BlackEnergy-killdisk-4aufgerufen. Werfen wir einen genaueren Blick auf deren Inhalt:BlackEnergy-killdisk-5Ihre Innenseiten enthalten einen interessanten Systemaufruf CreateFile und ein paar Prozeduren sub_40C390 and sub_40C400. Es ist also an der Zeit, einen Snapshot unseres virtuellen Labors zu machen und unsere Reise fortzusetzen!

Untersuchen wir den Funktionsaufruf CreateFile, setzen wir den BreakPoint darauf und führen unser Studienobjekt aus:

BlackEnergy-killdisk-6Ein kleiner Hinweis zu dieser Funktion:

CreateFile Die Funktion erstellt oder öffnet eine Datei oder ein I/O-Gerät. Die am häufigsten verwendeten I/O-Geräte sind: Datei, Dateistream, Verzeichnis, physische Festplatte, Volume, Konsolenpuffer (CONIN$ or CONOUT$), Bandlaufwerk, Kommunikationsressource, Mailslot und benannte Pipe. Die Funktion gibt einen Handle zurück, der verwendet werden kann, um auf die Datei oder das Gerät für verschiedene Arten von I/O zuzugreifen, abhängig von der Datei oder dem Gerät und den angegebenen Flags und Attributen.

Wie wir wissen, geben die meisten Compiler Argumente über einen Stack an eine Funktion weiter, und da wir es mit einem Beispiel zu tun haben, das ursprünglich in C geschrieben wurde, und gemäß den C-Direktiven werden Argumente von Stack von rechts nach links übergeben, sodass das erste Funktionsargument als letztes auf den Stack gepusht wird und schließlich als erstes Element oben endet. [1].

There is a special register for working with the stack – ESP (Extended Stack Pointer). The ESP, by definition, always points to the top of the stack:

BlackEnergy-killdisk-41Ein Blick in den Stack:BlackEnergy-killdisk-7Das letzte Argument, das in den Stack gepusht wurde (eines oben drauf), ist das erste Argument, das von der Funktion akzeptiert wird (gemäß der Syntax der CreateFile Funktion) und in unserem Fall den Dateinamen enthält \.PHYSICALDRIVE0, somit ist das Ziel die erste physische Festplatte.BlackEnergy-killdisk-8Nun analysieren wir andere Argumente, die an die Funktion gesendet werden, indem wir ihre Werte aus dem Stack entnehmen:BlackEnergy-killdisk-9Einige Schlussfolgerungen:

  • lpFileName – Zeiger auf eine ASCIIZ-Zeichenkette, die den Namen (Pfad) der geöffneten oder erstellten Datei enthält (wie wir bereits herausgefunden haben);
  • dwDesiredAccess – Zugriffsart auf die Datei: in unserem Fall ist der Wert 0C0000000h was bedeutet GENERIC_READ+GENERIC_WRITE or Lese-Schreib-Zugriff;
  • DwShareMode – ein Modus, der das Teilen der Datei mit verschiedenen Prozessen ermöglicht und dieser Parameter kann unterschiedliche Werte haben; in unserem Fall übersetzt der Wert pf 00000003b inFILE_SHARE_READ+FILE_SHARE_WRITE oderandere Prozesse können die Datei zum Lesen/Schreiben öffnen;
  • IpSecurityAttributes – Zeiger auf die SecurityAttributes-Struktur (Datei winbase.h), die Sicherheitseinstellungen im Zusammenhang mit dem Kernel-Dateiobjekt definiert; wenn die Sicherheit nicht festgelegt ist, wird ein NULL Wert verwendet;
  • dwCreationDistribution – entscheidet über Aktionen, wenn eine Datei existiert oder nicht existiert, in unserem Fall hat der Wert von 3 die Bedeutung OPEN_EXISTING or öffnen Sie die Datei, wenn sie existiert, und geben Sie einen Fehler zurück, wenn sie nicht existiert;
  • DwFlagsAndAttributes – Flags und Attribute; dieser Parameter wird verwendet, um die Merkmale der erstellten Datei festzulegen und wird im Lesezugriffsmodus 0 ignoriert.
  • hTemplateFile – Parameter wird nur verwendet, wenn eine neue Datei erstellt wird 0.
  • Bei Erfolg gibt die Funktion den Handler der neuen Datei an ЕАХ zurück, und wenn die Funktion fehlschlägt, wird ein NULL in das Register geschrieben. ЕАХ Alright, nachdem die Funktion

aufgerufen wurde, erhalten wir einen Wert ungleich Null für EAX, was bedeutet, dass er den Handler der angeforderten Datei enthält, z. B. CreateFile we get a non-zero value for EAX which means that it contains the handler of requested file e.g. \.PHYSICALDRIVE0:

BlackEnergy-killdisk-26Wir haben unseren Handler, fahren wir fort zum nächsten Aufruf, sub_40C390:

BlackEnergy-killdisk-10Wie zuvor schauen wir in die Prozedur und sehen einen weiteren interessanten Aufruf:BlackEnergy-killdisk-11Funktionshinweis:

DeviceIoControl Funktion sendet einen Steuerungscode direkt an einen bestimmten Gerätetreiber und veranlasst das entsprechende Gerät, die entsprechende Operation auszuführen.

Ein Blick in den Stack direkt vor dem Aufruf dieser Funktion:BlackEnergy-killdisk-11Schlussfolgerungen:

  • hDevice – Gerätedescriptor (wir erkennen hier einen bekannten Wert \.PHYSICALDRIVE0);
  • dwIoControlCode – Operationscode. In unserem Fall ist der Wert 0x70000h was bedeutet IOCTL_DISK_GET_DRIVE_GEOMETRY oder Abruf der Informationen zur Plattengeometrie (Anzahl der Zylinder, Medientyp, Spuren pro Zylinder, Sektoren pro Spur, Bytes pro Sektor);
  • lpInBuffer – Puffer mit Eingabedaten. Wir benötigen keine Eingabedaten, daher NULL ist es;
  • nInBufferSize – Größe des Eingabepuffers in Bytes gleich 0 in unserem Fall;
  • lpOutBuffer – Zeiger auf den Ausgabepuffer. Sein Typ wird durch den Parameter dwIoControlCode festgelegt 0x0011FCA8h;
  • nOutBufferSize – Größe des Ausgabepuffers in Bytes 0x18h (24);
  • lpBytesReturned – ein Zeiger auf eine Variable, die die Anzahl der Bytes enthält, die in den Ausgang geschrieben wurden 0x0011FCA4h;
  • lpOverlapped – Zeiger auf eine OVERLAPPED-Struktur;
  • Sobald der Aufruf verarbeitet ist, schauen wir in den Puffer (an der Adresse, die als Argument an lpOutBuffer gesendet wurde) 0x0011FCA8h):

BlackEnergy-killdisk-12um die Antwort zu interpretieren:

  • An der Adresse von 0x0011FCA4h haben wir eine Rückgabekodierung der Anzahl der in den Ausgangspuffer geschriebenen Bytes, wie von der Funktion beabsichtigt (grün markiert), genau so viele, wie wir angefordert haben, nämlich 24 Symbole.
  • An der Adresse von 0x0011FCA8h wir haben Informationen über die Geometrie der ersten physischen Festplatte erhalten (\.PHYSICALDRIVE0):
    • Anzahl der Zylinder – 0x519h (1305)
    • Medientyp – 0x0Ch (12), feststehende Festplatte.
    • Spuren pro Zylinder – 0x0FFh (255)
    • Sektoren pro Spur – 0x3Fh (63)
    • Bytes pro Sektor – 0x200 (512)

Jetzt beziehen wir uns auf die dritte Prozedur, sub_40C400:

BlackEnergy-killdisk-13Im Inneren der Prozedur zogen zwei Aufrufe sofort unsere Aufmerksamkeit auf sich, speziellSetFilePointerExundWriteFile:

BlackEnergy-killdisk-14Ein Hinweis zur Funktion:

SetFilePointerEx Die Funktion bewegt den Dateizeiger der angegebenen geöffneten Datei.

Wie üblich werfen wir einen Blick in den Funktionsaufruf-Stack:BlackEnergy-killdisk-15

  • hfile – unser bekannter Gerätedeskriptor \.PHYSICALDRIVE0;
  • liDustanceToMove – anfängliche Position zum Beginn eines Schreibens 0;
  • lpNewFilePointer – ein Zeiger auf eine Variable, die einen neuen Positionszeiger aus der Datei abruft 0x0011FCA8h. Falls der Parameter LEER (NULL) ist, wird kein neuer Zeiger in der Datei zurückgegeben;
  • dwMoveMethod – Größe des Eingabepuffers in Bytes. In unserem Fall 0x200h (512) – Bytes im Sektor;

Jetzt verwendet unser „Viech“ die WriteFile Funktion und um die Datei mit Nullen zu füllen, auf die es bereits direkten und zuverlässigen Zugriff hat.

Ein Funktionstipp:

Eine WriteFile Funktion schreibt Daten in die angegebene Datei oder das Eingabe-/Ausgabegerät (I/O-Gerät), beginnend mit der in der Datei angegebenen Position. Diese Funktion ist für sowohl synchrone als auch asynchrone Operationen ausgelegt.

Liste der Funktionsargumente:BlackEnergy-killdisk-16In Ordnung, damit wir nichts Interessantes verpassen, werfen wir einen Blick ins Dashboard und achten dabei auf den Stack:BlackEnergy-killdisk-17Alles bereit für den Einsatz:

  • 1 – Bleibend beim Funktionsaufruf WriteFile
  • 2 – Process Monitor wartet gespannt auf den kleinsten Zucken der „Muskeln“ unseres „Viech“
  • 3 – Argumente von Registern in den Stack gedrückt
  • 4 – und nun, der Stack selbst
  • Ein spezielles Quadrat im Fenster Hex View-EBP markierten wir ein Gebiet, das von dem zweiten Funktionsargument (Datenpuffer) adressiert wird und Sie können mir glauben – es enthält genau 0x200h (512) Nullen.

Wir führen den Befehl durch Drücken der F8-Taste aus und beobachten die Änderung:BlackEnergy-killdisk-18Wie erwartet ist das scharfe Auge von Process Monitor spot on und fixiert das Schreiben von 512 Bytes beginnend von Position 0.

Dann verlässt die Prozedur und wird erneut aufgerufen, jedoch nun mit unterschiedlichen Werten:

Nun nehmen wir die nächsten 512 Bytes, was insbesondere bei 0x200 sichtbar ist (im Gegensatz zu vorherigen Argumenten in dem wir den Stack durch einen blauen Rahmen markierten)BlackEnergy-killdisk-19Und das gleiche Schicksal ereilt sie:BlackEnergy-killdisk-20Diese Karussellfahrt geht weiter, bis die Überprüfung von 0x40C865h Adresse den Wert von EBX Registrierung gleich 0x100h (255 Mal ):

BlackEnergy-killdisk-21Um sicherzugehen, dass dies geschehen ist, setzen wir einen Stoppunkt an der Anweisung, die unserem destruktiven Zyklus folgt:

BlackEnergy-killdisk-22Insgesamt erhalten wir 256 von Überschreiboperationen:BlackEnergy-killdisk-23Das ist im Wesentlichen alles, unser „Viech“ hat seine dreckige Arbeit erledigt und schließt den Dateihandler, indem es die CloseHandle Funktion aufruft.

Funktionshinweis:

Eine CloseHandle Funktion schließt einen Objekt-Handler eines geöffneten Objekts.

Funktionsargumente:

BlackEnergy-killdisk-24Unser traditioneller Stack Vorbereitungsblick:

BlackEnergy-killdisk-25Und was haben wir im ESI-Register?BlackEnergy-killdisk-26Wie erwartet wird unser bekannter Deskriptor 0x44h an den Stack als Funktionsargument übergeben.

Wir sehen die von der Funktion zurückgegebenen Werte im EAX-Register: BlackEnergy-killdisk-27Wenn die Funktion erfolgreich endet, ist der zurückgegebene Wert nicht-null.

Wenn die Funktion mit einem Fehler endet, ist der zurückgegebene Wert null.

Diese Aktion entging nicht dem wachsamen Auge unseres Process Monitor:

BlackEnergy-killdisk-28Alles verlief normal (wenn man die Zerstörung des wichtigsten Teils des Systemlaufwerks normal nennen kann) und die Datei wird geschlossen.

Lass uns weitermachen:BlackEnergy-killdisk-29Für eine bequemere Wahrnehmung benannte ich die Funktion um in Eraser. Nachdem es erfolgreich die Daten auf PhysicalDrive0zerstört hat, wird der Zähler im ESI Register um 1erhöht, mit dem Wert von 0x0Ah (10) verglichen und startet den Überschreibungsvorgang mit einem neuen Wert:BlackEnergy-killdisk-30und so weiter, bis die 10 erreicht ist.

Auf diese Weise wandert das Viech weiter über alle physischen Laufwerke und löscht die ersten 512 * 255 = 128kb (dies hängt von der Anzahl der Bytes in einem Sektor ab, die „Viech“ aufgrund der Kenntnis der Plattengeometrie gelernt hat).

Dieses Mal wird jedoch das Ergebnis der Funktion CreateFileW ein folgender Wert sein: BlackEnergy-killdisk-31Was bedeutet, dass in unserem „improvisierten Labor“ kein zweites physisches Laufwerk (oder nicht irgendeine Art von Laufwerk mit einer Sequenznummer größer als 0) existiert und der Zyklus nichts zu überschreiben hat.part1_op_enJetzt, da wir mit frisch erworbenem Wissen bewaffnet sind, lassen Sie uns versuchen, die destruktive Funktion, die wir gerade gelernt haben, zu deaktivieren, indem wir den Code so ändern, dass unsere Änderung es erlaubt, das Überschreiben der Daten zu umgehen:

BlackEnergy-killdisk-33Erinnern wir uns an die Prozedur sub_40E080 die den Funktionsaufruf CreateFileWenthält. Da wir wissen, dass der Wert von 0xFFFFFFFFh nach Abschluss der Funktion CreateFileW nur dann erscheinen würde, wenn kein physisches Laufwerk vorhanden ist und in einem solchen Szenario die Funktion „Datenzerstörung“ einen Ausgang ausführt, ändern wir eine bedingte Verzweigungsanweisung an der 0x0040C7E4h Adresse in eine unbedingte:

 BlackEnergy-killdisk-34
IDA Pro ist ein cooles Instrument, sodass wir einfach einen Befehl schreiben müssen.

Das Einzige, worauf wir achten müssen, ist die Befehlsgröße vorher und nachher, also lassen Sie uns überprüfen, wie viele Bytes von der bedingten Sequenz verwendet werden:

BlackEnergy-killdisk-35Wie wir sehen können, entspricht eine bedingte Verzweigungsanweisung 6 Bytes, während die bedingungslose Sequenz nur 5 nimmt, und deshalb sollten wir einen weiteren Befehl hinzufügen – NOP, um die Dinge gleich zu halten:BlackEnergy-killdisk-36Wir NOP den nächsten Befehl, unabhängig davon, was er vorher enthielt (auch wenn wir dort eine 0 sehen):

BlackEnergy-killdisk-37Wir überprüfen doppelt, dass die Befehlsadressen jetzt basierend auf der Adresse des folgenden Befehls gleichgestellt sind:

BlackEnergy-killdisk-38und drücken Abbrechen. Fertig.

Nach diesen Manipulationen sieht die Prozedur einfach aus: eintreten und austreten, eine leere Schleife erstellen (da wir nicht wissen, welche anderen Teile des Programms darin eingreifen könnten, belassen wir sie in diesem Zustand).BlackEnergy-killdisk-39Der letzte Beweis, der durch den Process Monitor festgestellt wurde, zeigt, dass keine physischen Laufwerke bei der Ausführung des zerlegten KillDisk geschädigt wurden:

BlackEnergy-killdisk-40Malware hat die Information über die Festplatte gelesen, sie untersucht und mit ihrem Geschäft fortgefahren, das wir in unseren nächsten Artikeln gründlich analysieren werden.

Quellen:

[1] – Hacker Disassembling Uncovered. Kris Kaspersky, 2009.

Übersetzt aus dem Original von Andrii Bezverkhyi | CEO SOC Prime

War dieser Artikel hilfreich?

Gefällt es Ihnen, teilen Sie es mit Ihren Kollegen.
Treten Sie der Detection as Code-Plattform von SOC Prime bei um die Sichtbarkeit in Bedrohungen zu verbessern, die für Ihr Unternehmen am relevantesten sind. Um Ihnen den Einstieg zu erleichtern und sofortigen Nutzen zu bieten, buchen Sie jetzt ein Treffen mit SOC Prime-Experten.