WS IT-Solutions

PS-Hacking – ReversePowerShell

Servus aus dem schönen Niederbayern!

Seit einiger Zeit beschäftige ich mich mit meinem BashBunny und suche nach weiteren interessanten Angriffsvektoren. Es gibt so viele fertige Scripte und Tools – aber ich wollte etwas eigenes! Inspiriert vom Meterpreter hab ich eine kleine Scriptreihe erstellt. Mit dieser kann ich einen Rechner nahezu komplett fernsteuern.

Aber immer der Reihe nach.

Was ist eine ReverseShell?

Einen Rechner über das Netzwerk anzugreifen ist nicht leicht, da es viele Sicherheitsmechanismen gibt. Und nicht jede Sicherheitslücke im Betriebssystem oder von laufenden Programmen ist über das Netzwerk aus exploit-fähig. Gerade die Windows-Firewall schützt bereits in ihrer Grundkonfiguration vor vielen solcher Angriffe – aber sie blockiert leider nur eingehenden Datenverkehr. Den ausgehenden Traffic lässt sie einfach durch.

Und hier setzt eine ReverseShell an. Man bringt den Benutzer dazu, einen Schadcode zu starten, der dann als ausgehender Datenstrom einfach durch die Firewall hindurch „nach Hause“ telefoniert. Da die Verbindung von beiden Seiten aus verwendet werden kann, ist es danach viel einfacher, das System weiter zu infiltrieren.

In meinem Fall handelt es sich um ein PowerShell-Script, das nach seinem Start eine TCP-Verbindung zu meinem Server aufbaut – es ist eine Reverse-Shell. Reverse bedeutet in diesem Fall, dass der Angreifer keine Verbindung zum Zielrechner aufbaut, sondern der Zielrechner kontaktiert den Angreifer. Das Szenario nennt man auch „Command & Control“. Daher habe ich in meinen Scripten dieses Kürzel CC verwendet.

Wie funktioniert die ReversePowerShell?

Die Scriptreihe besteht aus einem Script für den Client und ein Server-Master-Script. Dazu existieren etliche weitere Hilfsscripte:

Im Client-Script CC-Client.ps1 steht die IP-Adresse und der TCP-Port des CC-Servers drin:

Sobald der CC-Client gestartet wurde, versucht er eine TCP-Verbindung zu dem CC-Server aufzubauen. Kommt diese nicht zustande, dann wartet er eine Sekunde und versucht es dank einer Schleife erneut:

Auch das Script des Servers enthält die Variablen der TCP-Verbindung:

Wird auf dem Angreiferrechner das CC-Server-Script gestartet, dann baut dieses einen TCP-Server mit den definierten Parametern auf und wartet auf den CC-Client:

Wenn der Client den Server erreicht, dann wird der CC-Server die Verbindung nutzen, um eine Vielzahl von Codezeilen auf den Client zu übertragen. Das ist erforderlich, denn der Client ist komplett Schadcodefrei! Die Codezeilen werden direkt im Arbeitsspeicher des Clients ausgeführt. Ein Virenscanner wird es da nicht leicht haben…

Danach ist der Server einsatzbereit:

Mein Ziel war hierbei eine saubere Modularisierung. Ich wollte eine neue Idee oder eine neue Funktion einfach in das Konstrukt integrieren können ohne aufwendige Anpassungen vornehmen zu müssen. Dafür habe ich mehrere Dateiverzeichnisse erstellt, in dem ich jede neue Zusatzfunktion als eigene Scriptdatei gespeichert habe:

Erkennt der CC-Server die Verbindung eines CC-Clients, dann wird er mit einer Schleife jede Scriptdatei einlesen, als RemoteFunktion auf den CC-Client übertragen (das sieht man im Bild des Servers weiter oben) und final auf dem CC-Server eine einfach aufzurufende lokale Funktion erstellen. Das kann man sich dann auch schön mit der eingebauten Hilfe-Funktion cc-help anzeigen lassen:

Cool, oder? 🙂

Wenn die Clientinitialisierung abgeschlossen ist, dann kann man auf dem CC-Server die lokalen Funktionen nutzen, um den Zielrechner zu beeinflussen.

Wie kommt denn das Clientscript auf den Zielrechner?

Na klar, ein Script auf nem fremden Rechner starten… Meint ihr, dass geht nicht? Mir fallen da einige Möglichkeiten ein:

  • eine Worddatei mit einem DDE-Autostart
  • ein Besuch einer manipulierten Website
  • mein BashBunny 🙂

Angriff mit dem BashBunny

Der BashBunny kann gleichzeitig USB-Stick und Tastatur sein. Damit lässt sich ein kleines Szenario aufbauen Der Angreifer kommt zu dir und bittet dich, eine Datei von seinem „USB-Stick“ auszudrucken. Hilfsbereit steckst du den Stick an deinen PC. Da zeigt der Angreifer auf deinen Drucker und fragt dich etwas zu den Verbrauchskosten. Du schaust auf deinen Drucker und überlegst kurz – ein kleiner Moment, in dem der Bashbunny eine Powershell startet und versteckt:

Danach läuft alles im Hintergrund. Während du nun die Datei ausdruckst

startet der BashBunny in der versteckten PowerShell das versteckte CC-Client-Script. Den Rest könnt ihr euch vorstellen, oder?

Und das ist echt easy zu programmieren. Man benötigt eine Payload.txt für den BashBunny mit diesem Inhalt:

Und das wars! 🙂

Was kann die ReversePowerShell?

Hauptfunktionen des Servers

Start-CCServer

Diese Funktion startet den Server und wartet auf eine eingehende Clientverbindung. Wenn diese aufgebaut ist, dann werden die Funktionen Create-QuickCommands und Show-CCState aufgerufen.

Während der Server auf die Verbindung wartet ist die CLI des Servers blockiert:

Create-QuickCommands

Das ist die Funktion, welche die Unterverzeichnisse Payloads und QuickCommands durchsucht und für jede gefundene Scriptdatei folgendes ausführt:

  • Auf dem Client wird eine Funktion mit dem Namen des Scriptes und einem Teil des Scriptinhaltes als Code erzeugt. Aus der Scriptdatei ‚Close-Connection.ps1‘ wird so z.B. die RemoteFunktion ‚Close-Connection‘
  • Auf dem Server wird eine Funktion mit dem Namensmuster ‚CC-ScriptNameOhneMinuszeichen‘ erstellt. Für die Scriptdatei ‚Close-Connection.ps1‘ wäre das dann ‚cc-CloseConnection‘. Diese Funktion bekommt die gleichen Parametersets wie die RemoteFunktion. So ist ein lokaler Aufruf recht einfach möglich, da die IntelliSense der PowerShell bei der Eingabe unterstützt. Beim Aufruf der lokalen Funktion wird die RemoteFunktion mit den gleichen Parametern aufgerufen.

So schaut des dann aus. Oben im Bild steht der Code aus der Scriptdatei. Dieser wurde in die RemoteFunktion übertragen. Und unten steht der Code der lokalen Funktion. Mit dieser wird die RemoteFunktion gestartet:

Es gibt noch eine Besonderheit. Auf die komme ich später noch mal zurück.

Show-CCState

Falls ich mich über den aktuellen Stand der Verbindung informieren möchte, dann hilft Show-CCState:

Send-CCMessage

Mit dieser Funktion wird die eigentliche TCP-Kommunikation geführt. Im Grunde übergebe ich an Send-CCMessage nur einen String. Dieser wird an den Client übertragen und dort als Befehl ausgeführt. Das Ergebnis überträgt der Client zurück zum Server und die Funktion Send-CCMessage gibt es als Text aus.

Die Funktion wird aber von mir nur indirekt im Hintergrund verwendet. Im Vordergrund arbeite ich viel lieber mit den dynamisch erzeugten lokalen Funktionen.

CC-Help

Jede Funktion kann eine Hilfe eingebettet bekommen. Mit CC-Help kann die Hilfe speziell für alle RePS-Funktionen angezeigt werden:

Enter-CCSession

Ähnlich wie das cmdlet Enter-PSSession kann ich mit Enter-CCSession eine Remote-CLI-Verbindung zum Client aufbauen. Alle lokal getippten Befehle werden in einer Endlosschleife an den Client übertragen:

Die Session kann mit dem command ‚Exit‘ beendet werden.

Stop-CCServer

Wenn die Verbindung unterbrochen wurde oder nicht mehr benötigt wird, dann kann dieser Befehl den TCP-Server herunterfahren.

Basisfunktionen des Servers

CC-ShowConsole & CC-HideConsole

Ihr dachtet doch nicht, dass dann beim Benutzer ein sichtbares PowerShell-Fenster mit allen gestarteten commands aufploppt, oder? Das Clientscript versteckt sich natürlich und hat auch sonst keine textbasierte Ausgabe. Ich habe aber für die Entwicklung eine Version mit genau diesen Merkmalen erstellt. So lässt es sich leichter testen.

Zu den Testfunktionen gehören cc-HideConsole und cc-ShowConsole. Diese zeigen oder verstecken die CLI auf dem Client.

cc-CloseConnection

Für einen sauberen Shutdown des Clients und des Servers kann nach dem Verbindungsaufbau diese Funktion verwendet werden. Der Server wird dabei seinen TCP-Service ebenso beenden.

cc-GatherSysInfo

Nach einer erfolgreichen Verbindung möchten wir uns auf dem Client ein wenig umsehen. Da hilft diese Funktion. Es kann dank IntelliSense auch nur ein Teil der Infos gezielt abgerufen werden:

Das wären dann mögliche Ergebnisse:

Die Funktion benötigt keine besonderen Rechte oder Erweiterungen. Alles kann mit Boardmitteln abgefragt werden. 🙂

cc-GatherSecurityChecks

Auf dieses kleine Script bin ich stolz. Wenn du mal nen Überblick über deine Schutzmechanismen brauchst – es ist so einfach:

Aber auch gezielte Informationen sind möglich. Und dank IntelliSense sehr leicht zu tippen:

Da kommen mir bestimmt noch mehr Ideen, die ich da modular mit einbauen kann… 🙂

cc-GatherPrivileges

Die PowerShell kann auf dem Ziel von einem Administrator oder von einem Benutzer gestartet sein. Für ein weiteres Vorgehen ist es wichtig zu wissen, welche Berechtigungsstufe unser Gegenüber hat:

Viele meiner Payloads verwenden die Funktion dynamisch, um vor ihrem Lauf die erforderlichen Rechte zu prüfen. Dazu genügt im Header des Scriptes diese Zeile:

Wird dann die Funktion im CC-Server gestartet, dann bekommt man ggf. diese Info und die Funktion wird auf dem Ziel NICHT ausgeführt:

So bleibt der Angreifer schön unter dem Radar. 🙂

cc-DownloadFile & cc-UploadFile

Diese beiden Befehle waren schon etwas schwieriger zu programmieren. Über die TCP-Verbindung stehen keine Up- und Download-Prozeduren bereit wie bei SMB oder HTTP. Daher habe ich Folgendes ermöglicht:

  1. Nach der Auswahl der Datei wird diese mit einer in der PowerShell bekannten Prozedur gezippt. Dabei entsteht eine Bytefolge.
  2. Diese Bytefolge wird via TCPStream auf das Ziel übertragen und dort mit gzip wieder in einen FileStream entpackt.
  3. Der FileStream wird in eine Datei geschrieben.

Das ist die Kompression und die Übertragung auf dem Sender:

Und das hier der Empfang und das Speichern der Datei:

Damit ist ein Upload echt easy:

Und auch ein Download ist einfach:

cc-RestartAsAdmin

Ein User mit administrativen Rechten hat das Script ggf. nur als User ausgeführt. Viele interessante Payloads funktionierten aber nur mit Adminrechten. Mit der Funktion cc-RestartAsAdmin können wir versuchen, höhere Rechte zu bekommen.

In meinem Beispiel ist Tessa ein administrativer Benutzer des Clients – aber der CC-Client läuft ohne diese Rechte:

Meine Funktion kennt aktuell 2 Möglichkeiten:

  • frag den Benutzer nach administrativen Rechten
  • den FodHelperExploit:

Beim Aufruf wird versucht, eine administrative PowerShell mit dem gleichen Scriptcode des CC-Clients zu starten. Gelingt dies, dann wird der CC-Server seine Verbindung neu aufbauen. Und beim FodHelper-Exploit kommt noch nicht einmal die Benutzerkontensteuerung (UAC) ums Eck:

Und noch einmal zur Klarheit: auf dem Client wird kein Fenster sichtbar sein. Diese blende ich nur zur Visualisierung ein. 🙂

cc-StayPersistent

Was passiert, wenn sich der Benutzer auf dem Client abmeldet oder den PC neustartet? Dann ist natürlich auch die PowerShell beendet und die Verbindung ist verloren. Wenn ich als Angreifer beabsichtige, später wiederzukommen, dann wäre es zweckmäßig, dass mein CC-Client-Script immer wieder gestartet wird. Das habe ich mit meiner Funktion cc-StayPersistent geschafft. Diese erstellt aktuell nur eine geplante Aufgabe, kann aber um weitere Mechanismen erweitert werden:

Je nach Recht des Benutzers wird entweder eine Aufgabe beim Logon des Benutzers ODER eine Aufgabe beim Systemstart mit Systemrechten erstellt. Über den Mode kann diese Aufgabe auch gelöscht werden. Hier im Beispiel sieht man die Aufgabe erstellt als Admin – die Aufgabe ist auch nicht leicht zu finden 🙂

Damit hat man also immer einen Fuß in der Türe… 🙂

Welche Schutzmechanismen gibt es?

Das ist nun die spannende Frage! Wie verhindert man eine solche Attacke?

Antiviren-Scanner

Eine Prävention über nen Antiviren-Scanner schließe ich aus, denn der Code des CC-Clients selber ist vollkommen harmlos. Die „interessanten“ Funktionen bekommt er ja erst nach der Verbindung zum CC-Server 🙂 Und die hat nicht einmal mein Kaspersky erkannt.

Der AV wird hier wenig bis gar nichts erreichen!

Windows Firewall

Eine Firewall wäre ein probates Mittel, da die ReverseShell zwingend eine Netzwerkverbindung benötigt – natürlich schafft das nur eine ausgehend filternde Firewall! Und die ist leider nicht per Default aktiv. Sie kann aber am Client (auch sehr gut mit Gruppenrichtlinien) konfiguriert werden. Auch lokal ist das möglich:

Bitte beachtet aber dazu folgende Hinweise:

  • Die ausgehende Firewall benötigt unbedingt entsprechende Ausnahmen, damit der reibungslose Betrieb der Clients im Netzwerk sichergestellt ist.
  • Ebenso ist eine entsprechende Protokollierung sehr sinnvoll. Diese sollte je nach Verbindungsaufkommen vielleicht ein paar MB extra bekommen. Dann kann eine unerwünschte Verbindung leichter beobachtet werden:
  • Eine Clientfirewall ist Out-of-the-Box kein Intrusion-Detection-System. Blockierungen unerwünschter Verbindungen führen nicht zwangsweise zum Erkennen des Schadcodes! Die Firewall kann also nur als Teil der Lösung betrachtet werden.
  • Wenn der Angreifer sein Handwerk versteht, dann findet er eine Lücke in der Konfiguration. Hier z.B. verwende ich einfach einen Port, der bestimmt auf einem Enterprise-Client freigegeben ist – TCP 389 für LDAP:

    Hehe, da wäre eine detailliertere Regel (bestimmte Server mit Port 389) sinnvoller gewesen (als global den Port 389 freizugeben).

Netzwerk-Firewall

Aber auch eine Netzwerkfirewall ist denkbar. Diese kann die Clients von den Servern und vom Internet separieren. Für jede dieser so entstehenden Zonen werden Ausnahmen für die Kommunikation definiert. Ebenso kann hier auch eine zentrale Protokollierung von Zugriffsversuchen geschehen.

In meinem Netzwerk wird der gesamte Netzwerkverkehr durch eine PFSense geführt. Diese isoliert unter Anderem die Clients von den Servern und beide vom Internet:


In den Regeln der PFSense wird nun genau definiert, welcher Host mit welchem Protokoll auf welchen Service zugreifen darf:

Und wenn es doch wer versucht, dann wird das brav unterbunden und protokolliert:

PowerShell-Security

Es gibt natürlich eine ganze Reihe von Schutzmechanismen. Aber von denen berichte ich ein anderes Mal. 🙂

Fazit

DEN einen Schutz wird es nicht geben. Und wenn ihr meint, jetzt ist alles abgesichert – dann wartet einfach ne Woche und schaut noch einmal nach!

Mein kleines Beispiel hat euch hoffentlich gezeigt, wie leicht es sein kann. Und denkt daran: ich bin kein Programmierer! Jemand mit dem richtigen Background bekommt noch ganz andere Sachen zum Laufen!

Versucht einfach, ein gesundes Misstrauen gegen fremde Dateien und Hardware-Komponenten zu entwickeln, haltet euch auf dem Laufenden über neue Angriffsvektoren und lernt euer System kennen. So werden unnormale Verhaltensweisen schnell erkannt. Legt euch auch in einer ruhigen Minute einen Maßnahmenkatalog für den Fall der Angriffserkennung zurecht. Hier einige Ideen und Anhaltspunkte

  • Soll das betroffene System isoliert werden, damit eine Ausbreitung des Schadcodes oder ein Datenabfluss unterbrochen wird?
  • Soll das System weiterlaufen und der Traffic wird von außen überprüft, um ggf. die Herkunft der Attacke zu bestimmen?
  • Wie werden Logfiles vom System gesichert, bevor der Schadcode seine Spuren verwischt?
  • Ist eure Firewall und eure AV-Software in der Lage, den Schadcode zu erkennen? Ggf. könnte eine Kopie isoliert getestet werden.

Seid wachsam und führt nicht jeden Code da draußen auf.

PS: jetzt wo ich es tippe: hier (ReversePowerShell) könnt ihr auf eigene Gefahr meine ReversePowerShell in der aktuellen Konfiguration herunterladen. Denkt daran, dass dies nur für Testzwecke gedacht ist! Macht keinen Blödsinn! 🙂

Stay tuned!