Malware Entwicklung - Professionalisierung einer alten Kunst

Malware Entwicklung

Professionalisierung einer alten Kunst

Marc Ruef
von Marc Ruef
Lesezeit: 56 Minuten

Keypoints

  • Kundenspezifische Malware kann im Rahmen von professionellen Sicherheitsüberprüfungen eingesetzt werden
  • In der Regel werden Würmer bzw. Backdoors genutzt, um vernetzte Systeme zu kompromittieren
  • Durch verschiedene Stealth-Techniken kann eine Erkennung und Analyse erschwert werden
  • Meist erfolgt eine Datensammlung, die ihrerseits an den Backend-Server übertragen wird
  • Diese Exfiltration kann über verschiedene Kanäle, bevorzugt mittels kryptografisch geschützten Mechanismen, erfolgen

Mein Einstieg in den Bereich Cybersecurity fand ich in der ersten Hälfte der 90er Jahre. Damals bin ich per Zufall in der lokalen Bücherei über ein Buch zum Thema Computerviren gestolpert. Die darin enthaltenen Konzepte sollten mir schlussendlich den Zugang zur Programmierung, gerade in Bezug auf unkonventionelle Konzepte, ermöglichen. Dieses obskure Hobby konnte ich Jahre später zum Beruf machen. Wie professionelle Malware-Entwicklung heutzutage aussieht, soll dieser Artikel diskutieren. Dadurch kann das Verständnis, vor allem in Bezug auf moderne Malware-Infektionen, gefestigt werden.

Professionelle Malware

Der traditionelle Grund für Entwicklung von Malware war das Ergründen von unorthodoxen Konzepten. Durch besonders kleine, effiziente und schwierig zu entdeckende Viren, konnte man sich in der Virii-Szene Anerkennung schaffen.

Zum Beispiel entwickelte der deutsche Programmierer ~knzyvo Anfang der 90er Jahre einen DOS-Virus namens Whale. Dieser wurde unter anderem für seine polymorphen Mechanismen bekannt. Obschon der Programmcode bei jeder Infektion das Gleiche tat, sollte dieser jedes Mal anders aussehen. Ein Albtraum für die noch junge Antivirus-Industrie, die mit simplen musterbasierten Ansätzen agierte.

Oder ein russischer Programmierer namens Z0mbie entwickelte einen Virus namens Tiny. Die virale Komponente umfasste lediglich 185 Bytes. Eine grossartige Leistung, die über Jahre hinweg innerhalb und ausserhalb der Virenfamilie optimiert wurde (siehe auch Mini-Virus).

Heutzutage entwickelt praktisch niemand mehr Malware aus Neugierde oder dem Bedürfnis heraus, etwas Besonderes schaffen zu können. Malware wird heute geschrieben und verteilt, um in Systeme eindringen und damit Geld machen zu können. Es ist dann auch zu beobachten, dass dem Grossteil der jüngsten Malware die gleiche Krankheit anhaftet, wie moderner kommerzieller Software: Sie ist gross, aufgeblasen, schwerfällig und ineffizient – und trotzdem beobachten wir regelmässig epidemische Auswirkungen.

Dennoch haben Malware-Entwickler als Hobbyisten oder als Cyberkriminelle einen entscheidenden Vorteil: Die Qualität ihres Produkts vermag zwar den Gewinn zu beeinflussen – Falls eine Malware aber mal nicht richtig funktioniert, ist das halt lediglich Pech gewesen.

Wir entwickeln kundenspezifische Malware für professionelle Sicherheitsüberprüfungen. Dort sind die Anforderungen an Qualität und Zuverlässigkeit anders strukturiert. Eine Malware darf lediglich in dem ihr vorgeschriebenen Rahmen agieren. Fehler werden nicht toleriert und müssen in jedem Fall zu Gunsten des Kunden abgefangen werden. Und auch wenn dies bedeutet, dass eine Ausführung abgebrochen wird. Dies wiederum ist ein Albtraumszenario für unser Unternehmen, denn eine abgebrochene Malware-Verteilung entspricht dem Nicht-Erfüllen des Auftrags. Dies ist natürlich nicht in unserem Sinn. Wir sind froh, bisher von einem solchen ultimativen Rückschlag verschont geblieben zu sein.

Wahl der Klasse

Es gibt eine verschiedene Anzahl an Malwareklassen. Da ist einerseits der klassische Computervirus, der typischerweise als File-Infector funktioniert. Nach seiner Ausführung sucht er sich bestimmte Dateien, die er infizieren kann. Diese Dateien werden dann zum neuerlichen Virenträger und können nach ihrer Ausführung wiederum andere Dateien infizieren.

Dateiviren sind heutzutage, gerade im professionellen Umfeld, eher unpopulär. Einerseits handelt es sich in den allermeisten Fällen (partiell abgesehen von Companion-Viren) um einen direkten invasiven Eingriff. Schliesslich müssen Dateien verändert werden (Virus-Teil voranstellen, anhängen oder überschreibend).

Stattdessen wird bevorzugt ein Computerwurm eingesetzt. Dieser infiziert nicht im eigentlichen Sinne Dateien, sondern ganze vernetzte Computersysteme. Ende der 90er Jahre, als das Internet rasant an Popularität gewinnen sollte, wurden in erster Linie Infektionen durch Email-Anhänge vorangetrieben. Der Wurm hat sich selber per Email verschickt und setzte voraus, dass ein Opfer zuerst den Anhang herunterladen und öffnen sollte. Erste grosse Popularität erreichte der Happy99 Wurm.

Professionelle Würmer versuchen diese Benutzerinteraktivität zu umgehen, indem sie in automatisierter Form Schwachstellen in einem System mittels Exploiting ausnutzen. Dies kann die Interpretation eines Emails oder Zugriff auf einen angebotenen Dienst sein. Der Wurm Blaster hat zum Beispiel eine Remote-Schwachstelle in DCOM RPC von Microsoft Windows ausgenutzt. Mit WannaCry und NotPetya zeigte sich erst jüngst, dass mittels Exploiting propagierende Malware sehr gefährlich sein kann.

Infektionsvektoren

Jede Malware-Infektion setzt voraus, dass auf dem Zielsystem entsprechender Malicious Code ausgeführt werden kann. Dieser Code muss auf das Zielsystem gebracht werden, wobei verschiedene Infektionsvektoren zur Verfügung stehen. Früher wurde dies durch eine virenverseuchte Diskette vorangetrieben. Heute stehen eher USB-Sticks und Internet-Zugriffe im Mittelpunkt. Die Wahl dessen richtet sich nach dem Szenario, das man reproduzieren möchte.

Erstinfektion Malware Plattform
1971 Creeper System DEC PDP-10, TENEX
1975 ANIMAL UNIVAC 1108
1981 Elk Cloner Apple II
1983 Virus VAX11/750
1984 UNIX
1986 Virdem MS DOS
1987 Vienna IBM
1987 SCA Amiga
1987 Christmas Tree EXEC VM/CMS
1988 Morris Worm DEC VAX / Sun, BSD UNIX
1995 Concept Microsoft Word
1996 Boza Microsoft Windows 95
1996 Laroux Microsoft Excel
1999 Kak Worm Microsoft Outlook Express

Das nachfolgende Video zeigt eine durch uns entwickelte Backdoor, die auf Javascript/Ajax basiert und komplett im Browser ausgeführt wird. Das Opfer muss lediglich dazu gebracht werden, eine Webresssource aufzurufen, die unter unserer Kontrolle ist.

Falls die menschliche Komponente bestmöglich ausgeschaltet werden soll, um zusätzliche Effizienz erreichen und die Entdeckung verhindert werden soll, wird auf exploitgestützte Infektionen gesetzt. Diese erfordern das Auffinden und Ausnutzen von Schwachstellen in einem Zielsystem. Die Komplexität des Angriffs steigt damit unweigerlich an. Oftmals wird die Infektion losgelöst von der eigentlichen Malware angegangen.

Besondere Autonomie der Malware gewährt die Integration des Exploits direkt in ihr selbst. Die Komplexität derer nimmt stark zu und damit auch die Möglichkeit einer Erkennung. Auch wenn die Malware selber vielleicht noch nicht durch Antiviren-Lösungen erkannt wird, könnte die Exploit-Komponente (der Exploit selber oder beispielsweise der Shellcode) als bösartiger Code erkannt werden.

Zu manchen Schwachstellen sind entsprechende Exploits bekannt. Diese stehen aber oftmals nicht in der Programmiersprache zur Verfügung, in der die Malware geschrieben wird. Entweder muss hier also eine aufwendige Portierung stattfinden, die so manchen Malware-Entwickler vor grosse Hürden stellen kann. Oder man entscheidet sich dafür, die Exploit-Komponente separat zu integrieren und auszuführen.

Zu diesem Zweck kann der externe Code – dies ist nicht nur auf Exploits beschränkt – in gepackter Form im Binary der Malware hinterlegt werden. In .NET bietet sich hierfür die ZipArchive Class an. Der Einfachheit halber kann der Bitstream eines solchen Archivs mit Base64 encodiert und im Datenteil des Binaries abgelegt werden. Bei der Ausführung derer wird dann die externe Komponente entpackt und ausgeführt. Durch diese Modularität wird ein hohes Mass an Unabhängigkeit und Flexibilität ermöglicht.

Mehrfachinfektionen verhindern

Ein grundlegendes Merkmal eines klassischen Dateivirus ist, dass er eine schon vorhandene Infektion erkennen kann. Dadurch kann auf eine Mehrfachinfektion verzichtet werden. Diese würde wertvolle Systemressourcen verschwenden, die Trägerdateien nur unnötig aufblähen und eventuell gar die eigentliche Virenfunktion negativ beeinträchtigen. Um dem entgegenzuwirken wird in der Regel vor der Infektion die Zieldatei überprüft. Bei simplen Viren kann eine Mustererkennung eingesetzt werden, um zum Beispiel typische Strings ausmachen zu können. Den gleichen Ansatz können und werden aber auch die Antiviren-Hersteller zur Entdeckung verwenden.

Bei modernen Würmern wird diese Funktionalität ebenso vorausgesetzt. Ein Netzwerk mit 200 Rechnern, die sich ständig und fortwährend selber erneut und erneut infizieren, ist zu vermeiden.

Oftmals benötigt Malware eine lokal ausgeführte Datei. Falls deren Lokation bekannt ist, kann die Existenz geprüft und von einer weiteren Infektion abgesehen werden.

Falls eine speicherresidente Lösung etabliert wird, muss diese eventuell trotzdem (temporäre) Dateien erstellen (z.B. zum Anlegen von Screenshots). Diese könnten alternativ gesucht werden. Oder es wird der Speicher auf eine bestehende Instanz der Malware geprüft. Bei VB6 kann hierzu in einfacher Weise App.PrevInstance geprüft werden. Bei .NET benutzen manche Entwickler einen Mutex mit .WaitOne, um den gleichen Effekt zu erzielen. Oftmals wird alternativ der Prozessname mit Process.GetProcessesByName und Process.GetCurrentProcess.ProcessName gesucht und dann die Programmausführung abgebrochen. Die Microsoft IDE stellt aber eigentlich unter Project / Properties / Application die Option Make Single Instance App zur Verfügung.

Einrichten der Umgebung

Manchmal fungiert eine Malware als One-Shot. In diesem Fall wird sofort nach der Infektion die eigentliche Aktivität vorangetrieben, ohne eine Persistenz, die einen Neustart des Systems überleben würde, einzurichten. In den meisten Fällen geschieht unmittelbar eine Datensammlung und Exfiltration. Dies dauert je nachdem nur wenige Sekunden oder gar Millisekunden. Falls eine Entdeckung stattfindet, ist es in der Regel sowieso schon zu spät, diese einzuschränken. Deshalb kann bei solchen One-Shots auf umfangreiche Stealth-Techniken verzichtet werden.

Ganz im Gegensatz zu Malware, die über einen längeren Zeitraum auf dem Zielsystem verbleiben muss. Bevorzugt werden heutzutage persistente Lösungen eingesetzt. Rein speicherresidente Lösungen (TSR) kommen also nicht mehr infrage, denn der Aufwand einer erfolgreichen Infektion ist bedeutend grösser als das nachhaltige Einnisten im System. Stattdessen müssen sich diese als Binaries irgendwo etablieren und bei einem Neustart wiederum mitgestartet werden.

Anlegen der Binaries

Zum Zweck der Persistenz wird an einem vordefinierten Ort das Binary abgelegt. Voraussetzung hierfür ist, dass die Malware Schreib- und Ausführberechtigungen an dieser Lokation hat. Der persönliche Ordner des Benutzers bietet sich hierfür an. Windows kennt dafür die Umgebungsvariable %HOMEPATH%. Diese kann bei Direktzugriffen auf der Ebene des Dateisystems beigezogen werden. Die Nutzung innerhalb von Programmcode gestaltet sich jedoch aufwendig, ineffizient und fehleranfällig.

Stattdessen kann auf API-Schnittstellen des Kernels zurückgegriffen werden, um die Informationen zuverlässig erhalten zu können. So wird in userenv.dll über GetUserProfileDirectory das Auslesen des Heimverzeichnisses des aktuellen Benutzers möglich. Und alternativ über GetDefaultUserProfileDirectory die Identifikation der standardmässig vergebenen Ordner für Benutzerprofile. Wie so oft bei der Nutzung der Kernel-API ist es erforderlich, dass bei den Rückgabewerten sämtliche angehängten Nullbytes gelöscht werden.

Der Vorteil der Nutzung des Benutzerverzeichnisses liegt darin, dass die Lokation immer gleich bleibt, die entsprechenden Zugriffsrechte vorhanden sind und eine Dritteinwirkung sehr unwahrscheinlich ist. Es gibt immer wieder Malware-Entwickler, die stattdessen über GetTempPath den temporären Ordner (entweder Windows-global oder benutzerspezifisch) auswählen. Dort mögen zwar die erforderlichen Rechte vorhanden sein. In der Regel können aber auch andere Benutzer auf den Ordner zugreifen, was das Risiko einer Entdeckung erhöht. Es kann aber auch sein, dass eine regelmässige Säuberung alter temporärer Daten stattfindet, wodurch die für die Malware erforderlichen Komponenten mitentfernt werden.

Autostart bei Neustart

Eine Malware gilt nur dann als persistent, wenn sie auch nach einem Neustart wieder funktionieren kann. Dies bedeutet, dass auch sie auf eine Autostart-Funktionalität zurückgreifen muss.

Windows bietet verschiedene Möglichkeiten, wie beim Hochfahren des Betriebssystems eine Anwendung gestartet werden kann. Die einfachste Form ist das Erstellen eines Shortcuts im Ordner C:\Users\%USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup. Die Simplizität führt aber den Nachteil mit sich, dass ein solcher Autostart einfach gefunden und verhindert werden kann.

Stattdessen werden Autostarts auch heute noch über die Registry bevorzugt. Für jeden Benutzer werden die automatisch zu startenden Applikationen unter HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run separat festgehalten. Eine einmalige Ausführung kann unter HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce definiert werden.

Stealth Techniken

Eine Malware versucht in der Regel möglichst lange unentdeckt zu bleiben. Hierfür gibt es verschiedene Bereiche, die kontrolliert werden können müssen.

Defensives Timing

Generell kann gesagt werden, dass Timing eine wichtige Rolle spielt. Ein sehr aggressives Timing kann zu einer hohen Auslastung der Ressourcen und damit zu einer schnellen Entdeckung führen. Wenn zum Beispiel eine Malware nach der Infektion die lokalen Datenträger nach wichtigen Dateien untersucht, können dadurch viele Lesezugriffe stattfinden. Die Gesamtgeschwindigkeit des Systems wird darunter spürbar leiden. Jenachdem erhöht sich die Drehzahl der Platten, diese werden warm und müssen gekühlt werden. Eine Erhöhung der Lüftergeschwindigkeit kann also verräterisch sein.

Aus diesem Grund sollten Zugriffe jeglicher Art gedrosselt stattfinden. Bei .NET lässt sich hierzu ein Thread.Sleep einsetzen, um 2000 Millisekunden zu warten.

Beispiel eines Timer-Controls mit VB6

Vielen Malware-Entwicklern passiert jedoch das Ungeschick, dass sie eine hochgradig ineffiziente Methode für eine solche Drosselung auswählen. Zum Beispiel greifen sie auf kernel32.dll zurück, um mit dem API-Call Sleep den Effekt zu erzielen. Das Problem hierbei ist, dass dies zu einer Prozessorauslastung von 100% führt. Eine Verlangsamung des Systems ist zu beobachten und früher oder später die Emissionen der aufwändigen Aktivkühlung des Prozessors. Aus diesem Grund wurde traditionell bei VB6 mit Timer-Controls gearbeitet.

Im Dateisystem verstecken

Resistente Malware, die einen Neustart des Systems überleben muss, muss sich in der Regel auf dem Dateisystem einnisten. Hierzu müssen Dateien geschrieben werden, die wiederum nicht sofort gefunden werden sollten. Neben der Wahl einer geschickten Lokation ist es zusätzlich möglich, die Datei auf dem Dateisystem (früher FAT, heute NTFS) mit dem Hidden-Flag zu versehen. Hierzu kann das DOS-Kommando attrib -h C:\Temp\example.exe, unter VB6/VBA die Anweisung SetAttr "C:\Temp\example.exe", vbHidden (Quelle) und unter .NET File.SetAttributes("C:\Temp\example.exe", FileAttributes.Hidden) (Quelle) verwendet werden. Diese Dateien sind dann in den Standardeinstellungen nicht mehr ohne weiteres sichtbar. Ein Benutzer muss zuerst das Anzeigen versteckter Dateien im Dateiexplorer aktivieren, um diese wieder anzeigen zu lassen.

Dieser Schritt mag einfach klingen, kommt in der Praxis aber mit einigen Schwierigkeiten daher. Seit der Einführung von Polymorphie bei Malware versuchen Antiviren-Lösungen zusätzlich mit heuristischen Methoden zu funktionieren. Anstelle des klassischen musterbasierten Ansatzes wird bei einer Programmausführung darauf geachtet, welche Aktionen stattfinden. Eine Kombination von verdächtigen Aktionen deutet auf eine Malware hin. Und so ist es dann auch, dass ein Grossteil der Antiviren-Lösungen (1) das Erstellen einer Datei und (2) das Setzen des Hidden-Flags als Malware-Aktivität deklarieren.

Virustotal erkennt Hidden-Aktion als Malicious Code

Einer solchen Entdeckung und der damit einhergehenden Einschränkung kann man entgehen, indem zum Beispiel das Timing ausgedehnt wird. Wird eine Datei zum Beispiel erstellt und erst zwei Minuten später das Hidden-Flag gesetzt, können Antiviren-Lösungen keinen Zusammenhang mehr zwischen den beiden Aktionen erkennen. Innerhalb dieser ersten zwei Minuten ist die Datei aber sichtbar und eine Infektion könnte aus diesem Grund auffallen. Die erforderliche Zeitspanne ist in erster Linie vom eingesetzten Antivirus-Produkt abhängig.

Stattdessen ist deshalb angeraten, dass man einfach die Reihenfolge der Aktivitäten ändert. In diesem Fall bedeutet dies, dass man zwischen dem Erstellen der Datei und dem Setzen des Hidden-Attributs eine zusätzliche Aktion durchführt. Zum Beispiel kann dies ein anderer Dateizugriff oder ein Netzwerkzugriff sein.

Im Speicher verstecken

Egal ob eine Malware nun in persistenter Weise einen Neustart überleben muss oder einfach während der ersten Ausführung im Speicher verbleibt, ihre Existenz im Speicher sollte nach Möglichkeiten versteckt werden.

Als erstes taucht ein Prozess immer im Taskmanager auf. Üblicherweise geschieht dies mit dem Namen des Haupt-Frames, das zur Programmausführung definiert wurde. Da Malware in der Regel im Hintergrund ausgeführt wird, kann der Titel des Frames nach Belieben gewählt werden. Hier kann also genauso gut Internet Explorer angegeben werden in der Hoffnung, dass der Benutzer nicht realisiert, dass es sich hierbei nicht um einen echten Prozess des Standardbrowsers handelt.

Der Taskmanager zeigt zudem das Icon des Haupt-Frames an. Auch hier kann man sich entsprechend darum bemühen, das offizielle Icon der vorgetäuschten Anwendung einzubringen. Dies ist manchmal nicht so einfach, da die verschiedenen Windows-Versionen unterschiedliche Icons aufweisen und die jeweiligen Task Manager-Versionen diese wiederum unterschiedlich in Bezug auf die Auflösung darstellen. Beim Aufstarten der Malware wird es also wichtig herauszufinden, auf was für einer Plattform (Windows-Version, Patchlevel, Architektur) man ausgeführt wird. Bei .NET kann man auf Environment.OSVersion und dann über .Platform, .Version, .Minor sowie .Revision darauf zugreifen.

Eleganter ist es natürlich, wenn man gänzlich verhindern kann, dass die Malware im Task Manager angezeigt wird. Die einfachste Möglichkeit dies zu bewerkstelligen (ohne tiefschürfende Eingriffe in die Verarbeitung des Kernels machen zu müssen), ist das Definieren der Frame-Struktur als FixedToolWindow. Dadurch wird die Applikation nicht mehr eigenständig, sondern als Element eines Frameworks wahrgenommen. Diese Elemente sollten nicht separat angesteuert werden können, weshalb sie auch nicht mehr im Task Manager als Applikation angezeigt werden.

Die Prozesse werden manchmal aber auch mit ihrem Dateinamen angezeigt. Also lohnt es sich, den Dateinamen der Malware von vornherein auf das Spoofing im Task Manager einzustellen. Ein Dateiname browser.exe ist bedeutend unauffälliger als datastealer.exe.

Hierbei gilt es natürlich auch nicht zu vergessen, die Anzeige der Malware in der Taskbar zu verhindern. Dies geschieht mit der Eigenschaft Form.ShowInTaskbar.

Anti-Debugging

Eine Antivirus-Lösung oder spätestens ein Forensiker wird mit verschiedenen Mitteln darum bemüht sein, die Funktionsweise der Malware verstehen und dementsprechend einschränken zu können. Aus diesem Grund ist man im professionellen Umfeld darum bemüht, ein solches Debugging stark zu erschweren.

Ein Debugging von Malware wird in der Regel auf einem gesonderten System, in einer Sandbox oder mindestens mit typischen Tools durchgeführt. Um ein Blackbox-Debugging zur Laufzeit zu erschweren, kann versucht werden zu erkennen, ob ein solches stattfindet. In diesem Fall wird die Funktionsweise der Malware abgeändert. Es findet zum Beispiel keine Infektion oder Exfiltration statt. Oder es wird gänzlich auf eine Programmausführung verzichtet.

Debugging erkennen

Der klassische Trick zur Identifikation eines Debuggers besteht im Aufrufen der Funktion IsDebuggerPresent in kernel32.dll. Diese gibt True zurück, falls ein Debugger angehängt wurde:

if(IsDebuggerPresent()){
   ExitProcess(0);
}else{
   // ...
}

In ähnlicher Weise funktioniert INT 2Dh, das ein EXCEPTION_BREAKPOINT (0×80000003) provoziert:

xor  eax, eax        ;set Z flag
int  2dh
inc  eax             ;debugger might skip
je   being_debugged

Man kann auch einen eigenen Child-Prozess ausführen, das Debugging des Parent-Prozesses initiieren und bei einer Kollision in ZwDebugActiveProcess ein schon laufendes Debugging erkennen.

Es gibt auch produktespezifische Erkennungsmöglichkeiten. In der Fachliteratur wird gerne das simple Beispiel der Instruktion 0xF1 herangezogen. Diese setzt beim Produkt SoftICE eine SINGLE_STEP Exception an. Sollte ein Debugging mit SoftICE stattfinden, führt dies zu einer Access Violation.

Ähnlich wie im Bereich der Jailbreak-Detection auf Smartphones kann alternativ geschaut werden, ob typische Dateinamen und Dateipfade existieren, die auf eine gesonderte Analyse-Umgebung hindeuten.

Oder man prüft den Speicher auf aktuell ausgeführte Prozesse, die ein Debugging verraten. Prozessnamen wie wireshark.exe und idag.exe deuten auf eine Analyse hin. Dadurch kann quasi eine softwaretechnische heisenberg’sche Unschärferelation erzwungen werden.

In manchen Umgebungen, gerade solche mit virtuellem Overhead, kann auch das Timing von bestimmten Routinen und Funktionsaufrufen beobachtet und bei Abweichungen andere Ausführungen angestrebt werden. Dies ist aber ein eher esoterischer Ansatz, der sich nur unter bestimmten Bedingungen anbietet.

Virtuelle Umgebungen erkennen

Es gibt verschiedene Instruktionsfolgen, die innerhalb einer Vmware anders abgearbeitet werden, als auf nativer Hardware. Dadurch lassen sich unter Umständen virtuelle Installationen zur Umsetzung von Analysen identifizieren:

int swallow_redpill(){
   unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3";
   *((unsigned*)&rpill[3]) = (unsigned)m;
   ((void(*)())&rpill)();
   return (m[5]>0xd0) ? 1 : 0;
}

Vmware selber dokumentiert gar das CPUID Hypervisor Present Bit, mit dem eine Ausführung erkennt werden kann:

int cpuid_check(){
   unsigned int eax, ebx, ecx, edx;
   char hyper_vendor_id[13];

   cpuid(0x1, &eax, &ebx, &ecx, &edx);
   if(bit 31 of ecx is set) {
      cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
      memcpy(hyper_vendor_id + 0, &ebx, 4);
      memcpy(hyper_vendor_id + 4, &ecx, 4);
      memcpy(hyper_vendor_id + 8, &edx, 4);
      hyper_vendor_id[12] = '\0';
      if (!strcmp(hyper_vendor_id, "VMwareVMware"))
         return 1; // Success - running under VMware
   }
   return 0;
}

Hinweise können auch Informationen im BIOS und der Hypervisor Port geben.

Ein simpler Ansatz ist ebenso, die MAC-Adressen der lokalen Schnittstellen zu prüfen, um den Vendor zu identifizieren. Dadurch lassen sich zum Beispiel virtuelle Maschinen als solche erkennen. Die nachfolgende Tabelle zeigt bekannte Hersteller auf.

Produkt↓ MAC Unique Identifier
IBM z/VM 02-00-41
Microsoft Hyper-V, Virtual Server, Virtual PC 00-03-FF
Novell Xen 00-16-3E
Oracle Virtual Iron 00-0F-4B, 00-21-F6
Oracle VM 00-16-3E
Parallells Desktop, Workstation, Server, Virtuozzo 00-1C-42
Red Hat Xen 00-16-3E
Sun xVM VirtualBox 08-00-27
Vmware ESX, Server, Workstation, Player 00-50-56, 00-0C-29, 00-05-69, 00-1C-14
XenSource 00-16-3E

Debugging erschweren

Das effektive Debugging kann dann erschwert werden, indem grundsätzlich simple Routinen kompliziert gestaltet werden. Ein Aufruf einer URL wird dann nicht mit einem unmittelbaren urlopen("https://www.scip.ch") durchgeführt. Stattdessen findet zuerst ein langwieriges Zusammensetzen des Strings statt: urlopen("https:" + "//www." + "scip.ch"). Auch wenn dies im Code selbst nachvollziehbar erscheint, wird der kompilierte Code damit um ein Vielfaches komplizierter.

Zusätzlich können Strings ausschliesslich codiert oder gar verschlüsselt im Binary abgelegt werden. So wird dann zuerst ein urlopen(hex2string("68747470733a2f2f7777772e736369702e6368")) eingesetzt, was die Komplexität zusätzlich erhöht. Und die Chancen werden minimiert, dass ein Disassembler oder Hex-Editor durch einfache String-Suchen die relevanten Teile ausfindig machen kann.

Selbstsprechende Funktionsnamen erleichtern die Analyse von Code. Stattdessen auf möglichst kryptische bzw. kurze Funktionsnamen zurückgreifen macht dies erneut schwieriger. Ein uo(hs("68747470733a2f2f7777772e736369702e6368")) ist nicht mehr ohne weiteres verständlich, ohne den Ablauf und Kontext der Applikation zu kennen.

Um ein Debugging durchführen zu können, ist es meistens erforderlich, dass spezielle Software zeitgleich mit der Malware ausgeführt wird. Die Malware selbst kann nun darum bemüht sein, solche Debugging-Software (z.B. Hex-Editoren, Disassembler, Antiviren-Produkte) als Prozesse zu erkennen und mittels Process.Kill abzuschiessen. Die Vorgehensweise unterscheidet sich je nach Windows-Generation im Detail, fusst aber stets auf dem gleichen Prinzip der Prozessidentifikation, die über die Kernel-API von kernel32.dll zur Verfügung gestellt wird:

iAppCount = 0
uProcess.lSize = Len(uProcess)
lSnapshot = CreateToolhelpSnapshot(2&, 0&)
lProcessFound = ProcessFirst(lSnapshot, uProcess)

Do While lProcessFound
   i = InStr(1, uProcess.sExeFile, Chr$(0))
   sExename = LCase$(Left$(uProcess.sExeFile, i - 1))

   If Right$(sExename, Len(sProcessName)) = LCase$(sProcessName) Then
      iAppCount = iAppCount + 1
      lProcess = OpenProcess(1&, -1&, uProcess.lProcessID)
      bAppKill = TerminateProcess(lProcess, 0&)
      Call CloseHandle(lProcess)
   End If

   lProcessFound = ProcessNext(lSnapshot, uProcess)
Loop
Call CloseHandle(lSnapshot)

Erweiterte Anti-Debugging-Techniken versuchen Schwachstellen in den jeweiligen Tools auszunutzen, um Analysen erschweren, verfälschen oder verhindern zu können.

Anti-Debugging wird bevorzugt durch Cyberkriminelle herangezogen. Diese werden also eher selten bei legitimen Malware-Tests eingesetzt. In der Regel nur dann, wenn damit ein praktikabler Vorteil erlangt werden kann. Also wenn zum Beispiel der Erkennung durch eine Antiviren-Lösungen entgangen werden kann. Intrusive und destruktive Komponenten sind bei professionellen Projekten, die die Identifikation von Schwachstellen zum Ziel haben, selten gefragt.

Datensammlung

Eine moderne Malware will in der Regel Daten sammeln, um diese zu Geld machen zu können. Hierbei gibt es verschiedene Quellen und Ansätze.

Dateien

Klassischerweise werden Dateien exfiltriert. Zu diesem Zweck müssen sie zuerst gefunden werden. In der Regel fokussiert man sich dabei auf interessante Muster in Dateinamen, Dateiinhalten oder Dateierweiterungen. Zum Beispiel könnten alle Word-Dokumente (Erweiterung .doc*) mit dem Inhalt Confidential gesammelt werden.

Um dies machen zu können, müssen alle Dateien auf den erreichbaren Datenträgern gefunden und durchsucht werden. Hierzu wird in der Regel eine rekursive Funktion verwendet, um Unterverzeichnisse mitberücksichtigen zu können.

Dies ist ein sehr aufwendiger, langwieriger und je nachdem ressourcenintensiver Prozess. Hier spielt also einmal mehr ein defensives Timing eine wichtige Rolle, um einer Überbelastung und damit einer Entdeckung entgehen zu können. Und es bleibt zu hoffen, dass kein DRM/RMS eingesetzt wird.

Registry

Neben dem Dateisystem bietet die Registry eine Fülle von Informationen, die gesammelt und exfiltriert werden können. Manche von diesen Informationen sind nützlich, um die Malware während der Ausführung zu steuern.

Zum Beispiel können Hinweise auf Antiviren-Lösungen gesucht werden. Je nach identifiziertem Produkt weiss man dann, welche Vorgänge von der Heuristik erkennt werden könnten. Einerseits legen Antiviren-Lösungen eigene Keys an, um eine persistente und systemweite Konfiguration zu etablieren. Sie werden aber manchmal auch mit dem Autostart der Registry bei jedem Startvorgang neu ausgeführt.

Manche Komponenten, sowohl des Betriebssystems als auch einzelner Applikationen, speichern aber auch Passwörter in der Registry. Falls die Keys bekannt sind, können sie ausgelesen werden. Unter .NET wird hierfür die Methode Registry.GetValue angeboten:

static object GetRegistryValue(string fullPath, object defaultValue){
   string keyName = Path.GetDirectoryName(fullPath);
   string valueName = Path.GetFileName(fullPath);
   return Registry.GetValue(keyName, valueName, defaultValue);
}

In eher seltenen Fällen liegen diese Passwörter heutzutage im Klartext vor. Oftmals werden aber standardisierte Hash-Mechanismen (z.B. MD5) oder proprietäre Ansätze mit zweifelhafter Stärke eingesetzt. Das Sammeln und nachträgliche Offline-Cracking auf einem dedizierten System kann sich durchaus lohnen.

Applikationsdatenbanken

Moderne Applikationen pflegen die durch sie anfallenden Daten in einer Datenbank zu bewirtschaften. Exemplarisch seien an dieser Stelle die Produkte von Mozilla genannt. Diese legen im Benutzerordner unter C:\Users\%USERNAME%\AppData\Roaming\Mozilla\Firefox\Profiles\<profilname>\ einzelne SQLite-Datenbanken an. Von Interesse in Bezug auf Auslesen oder Manipulieren sind die folgenden Dateien:

Über einen schlanken SQLite-Client kann auf diese zugegriffen werden. Ein solcher kann in der Malware mitgeliefert und dementsprechend unkompliziert und effizient eingesetzt werden.

SQLite Analyse der Formulardaten in Firefox

Screenshots

Das Anfertigen von Screenshots gehört bei vielen Backdoors zum guten Ton. Schliesslich lassen sich diese hervorragend zur Visualisierung in einem Bericht oder einer Abschlusspräsentation einbeziehen.

Hierbei muss zwischen dem Anfertigen des Screenshots und des Abspeichern dessen unterschieden werden. Die einfachste Form der Anfertigung ist das Schicken der Tastatursequenz für Print Screen. Dies kann wiederum über die Kernel-API von Windows durchgeführt werden, indem keybd_event (veraltet) bzw. SendInput von user32.dll angesteuert wird. Zuerst wird die Alt-Taste und zeitglich die Print-Screen-Taste gedrückt. Das Beiziehen von Alt reduziert den Screenshot auf das Fenster, das den Fokus hat. Wird dieses weggelassen, wird der Screenshot des gesamten Bildschirmbereichs angefertigt.

keybd_event(18, 0, 0, 0)
keybd_event(44, 0, 0, 0)

Die beiden Tasten müssen dann nur noch wieder losgelassen werden:

keybd_event(18, 0, &H2, 0)
keybd_event(44, 0, &H2, 0)

Noch einfacher aber weniger steuerbar geht es unter .NET mit der Klasse SendKeys, die ein lesbares Format unterstützt. Mit dem Prozentzeichen wird die Alt-Taste gedrückt. Der sprachabhängige Keycode {PRTSC} wird nach wie vor nicht unterstützt, weshalb dieser mit {1068} substituiert werden muss:

SendKeys "(%{1068})"

Nach dem Anlegen des Screenshot kann dieser in der Zwischenablage gefunden und mit der Methode Clipboard.GetData als unkomprimiertes BMP-Bild ausgelesen werden.

Screenshot des SAP Logon Pad

Problematisch bei diesem simplen Ansatz ist, dass jeweils der Inhalt der Zwischenablage mit dem neu angelegten Screenshot überschrieben wird. Dies kann mit den regulären Aktivitäten des Benutzers interferieren, wodurch dieser Verdacht schöpfen könnte. Aus diesem Grund sollte der bestehende Inhalt der Zwischenablage vor dem Anlegen des Screenshots ausgelesen und zwischengespeichert werden. Nach dem Anfertigen und abspeichern bzw. verschicken des Screenshots kann die Zwischenablage wieder freigegeben bzw. mit dem ursprünglichen Inhalt befüllt werden. Dies ist innerhalb eines Bruchteils einer Sekunde möglich. In diesem Zusammenhang ist es wichtig zu verstehen, wie Windows mit unterschiedlichen Datenformaten umgeht.

In manchen Fällen lohnt es sich deshalb, den Screenshot wirklich direkt über die Kernel-API durchzuführen, wodurch auf die Simulation von Tastatureingaben und dem Zwischennutzen der Zwischenablage verzichtet werden kann. Zum Beispiel dann, wenn Einschränkungen in Bezug auf Tastatureingaben zu erwarten sind. Dies ist bei Embedded Devices manchmal der Fall. Mit C# sieht eine Umsetzung folgendermassen aus und lässt sich vom Prinzip her unkompliziert in andere Sprachen portieren:

var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
var gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
bmpScreenshot.Save("Screenshot.png", ImageFormat.Png);

Der Vorteil ist, dass man hier mit Koordinaten arbeiten kann und so lediglich einzelne Fenster oder Teilbereiche festhalten kann (dies erfordert aber wiederum ein Verständnis für die Position und Grösse von Fenstern). Dies ist bei professionellen Tests wichtig, da aufgrund des Datenschutzgesetzes keine persönlichen bzw. privaten Daten gesammelt werden dürfen. Bei unseren Arbeiten fokussieren wir uns zum Beispiel auf die Ausgaben des SAP-Clients und verzichten auf eine Aufnahme des Mail-Clients. Zu diesem Zweck wird also zuerst geprüft, ob die gewünschte Applikation den Fokus hat und nicht durch eine unerwünschte Applikation verdeckt wird. Nur in diesem Fall wird dann für den vorgesehenen Bereich eine Aufnahme durchgeführt.

JPEG Kompression 80, 60, 40 und 20

Beim Abspeichern der Bilder bewährt sich in der Regel JPEG als Format. Durch die variabel definierbare Kompression wird es möglich, bei Bedarf Speicherplatz zu sparen. Dies ist besonders bei der Exfiltration von Nutzen. Im normalen Umfeld gilt eine Kompressionsrate von 80 als sinnvoll. Falls Speicherplatz wichtig wird, kann durchaus auch mit 60 oder 50 gearbeitet werden. Vor allem dann, wenn es hauptsächlich um die Erkennung von Bildern und weniger um die Möglichkeit des Lesens von kleineren Texten geht. Texte werden bevorzugt in Textform bzw. Dateien und nicht als Screenshot gesammelt.

Exfiltration

Die Datensammlung ist darum bemüht, die Daten für eine Exfiltration vorzubereiten. In einem nächsten Schritt werden diese also an den Angreifer zurückgeschickt.

Unidirektionale und bidirektionale Verbindungen

Der Einfachheit halber funktioniert ein Grossteil der Malware mit Exfiltrations-Mechanismen mit unidirektionalen Verbindungen. Es wird also nur eine Verbindung in eine Richtung initiiert. Diese Richtung ist normalerweise ausgehend von der Malware zum Angreifer. Dabei handelt es sich aus der Sicht der Malware um eine Outbound-Verbindung. Dies ist von entscheidendem Vorteil, denn viele Unternehmen schränken ausgehende Kommunikationen auf der Firewall nicht ein. Ganz im Gegenteil zu einer eingehenden Verbindung, die entweder wegen NAT (Network Address Translation) oder restriktiven Firewall-Regeln unterbunden wird.

Geschickt eingesetzte, unidirektionale Verbindungen reichen aus, um selbst ein Remote-Controlling der Malware vornehmen zu können. Hierbei wird dann zeitgleich ein Polling-Ansatz implementiert. Nur in wenigen Fällen ist eine bidirektionale Verbindung wirklich erforderlich.

Client/Server Prinzip

Die Malware basiert in diesem Fall auf dem klassischen Client/Server Prinzip. Die Malware selber ist der Client, der sich auf einen Backend-Server verbinden wird. Mit dieser Outbound-Connection können dann gesammelte Daten exfiltriert werden.

Die einfachste Variante ist ein FTP- oder Webserver, der die Möglichkeit von Uploads bereitstellt. Mit einem simplen ASP-, PHP- oder gar CGI-Skript können die hochgeladenen Daten entgegengenommen und Dateien einem Ordner abgespeichert werden.

<?php

error_reporting(0);

$fh = fopen('private/malware_exfiltration_data.txt', 'a');
fwrite($fh, $_SERVER['QUERY_STRING']."\n");
fclose($fh);

echo "Ok\n";

?>

Der Endpunkt des Backends ist im Idealfall aufgrund serverseitiger Konfiguration nicht direkt über die öffentliche Webschnittstelle erreichbar. Indem er zum Beispiel ausserhalb von webroot liegt oder durch eine entsprechende Konfiguration (z.B. mittels htaccess oder require) geschützt wird:

order deny,allow
deny from all
allow from 80.238.216.33

Zusätzlich besteht aber auch die Möglichkeit, dass die Malware neue Anweisungen für weitere Aktivitäten erhält. Dies kann einerseits die Konfiguration der Malware selbst sein, wie zum Beispiel das Ändern des Intervalls für Uploads oder die Definition eines neuen Backend-Servers. Oder es werden Anpassungen am Verhalten vorgenommen, indem zum Beispiel Registry-Keys geändert oder Dateien gelöscht werden.

In diesem Fall muss der Backend-Server eine aktuelle Konfiguration oder eine Queue mit neuen Anweisungen bereitstellen. Die Malware muss diese Daten holen, interpretieren und anwenden. Hier können simple CSV- oder INI-Dateien eingesetzt werden. Oder es werden Datenkonstrukte auf der Basis von XML und JSON verwendet.

Simplizität ist in jedem Fall angeraten. Muss zum Beispiel mit XML-Dokumenten umgegangen werden können, muss die Malware einen entsprechenden XML-Parser beinhalten. Dieser kann gross, schwerfällig und fehleranfällig sein. Zwar stellt wiederum Windows von Haus aus gewisse Module zur Verfügung. Diese funktionieren aber je nach Windows-Version unterschiedlich. Mit CSV-Dateien zu arbeiten und diese mit einem String.Split zu dissektieren ist bedeutend einfacher. Weniger ist oftmals mehr.

Die bereitgestellte Queue kann über den gleichen Endpunkt ausgeliefert werden, der die Datenannahme ermöglicht. In der Response sind halt einfach die Steuerdaten enthalten. Hier hat sich die Adaption altbekannter Konzepte wie Statuscodes (siehe HTTP) bewährt. Falls die Malware eine Anweisung entgegengenommen und verarbeitet hat, sollte dies dem Backend mitgeteilt werden, damit die Anweisung aus der Queue wieder entfernt werden kann. Dies kann mit Sequenznummern (siehe IP) gemacht werden. Zur Steuerung mehrerer Clients über den gleichen Endpunkt ist eine Client-ID von Nutzen. Diese ist in der Queue ebenfalls enthalten bzw. wird durch die Malware ausgewertet, wodurch einzelne (Unicast) oder mehrere Clients (Multicast oder Broadcast) gezielt mit Anweisungen versorgt werden können.

Sichere Übertragung

Gerade in professionellen Malware-Tests ist es erforderlich, dass die Exfiltration von Daten in gesicherter Form stattfindet. So wird zum Beispiel vorausgesetzt, dass eine verschlüsselte Kommunikation zum Backend stattfindet. Im Beispiel von Web kann dies mit der Übergabe einer Webadresse mit https:// erfolgen.

Bei der Nutzung der Kernel-API wininet.dll für Webzugriffe muss dies über InternetConnect definiert werden.

Oder man nutzt eigenständig die Microsoft CryptoAPI, um die Daten vor dem Versand zu verschlüsseln. Dies erfordert aber ein solides Verständnis für die bereitgestellten Mechanismen.

In manchen Fällen kann es sich anbieten, dass eine eigene bzw. zusätzliche Verschlüsselung (Overcipher) eingesetzt wird. Davon ist aber in der Regel abzuraten, da sowohl die Komplexität als auch die Fehleranfälligkeit um ein Vielfaches steigt. Für einfachere Codierungen reicht es aber oftmals.

Umgang mit grossen Daten

Jenachdem können bei der vorangegangenen Datensammlung sehr viele und vor allem grosse Dateien anfallen. Letztgenannte könnten gar versehentlich für eine Übertragung vorgesehen werden. Dies kann zu Komplikationen führen:

Aus diesem Grund ist es empfehlenswert, dass vor der Datenübertragung die Grösse der Dateien, zum Beispiel mit FileInfo.Length, geprüft wird. Grosse Dateien sollten gesondert behandelt und auf die Übertragung von zu grossen Dateien (mehrere hundert MB) gänzlich verzichtet werden.

Falls sehr grosse Dateien (z.B. Datenbanken) dennoch exfiltriert werden müssen, können sie in einzelne Teile zerlegt und separat transferiert werden. Der Einsatz einer Kompression, entweder vor (z.B. ZIP oder RAR) oder während der Übertragung (HTTP-Komprimierung), können zusätzliche Optimierungen versprechen.

HTTP Reverse Connection

Als erste Wahl für den Aufbau von Netzwerkkommunikationen zum Backend hat sich HTTP hervorgetan. Einerseits handelt es sich um ein relativ simples Protokoll. Dieses zu verstehen und mit ihm zu arbeiten ist unkompliziert möglich.

Zusätzlich gilt es als robust und wird auf allen Plattformen und in den meisten Netzwerken ohne nennenswerte Einschränkungen unterstützt. In Windows-Umgebungen gibt es gar verschiedene Techniken, wie HTTP-Verbindungen umgesetzt werden können. Traditionell wurde hierfür das archaische OCX-Control winsock.ocx verwendet. Dieses muss aber auf dem Zielsystem installiert bzw. registriert sein. Falls nicht, muss es mit der Malware mitgeliefert werden. Die unterschiedlichen Windows-Versionen und Architekturen machen es dann erforderlich, verschiedene Varianten mitzugeben. Von diesem Ansatz ist deshalb abgeraten.

Heutzutage lässt sich WinSock in .NET aber unkompliziert über den Namespace System.Net.Sockets einbinden. Als Entwickler ist man dann aber für eine Vielzahl an grundlegenden Funktionen selber verantwortlich.

Stattdessen kann wiederum auf den Kernel gesetzt werden. Mit wininet.dll werden standardisierte und vom Betriebssystem gesteuerte Möglichkeiten angeboten. Aus diesem Grund sind sehr wenige hostbasierte Intrusion Detection-Systeme oder Antiviren-Lösungen in der Lage, darüber umgesetzte Zugriffe als illegitim zu erkennen.

Eine ebenso zuverlässige Alternative ist das Einbinden des Internet Explorers als Control oder das Ansteuern als Objekt (das Verhalten ist praktisch identisch). In diesem Fall wird innerhalb der Malware ein Internet Explorer gestartet. Dieser wird genutzt, um Webzugriffe durchzuführen. Dies kommt einer automatisierten Nutzung des Standardwebbrowsers gleich. Auch hier ist es praktisch unmöglich für Intrusion Detection-Systeme oder Antiviren-Lösungen eine Erkennung durchzusetzen.

Public Function DownloadIE(ByRef sUrl As String, Optional ByRef iVisible As Integer = 0) As String
    Dim ie As Object
    Dim iState As Integer
    Dim dTime As Date
    Dim sResult As String

    On Error Resume Next
    Set ie = CreateObject("InternetExplorer.Application")
    ie.Visible = iVisible
    ie.Navigate sUrl

    iState = 0
    dTime = Now + TimeValue("00:00:30")

    Do Until iState = 4 Or Now > dTime
        DoEvents
        iState = ie.ReadyState
    Loop

    sResult = ie.Document.body.innerHTML
    ie.Quit
    DownloadIE = sResult
End Function

Problematisch ist, dass der Internet Explorer stallen (freeze) kann, also nicht mehr ansprechbar ist. Zum Beispiel, weil ein Timeout auf sich warten lässt (wird mit dTime nach 30 Sekunden abgefangen) oder eine Fehlermeldung generiert wurde. Es ist deshalb nützlich, mehrere Instanzen des Controls zu betreiben und solche, die nicht mehr ansprechbar sind, mit einem separaten Thread erkennen und freigeben zu können. Andernfalls wird der Speicher aufgebraucht (Memory Leak) und es sind keine weiteren Controls (Ressource Exhaustion) mehr nutzbar.

Durch die DOM-Struktur von HTML-Dokumenten in ie.Document können dann einzelne Elemente unkompliziert angesteuert werden. Dies bedingt aber, dass die Rückgabe valides HTML ist. Mit alternativen Formaten, wie zum Beispiel RSS oder XML kann nicht ohne weiteres umgegangen werden.

Schwierig kann es werden, wenn für den (ersten) Webzugriff die Authentisierung bei einem Proxy erforderlich wird. Obwohl in professionellen Umgebungen heutzutage transparente Proxies zum Einsatz kommen, kann dies der Fall sein. Falls man ein Internet Explorer Control nutzt, kann unter Umständen auf dessen Konfiguration abgestützt werden. Sollte man dies jedoch nicht tun können, können die lokalen Proxy-Daten aus der Registry ausgelesen werden. Diese finden sich für den aktiven Benutzer unter HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings. Dadurch lassen sich diese auch in anderem Kontext (z.B. mit alternativem Browser oder eigener HTTP-Engine) verwenden.

Email

Die Exfiltration per Email ist ein altbekannter Ansatz, der durch klassische Computerwürmer für ihre Propagierung genutzt wurde. Genauso können aber auch nur die gesammelten Daten so verschickt werden.

Der simple Ansatz besteht darin, direkte SMTP-Kommunikationen durchzuführen. Zu diesem Zweck muss in der Malware ein rudimentärer Mailclient implementiert werden. Und dieser muss die Möglichkeit haben, mit einem SMTP-Relay kommunizieren zu können und dürfen. Entweder wird also durch den Malware-Entwickler ein Relay zur Verfügung gestellt oder man benutzt das in der Zielumgebung vorgesehene Relay. Dies kann geschehen, indem zum Beispiel die eigene Domain ausgelesen und Varianten wie smtp.example.com oder mail.example.com ausprobiert werden. Oder man liest die Konfiguration des infizierten Systems aus, um zum Beispiel die im lokal installierten Mail-Client definierten Server zu finden. Problematisch kann dies werden, wenn für den Versand eine Authentisierung erforderlich wird. Aber auch hier ist davon auszugehen, dass die dafür notwendigen Credentials auf dem lokalen System irgendwo abgelegt sind.

Dabei ist wichtig zu bedenken, dass Email nicht zwingend nur ein unidirektionaler Ansatz sein muss. Falls ein Mailclient auf dem Zielsystem für den Empfang von Nachrichten vorhanden ist, kann dieser genutzt werden, um aktiv Anweisungen an die Malware zu schicken. Zum Beispiel bietet Microsoft Outlook eine Schnittstelle namens ISAPI an, mit der darauf zugegriffen werden kann.

In manchen Fällen erfordert die angesteuerte Anwendung ein Zutun des Benutzers, der zum Beispiel eine Warnmeldung bestätigen muss. Falls eine solche nicht modal ausgeführt wird, kann sie automatisiert durch die Malware bestätigt werden. Andernfalls hat sich bewährt, dass die Warnmeldung mit einer eigenen Meldung überdeckt und damit eine andere Funktion der Bestätigung suggeriert wird (Clickjacking). Hierzu kann in user32.dll die Funktion SetWindowPos verwendet werden:

Public Sub FormOnTop(hWindow As Long, bTopMost As Boolean)
    Dim wFlags As String
    Dim Placement As String

    Const SWP_NOSIZE = &H1
    Const SWP_NOMOVE = &H2
    Const SWP_NOACTIVATE = &H10
    Const SWP_SHOWWINDOW = &H40
    Const HWND_TOPMOST = -1
    Const HWND_NOTOPMOST = -2

    wFlags = SWP_NOMOVE Or SWP_NOSIZE Or SWP_SHOWWINDOW Or SWP_NOACTIVATE

    Select Case bTopMost
    Case True
        Placement = HWND_TOPMOST
    Case False
        Placement = HWND_NOTOPMOST
    End Select

    SetWindowPos hWindow, Placement, 0, 0, 0, 0, wFlags
End Sub

Eine oft vergessene, da unterschätzte Form des Backdoorings in Outlook kann durch Rules umgesetzt werden. In diesem Fall werden Rules and Alerts erstellt, die beim Eintreffen spezieller Nachrichten eine Aktion auslösen. Dies ist zum Beispiel als Zweitinfektion empfehlenswert, um eine Persistenz bei einer Kompromittierung gewährleisten zu können.

Backdooring mit Outlook Rules

Exotische Ansätze

Es existiert eine Reihe weiterer Ansätze, mit der Daten exfiltriert oder eine Kommunikation mit der Malware hergestellt werden kann. Ein Ansatz besteht darin, dass ein untypischer Dienst als Dreh und Angelpunkt verwendet wird. Zum Beispiel könnten Daten über NNTP ins Usenet bzw. einen privaten Newsserver geladen werden.

Dieser Ansatz ist vor allem dann von Nutzen, wenn zum Beispiel ausgehender Datenverkehr für populäre Dienste wie HTTP und SMTP umfassend geprüft und eingeschränkt wird. Zum Beispiel, weil eine Firewall-Komponente mit Proxy-Funktionalität samt Authentisierung, ein Intrusion Prevention-System oder eine Data Loss Prevention-Lösung eingesetzt wird. NNTP lässt solche Einschränkungen nicht in der bekannten Form zu.

Twitter koordiniert Backdoor

Alternativ werden die Daten auf einer öffentlichen Plattform, wie zum Beispiel Twitter oder WikiPedia, abgelegt. Das Ziel liegt darin, dass hier möglichst populäre Dienste genutzt werden, bei denen Zugriffe nicht als verdächtig taxiert werden. Der entscheidende Nachteil ist aber, dass halt eine Exfiltration dem Seitenbetreiber oder den Besuchern auffallen könnte. Ohne Verschlüsselung sind solche Möglichkeiten, vor allem im Rahmen eines Kundenprojekts, ein Tabu.

Error Handling

Software-Entwicklung generell und Malware-Entwicklung im Speziellen ist fehleranfällig. Die Chancen sind gross, dass bei einer Programmausführung Zustände gegeben sind, die zu Fehlern führen. Diese werden typischerweise durch die betroffene Software, das eingesetzte Framework oder das darunterliegende Betriebssystem ausgegeben. Eine Malware möchte das aber genau nicht, da dies ein verräterisches Indiz sein kann, wodurch die erfolgreiche Infektion als solches gefährdet wird.

Fall-back Mechanismen

Eine professionelle Malware muss ein Maximum an Zuverlässigkeit gewährleisten können. Dies bedeutet, dass wenn eine Funktion nicht erfolgreich genutzt werden kann, auf eine alternative Funktion zurückgegriffen werden muss (Fall-back). Dies bedingt, dass einerseits der Fehlschlag als solcher erkannt wird. Wird zum Beispiel eine Datei an den Backend-Server übertragen, kann dieser in seiner Rückantwort die Dateigrösse quittieren. Entspricht diese nicht der erwarteten Grösse, muss von einem Fehler bei der Übertragung ausgegangen werden.

In der Regel ist es empfohlen, einen zweiten Versuch vorzunehmen (Re-transmission), da vielleicht nur wegen unglücklichen Umständen der Erfolg ausblieb. Ist aber auch der zweite Versuch nicht von Erfolg gekrönt, sollte für die Laufzeit der Malware dieser Zustand gespeichert und von nun an auf einen anderen Kanal gewechselt werden.

Falls es sich um eine unkritische Datenübertragung handelt – dies ist immer zuerst mit dem Kunden abzuklären – kann versucht werden von HTTPS auf HTTP zu wechseln (Downgrade). Vielleicht gibt es irgendein Problem mit dem Zertifikat, SSL-Handshake oder HTTPS-Zugriffen. Oder man wechselt direkt auf ein anderes Kommunikationsprotokoll.

Suppression

Wer zum ersten Mal eine professionelle Malware entwickelt, wird ab etwas besonders erstaunt sein: Ein Grossteil der Zeit wird damit verbracht, Fehler zu unterdrücken.

Dies betrifft an erster Stelle Programmfehler, die bei der Ausführung auftreten. Dazu gehören beispielsweise Overflows, Type Mismatches und Division-by-Zero. Eine saubere und vor allem sichere Programmierung hilft dabei, die Malware intrinsisch stabil zu halten.

Falls denn ein Fehler auftritt, muss er unterdrückt werden. Eine Malware darf im laufenden Betrieb niemals eine Fehlermeldung ausgeben. Jeder Benutzer würde sofort Verdacht schöpfen, wenn eine unsichtbare Software plötzlich den Dialog mit dem Text Bad file name or number generiert. Bei VB6, VBA und VBscript kommt die Anweisung On Error Resume Next zum Tragen. Diese ist in der Lage die meisten Fehler einfach zu übergehen. Es wird mit der nächsten Anweisung fortgefahren. Bei C#, Java und ähnlichen Sprachen kann ein try-catch eingesetzt werden.

Nutzen im Projekt

Die Entwicklung von Malware im professionellen Umfeld hat zum Ziel, Schwachstellen in Umgebungen identifizieren und damit frühestmöglich adressieren zu können. Der Einsatz von solchen Malware-Tests im Rahmen eines Projekts kann vielfältig und vielschichtig sein.

Vollumfängliche Analyse

Der grösste Vorteil von Malware-Tests ist, dass eine vollumfängliche Analyse eines Unternehmens stattfinden kann. So wird es möglich, dass sämtliche Facetten des Sicherheitsdispositivs tangiert werden. Dies beginnt bei Awareness, Mail- und Websicherheit, geht über Einstellungen von Proxies, Firewalls und Antiviren-Lösungen bis hin zu Prozessabläufen, Incident Response und Business Continuity Management.

Obschon es nicht unserem üblichen Auftreten entspricht, garantieren wir bei Malware-Tests eine 100 %ige Erfolgschance. Mit entsprechender Zeit und Aufwand ist es zwar immer möglich eine Umgebung zu kompromittieren. Zusammen mit dem Kunden wird es aber im Nachgang möglich zu erkennen, wo die Schwächen sind. Diese können dann eliminiert werden, so dass die Aufwände für einen Angreifer so stark ansteigen, dass sich Attacken nicht mehr lohnen bzw. breitflächige Angriffe nicht erfolgreich sein können.

Professionelle APT-Simulation

Viele Kunden führen regelmässig Security Assessments oder gar Penetration Tests durch. Hierbei handelt es sich meist um zeitlich und technisch klar abgesteckte Projekte. Mit einem Malware-Test kann hingegen eine fokussierte und langanhaltende Attacke (APT, Advanced Persistent Threat) auf professionellem Niveau simuliert werden.

Die Simulation ist dabei nicht nur auf potentielle Möglichkeiten beschränkt, sondern nutzt gefundene Schwächen konkret aus. Damit werden sehr klare Aussagen, die mit fundierten technischen Abklärungen untermauert werden können, möglich.

Teil von Awareness-Kampagnen

Bevorzugt werden die Malware-Tests mit Awareness-Kampagnen verknüpft. In diesem Fall findet die – mindestens erste – Infektion als Phishing per Email statt. Entweder beinhaltet das Schreiben einen Download-Link zur Malware (z.B. Pharming) oder diese ist direkt als Attachment beigelegt.

Der Erfolg eines solchen Social Engineering wird erhöht, indem ein möglichst personalisiertes Schreiben aufgesetzt wird. Die Plausibilität und Professionalität eines solchen Spear-Phishing darf gar keinen Zweifel daran lassen, dass es nicht legitim sein könnte. Erfahrungsgemäss können bei gross angelegten Phishing-Kampagnen in einem ersten Lauf Infektionen im Bereich von 15 bis 30% erreicht werden. Selbst direkt nach einem Awareness-Training, bei dem die Benutzer auf solche Risiken hingewiesen werden, lassen sich mindestens 5% der Zielpersonen zur kompromittierenden Handlung bewegen.

Alternativ können im Umfeld der Opfer USB-Sticks, die verloren gegangen schienen, verteilt werden. Dies in der Hoffnung, dass ein solcher mitgenommen und mit ihm eine Infektion durchgeführt wird. Hierzu konnte früher die Autorun-Funktionalität unter Windows genutzt werden. Hierzu wird im Wurzelverzeichnis des Datenträgers eine Datei namens autorun.inf angelegt. Beim Einlesen des Datenträgers wird dann die vordefinierte Datei ausgeführt:

[autorun] 
open=start.exe
icon=icon.ico,0

Microsoft hat ab Windows XP nachgebessert und erlaubt keine automatische Ausführung mehr. Benutzer müssen mittlerweile also auch hier explizit die Datei ausführen. Es liegt in diesen Fällen somit in der Verantwortung des Empfängers, ob er die getarnte Malware ausführen will oder nicht.

Dem entgegenwirken kann man mit Lösungen wie Teensy oder Rubber Ducky. Hierbei handelt es sich um sogenannte Keystroke Injection Tools. Diese werden beim Einstecken nicht als USB-Datenträger sondern als USB-Tastatur erkannt. Dementsprechend können sie dann auch vordefinierte Kommandoeingaben auslösen. Damit ist es möglich, ein Backdooring vorzubereiten, indem zum Beispiel versteckte Konten angelegt oder eine Malware heruntergeladen wird.

Zusammenfassung

Die Entwicklung von Malware im professionellen Umfeld stellt ganz besondere Anforderungen an Zuverlässigkeit. Schliesslich soll damit die Sicherheit einer Umgebung geprüft werden, ohne diese willentlich oder versehentlich negativ zu beeinträchtigen.

Mit der Infektion durch einen Wurm, typischerweise mittels Exploiting oder per Webdownload, kann dann das kompromittierte System und die Umgebung analysiert werden. Nach erfolgreicher Datensammlung, lokale Dateien und die Registry bieten sich kann, können die Daten exfiltriert werden. Hierzu werden bevorzugt klassische Mechanismen wie HTTP eingesetzt, um Daten auf dem Backend-Server abzulegen.

Im Rahmen eines solchen Malware-Tests wird es möglich, sämtliche Aspekte des Sicherheitsdispositivs eines Unternehmens zu untersuchen. Dies beginnt bei der Awareness der Benutzer, geht über die Konfiguration der Sicherheitskomponenten bis hin zu den etablierten Prozessen. Sie stellen also eine hervorragende Ergänzung zu klassischen Security Assessments oder Penetration Tests dar.

Über den Autor

Marc Ruef

Marc Ruef ist seit Ende der 1990er Jahre im Cybersecurity-Bereich aktiv. Er hat vor allem im deutschsprachigen Raum aufgrund der Vielzahl durch ihn veröffentlichten Fachpublikationen und Bücher – dazu gehört besonders Die Kunst des Penetration Testing – Bekanntheit erlangt. Er ist Dozent an verschiedenen Fakultäten, darunter ETH, HWZ, HSLU und IKF. (ORCID 0000-0002-1328-6357)

Links

Werden auch Ihre Daten im Darknet gehandelt?

Wir führen gerne für Sie ein Monitoring des Digitalen Untergrunds durch!

×
Konkrete Kritik an CVSS4

Konkrete Kritik an CVSS4

Marc Ruef

scip Cybersecurity Forecast

scip Cybersecurity Forecast

Marc Ruef

Voice Authentisierung

Voice Authentisierung

Marc Ruef

Bug-Bounty

Bug-Bounty

Marc Ruef

Sie wollen mehr?

Weitere Artikel im Archiv

Sie brauchen Unterstützung bei einem solchen Projekt?

Unsere Spezialisten kontaktieren Sie gern!

Sie wollen mehr?

Weitere Artikel im Archiv