Einleitung
Den Sourcecode stellt der Programmierer frei im Internet zur Verfügung. So ist es nicht sehr überraschend, dass die exe-Datei ebenso wie die dll-Datei von nahezu jedem Antivirus-Programm sofort eliminiert werden (sollte das bei euch nicht der Fall sein dann ist euer System nicht mit dem Basisschutz unterwegs!).
Die Herausforderung für mich war nun, den Code auf modernen Betriebssystemen dennoch zum Laufen zu bringen. Dazu gab es einige Hürden zu nehmen. Und die Evolution meiner Code-Modifikation möchte ich hier einmal vorstellen.
Ein wichtiger Hinweis: es geht mir in diesem WSHowTo ausdrücklich nicht um das Nachbauen meiner Lösung. Vielmehr liegt mein Fokus beim Aufzeigen verschiedener Strategien zum Umgehen von Schutzmaßnahmen. Im Endeffekt wird einmal mehr deutlich werden: es gibt keinen effektiven Schutz von solchen Angriffsszenarien!
Wir brauchen als Administratoren immer einen Plan B nach der Devise „Asume the Breach – ein Angreifer kann erfolgreich sein“. Was machst ihr dann?
Die Testbenutzerin hat keine administrativen Rechte. Alle Standardschutzkomponenten sind unverändert aktiv (Es ist also durchaus für die BlueTeamer noch Luft nach oben!).
Die Mutation eines Schadcodes
Es muss also eine andere Lösung her. Ganz ehrlich: das wäre auch zu einfach gewesen… 🙂
Die Powershell kann aber auch Date als BASE64-Code verarbeiten. Somit konnte die dll-Datei direkt in das PowerShell-Script integriert werden. Und genau das haben die Entwickler getan: eine komplette Lösung in einer Textdatei. Meine Version ist nur optisch etwas korrigiert (und damit lesbarer):
Das fertige Script kann man im Internet herunterladen. Antivirus-Hersteller tun das natürlich auch, und somit werden diese Textdateien als Schadcode erkannt und eliminiert. Hier seht ihr meinen Kaspersky AV bei der Arbeit:
Das Script wird zuverlässig bewertet:
Interessant ist aber, dass offensichtlich nicht alle Hersteller diese Dateien in ihre Erkennung integrieren. Der (aktuelle) Windows Defender lässt die Datei einfach auf meinem Desktop liegen:
Aber seit Windows 10 gibt es ja AMSI – das Anti Malware Scan Interface! Dieses dient als Schnittstelle zwischen einem AV (Antivirus) und diversen Komponenten im Betriebssystem. Dazu gehört auch die PowerShell. Der Code wird also zur Laufzeit gescannt und erfolgreich blockiert:
So ist der Schutz wieder gewährleistet. Oder? 🙂 Naja, das waren die öffentlich auffindbaren Varianten. Auf geht’s zu meiner Modifikation!
Daher hatte ich 2 Ziele:
- Der Code soll als Textdatei nicht erkannt werden
- Der Code soll zur Laufzeit in der PowerShell nicht erkannt werden
Die Methode wird obfuscation (Verschleierung) genannt. Aber egal wie man es dreht: Zur Laufzeit muss der Code interpretierbar und somit lesbar sein! Daher versuchte ich es mit einer BASE64-Umwandlung. Doch moderne AV-Lösungen können das einfach wieder umkehren und den Code erkennen – ähnlich wie sie zip-Dateien öffnen und den Inhalt analysieren. Ein einfaches BASE64 genügte nicht! Aber was wäre, wenn ich im BASE64-Text bestimmte Zeichen durch andere ersetze und diesen Vorgang zur Laufzeit umkehre? Dann kann ein Scanner nur noch Textfragmente erkennen – und schlägt vielleicht nicht mehr an!
So könnte eine Obfuskierung aussehen. Die 3. Ausgabezeile ist für einen AV nicht mehr lesbar:
Überträgt man das nun auf das originale Script, dann wird aus dem Klartext links der verschleierte BASE64-Code rechts. Eine Zeile „obfuscated“ entspricht dabei einer ganzen Funktion!
Zur Laufzeit werden nun einfach alle unlesbaren Zeilen in lesbares BASE64 und dann in Klartext interpretiert. Der Rest ist ein einfaches Invoke-Expression! So kann die Datei gespeichert werden, ohne dass der Kaspersky aktiv wird:
Und das gilt natürlich auch für den Windows Defender (der die Ursprungsdatei ja auch schon ignorierte):
Aber was erkennt der AV über AMSI? Erkennt er den Code vielleicht an der Reihenfolge der Codezeilen?
Finden wir es heraus: ich kann ja die Funktionsanweisungen zur Laufzeit in einer zufälligen Reihenfolge verarbeiten (get-random). Der PowerShell und auch mimikatz ist es egal, in welcher Folge die Deklaration vorgenommen wird, solange zum Ende alle Puzzleteile vorhanden sind. Das könnte dann so aussehen (nebenbei: auch die dll-Datei kann obfuskiert werden):
Zur Laufzeit würde das dann (mit ner kleinen Modifikation für die Anzeige) so aussehen: jeder Lauf wäre anders:
Damit wäre ein großer Teil der Heuristik in einem AV umgangen. 🙂
Und was sagt unser AV dazu? Der Windows Defender ist bereits komplett ausgetrickst:
ABER: er erkennt, dass hier etwas Böses passierte und reagiert ganz knapp nach der Anzeige der sensiblen Informationen und beendet den gesamten PowerShell-Prozess:
Das Bild mit dem Ergebnis habe ich erst nach einigen Versuchen snippen können. Ein Angreifer könnte die Informationen aber auch in eine Textdatei umleiten. Z.B. mit dem Transcript:
Die Informationen sind sichtbar. Nur kommt immer noch der Alarm! Was genau erkennt der Defender? Wir müssen weiter graben…
Würde es also helfen, wenn die Funktionen anders benannt sind? Das war einfach zu simulieren: Eine eigene Funktion analysiert den Code und findet alle verwendeten Funktionsnamen heraus. Eine zweite Funktion erstellt für jede einen zufälligen Alias. Und dann werden einfach alle Normalnamen durch die Aliase ersetzt. So kommt bei jedem Lauf ein neues Script heraus, da die Aliase zufällig sind:
Im Vergleich zum originalen Code sieht man die Arbeitsweise sehr einfach:
Jetzt generieren wir mit einer dritten Funktion noch die Datei mit der Obfuskierung (habt ihr gedacht, ich editiere manuell?? :-)). Heraus kommt eine unlesbare Datei, die zur Laufzeit in zufälliger Reihenfolge die zufällig benannten Funktionen deklariert:
Natürlich wird auch diese Datei nicht vom AV erkannt, denn er kann sie immer noch nicht lesen:
Aber wie schaut es mit der Laufzeit aus? Im letzten Versuch wurde zuletzt der gesamte PowerShell-Prozess terminiert… Ich starte den Code im Kontext meiner Testbenutzerin:
Leider wird das Ergebnis auch nur ganz kurz gezeigt:
Und dann beendet der Defender die PowerShell:
Schade: das Ändern der Funktionsnamen hat nicht genügt, um den Windows Defender zu täuschen. Man könnte natürlich noch weitere Änderungen vornehmen:
- sinnfreie Anweisungen könnten integriert werden
- Variablen kann man wie die Funktionen umbenennen
- Kommentare können gelöscht werden (auch da stehen gfg. TriggerWords drin)
Ich wollte aber was anderes probieren. Durch einen kleinen Zufall habe ich etwas Interessantes herausgefunden…
Der Code wurde ohne Warnung ausgeführt. Und die PowerShell ISE blieb offen!!! Das weckte meine Neugier! Was macht die ISE anders als die powershell.exe? Die Antwort ist einfach: die ISE startet den Code in einem Subprozess während die powershell.exe die Ausführung selber übernimmt. Das lässt sich doch nachstellen… 🙂
Dafür benötige ich nur einen kleinen Launcher – also nen Code, den ich an eine weitere PowerShell übergeben kann. Da die PowerShell mit BASE64 kodierten Aufrufen umgehen kann fand ich darin gleich noch eine zusätzliche Verschleierung. In dieser BASE64-Anweisung steht eigentlich nur drin: „Lieber SubProzess, bitte ließ die Scriptdatei und führe den Text mit Invoke-Expression aus“:
Es wird Zeit für den Versuch:
GESCHAFFT! Der Code läuft und wird vom Windows Defender komplett ignoriert! Auf einem aktuellen Windows 10 Version 1903!
Aber vielleicht ist damit nur der Defender überfordert. Was sagt denn mein Kaspersky AV dazu?
Dieser Code schlägt durch! Und durch die neuen Generator-Funktionen bekomme ich jedes Mal ein neues Script! Es ist also mit wenig Aufwand möglich, die modernen Systeme zu kompromittieren!
Schutzmaßnahmen
Natürlich kennen auch die AV-Hersteller diese Methoden und rüsten ihre Produkte entsprechend aus. Aber wenn wie in meinem Beispiel der Mechanismus manuell bzw. individuell programmiert wird, dann haben die Schutzprogramme keine Chance…
Aber wie schützt man sich und seine Infrastruktur denn davor? Die Antwort ist nicht ganz einfach. Ich würde ein System aus 2 Komponenten empfehlen: aktive Schutzvorkehrungen und aktives Monitoring.
Dazu würde ich aber immer entsprechende Systemhärtungen vornehmen. Dazu zählen
- Eingesetzte Betriebssysteme und Software sollte fehlerfrei konfiguriert sein
- Die Einschränkung der Accounts, die ins Internet dürfen.
- Die Einschränkung der administrativen Accounts auf wenige Systeme (siehe mein Blogbeitrag zum Thema „Tier-Management und SecurityScopes“
- Das Abschalten von veralteten und unsicheren Protokollen. Dazu zählt für mich auch das Verhindern der Passwortspeicherung. Denn jeder Speicher kann kompromittiert werden. Kein System ist sicher…
Zusammenfassung
Es gibt keinen totalen Schutz! Asume the breach! Aber ihr könnt es den Angreifern (sehr) schwer machen. Und das ist der Reiz für mich als Verteidiger. Seid kreativ und bleibt mit eurem Wissen aktuell. Dann tretet ihr euren Angreifern auf Augenhöhe gegenüber!
Ich distanziere mich an dieser Stelle noch einmal ausdrücklich vom Angebot über funktionierenden Schadcode und verweise auf den rein akademischen Aspekt dieses Beitrags!
Stay tuned
Und wie gewohnt könnt ihr hier das PDF zum Artikel herunterladen.