Powershell-Skripts digital signieren

2. Dezember 2009

Wer Powershell-Skripts auf seinen Systemen verwendet, sollte Vorkehrungen treffen, dass keine unautorisierten Skripts auf den Rechnern agieren können. Um die Ausführung von Skripts zu kontrollieren und somit die Sicherheit der Systeme zu erhöhen, sind drei Schritte nötig: Die Ausführungsrichtlinie für die Powershell ist zu setzen, ein X.509-Zertifikat muss ausgestellt werden und die Skripts sind damit digital zu signieren.

Das Skript zu diesem Beitrag ist für Abonnenten verfügbar.

Viele Unternehmen haben früher das Ausführen von Skripts auf ihren Systemen komplett verboten und die entsprechende Funktionalität abgeschaltet. Mittlerweile aber gibt es andere Ansätze, um der Bedrohung durch unautorisierte Skripts, die auf welchen Wegen auch immer den Weg in die Unternehmens-IT gefunden haben, Herr zu werden.

Speziell bei der Powershell unter Windows sollte man sich zunächst Gedanken machen, wie sich die Ausführung von unautorisierten Skripts verhindern lässt. Die erste Vorgabe bei der Powershell schottet schon mal alle zugehörigen Probleme ab: Standardmäßig kann man Powershell-Skripting nicht auf den Systemen verwenden.

Allerdings stehen dem Administrator mit Hilfe der Powershell und den zugehörigen Skripten jede Menge an effektiven Werkzeugen zur Verfügung. Und wer die positiven Auswirkungen dieser Hilfen erst einmal genossen hat, der wird sie wohl nicht so schnell wieder hergeben wollen. Daher ergibt sich die Aufgabe, eine andere Möglichkeit zu finden, um Powershell-Skripts möglichst abzusichern.

Vom Prinzip her ist ein Powershell-Skript nichts anderes als eine Textdatei mit der Endung .ps1. Diese Datei enthält eine oder mehrere Powershell-Anweisungen, die zur Ausführung kommen, wenn man das Skript über die Konsole aufruft. Dabei lässt einem die Powershell kontrollieren, ob ein Skript laufen kann und wenn ja, welche Skripts laufen dürfen. Um diese Skript-Ausführung zu kontrollieren und so das System besser abzusichern, sind folgende Dinge zu machen:

  • Die Ausführungsrichtlinie für die Powershell ist zu setzen.
  • Ein X.509-Zertifikat ist zu erstellen.
  • Die Skripts sind digital zu signieren.

Sind diese Vorsichtsmaßnahmen gegeben, dürfen nur mehr die von einem selbst digital signierten Skripts in der Powershell laufen. Damit kann man bereits einen Großteil der Angriffe von bösen Jungs ausschließen.

Für den weiteren Verlauf des Beitrags wird vorausgesetzt, dass sich der Leser bereits mit der Powershell-Umgebung auskennt. Ansonsten gibt es im Web und speziell auf den Microsoft-Websites sehr viele Informationen zum Einstieg in die Powershell.

Das Setzen der Ausführungsrichtlinie

Die Ausführungsrichtlinie für die Powershell bestimmt, ob man Skripts auf einem System laufen lassen kann, und welche Konfigurationsdateien beim Start der Powershell geladen werden. Um diese Ausführungsrichtlinien zu setzen, ist das Cmdlet Set-ExecutionPolicy zu verwenden. Dabei stehen vier verschiedene Optionen bereit:

Restricted: Die Konfigurationsdateien für die Powershell werden dabei nicht geladen und somit können auch keine Powershell-Skripts laufen. Dabei handelt es sich um die restriktivste Option – und die ist standardmäßig auch eingestellt. Als Ergebnis werden nach dem ersten Installieren der Powershell keine ungewünschten Skripts laufen oder Konfigurationen geladen. In der Powershell-Konsole aber lassen sich trotzdem einzelne Powershell-Kommandos ausführen.

AllSigned: Alle Skripts und Konfigurationsdateien müssen digital signiert sein und zwar von einem vertrauenswürdigen Herausgeber. Um ein Skript zu signieren, muss der Administrator ein Zertifikat für das Signieren von Code verwenden. Wie im weiteren Verlauf des Beitrags gezeigt wird, kann man dieses Zertifikat auch selbst erzeugen.

RemoteSigned: Alle Skripts und Konfigurationsdateien, die aus dem Internet herunter geladen wurden, müssen digital signiert sein. Dabei ist es aber gestattet, dass Skripts auf dem lokalen System und lokale Konfigurationsdateien geladen werden dürfen, ohne dass man diese signiert haben müsste.

Unrestricted: Alle Skripts lassen sich ausführen und alle Konfigurationsdateien werden dabei geladen. Diese freizügigste Variante ist allerdings auch die gefährlichste.

Diese Aufstellung zeigt eindeutig, dass man sehr wohl seine Systeme schützen kann, und dennoch das Ablaufen von Skripts und das Laden von Konfigurationsdateien zulassen kann. Dazu muss man nur die Variante AllSigned selektieren. Um diese Richtlinie zu aktivieren, ist der folgende Befehl auf der Powershell-Konsole auf der Kommandozeile einzugeben:

Set-ExecutionPolicy AllSigned

Es ist immer eine gute Idee, nach dem Umstellen der Ausführungsrichtlinie für die Powershell sich die aktuelle Variante anzeigen zu lassen. Dieses Verifizieren ist mit dem Aufruf des Cmdlet Get-ExecutionPolicy zu machen – dabei darf man dem Cmdlet nur keinen Aufrufparameter mitgeben.

Weitere Details zu den beiden Cmdlets Set-ExecutionPolicy und Get-ExecutionPolicy sind in der Hilfedatei der Powershell – zu jedem Kommando – zu finden. Mehr Infos über die Konfigurationsdateien (auch als Profile bezeichnet) finden sich zum Beispiel im MSDN-Artikel zu den Windows Powershell Profiles .

Das Erzeugen eines X.509-Zertifikats

Nachdem die Ausführungsrichtlinie auf AllSigned steht, sind die betreffenden Dateien auch alle zu signieren. Dazu muss der Administrator ein X.509-Zertifikat für das Signieren von Code verwenden. Bei X.509 handelt es sich um einen Verschlüsselungsstandard, der das Format für entsprechende Sicherheits-relevante Geräte – wie etwa öffentliche Schlüsselzertifikate und Zertifikatssperrlisten festlegt.

Dazu kann ein Unternehmen entweder ein X.509-Zertifikat kaufen, das von einer öffentlichen Zertifikatsstelle ausgestellt wurde oder aber man erzeugt selbst eine eigene Zertifikatsstelle und ein entsprechendes Zertifikat. In diesem Beitrag wird das Thema X.509 nicht detaillierter vorgestellt. Doch es soll eine kurze Erklärung erfolgen, wie sich ein Administrator eine eigene lokale Zertifizierungsstelle erzeugen und ein lokales Zertifikat ausstellen kann.

Um diese Konstrukte auf einem lokalen System zu erzeugen, kann man auf die Utility Makecert zurückgreifen. Sie ist im Rahmen des Dotnet-Frameworks SDK von Microsoft zu bekommen. Das Tool gehört aber auch zum Lieferumfang von Visual Studie 2008 oder dem älteren Visual Studio 2005.

Es sei aber explizit darauf hingewiesen: Für den Einsatz in einer Produktivumgebung sollte man Makecert nicht verwenden. Es eignet sich nur für eine Testkonfiguration. In einer Produktivumgebung sollte man unbedingt eine komplette PKI (Public Key Infrastructure) betreiben, wie sie zum Beispiel bei den Zertifikatsdiensten von Microsoft gegeben ist. Und nur damit sollte man dann seine Zertifizierungsstellen und Zertifikate erzeugen.

Wie jede Kommandozeilen-Utility lässt sich Makecert von der Powershell-Kommandozeile aus starten. Ehe der Administrator ein Zertifikat erzeugen kann, muss er zunächst eine Zertifizierungsstelle anlegen. Dazu muss er an Makecert die passenden Optionen übergeben. Im folgenden Beispiel wird eine Zertifizierungsstelle namens „PowerShell CA“ im Stammverzeichnis des Zertifikatsspeichers angelegt:

makecert -n "CN=PowerShell CA" `
  -eku 1.3.6.1.5.5.7.3.3 -r `
  -sv PowerShellCA.pvk PowerShellCA.cer `
  -ss Root -a sha1

Dieses Kommando beinhaltet eine Reihe von Optionen. Die Tabelle 1 gibt dazu eine kurze Beschreibung.

Option    Beschreibung
-n           Name der Zertifizierungsstelle oder des Zertifikats
-eku       Objekt-Identifier für die erweiterte Schlüsselverwendung
-r            selbstsignierendes Zertifikat
-sv          private Schlüsseldatei und Zertifikatsdatei des Antragsstellers
-ss          Zertifikatspeicher
-a           Signatur-Algorithmus (das kann MD5 oder SHA1 sein)
-pe         erlaubt, dass der private Schlüssel exportiert werden kann
-iv           private Schlüsseldatei des Herausgebers (die Datei, die in der
               Zertifizierungsstelle angegeben ist)
-ic           Zertifikatsdatei des Herausgebers (die Datei, die in der
              Zertifizierungsstelle angegeben ist)

Detailliertere Informationen über diese (und andere) Optionen finden sich im Dotnet Framework Development Center auf MSDN.

Damit dieser Befehl aus dem Beispiel laufen kann, muss der Pfad, an dem sich die Utility Makecert befindet, in der Umgebungsvariablen mit dem Systempfad eingetragen sein.

Bild 1. Die Dialogbox zum Erzeugen eines Kennworts für den privaten Schlüssel.
Bild 2. In diese Dialogbox muss man das Kennwort für den privaten Schlüssel eingeben.
Bild 3. Die Messagebox mit der Sicherheitswarnung
Bild 4. Das Zertifikat lässt sich mit der MMC und dem zugehörigen Snap-in anzeigen.

Wenn man diesen Befehl absetzt, erschein die Dialogbox, die im Bild 1 gezeigt ist. Dabei geht es um das Erzeugen eines Kennworts für einen privaten Schlüssel. Hier ist das Kennwort einzugeben. Nachdem man dieses Kennwort zweimal eingegeben und dann mit Ok bestätigt hat, kommt eine weitere Dialogbox – sie ist im Bild 2 zu sehen. Hier muss man das zuvor eingegebene Kennwort eintragen. Dann erscheint eine Warnung – die Messagebox dazu ist in Bild 3 zu sehen.

Damit wird nochmals hingewiesen, dass nun eine Powershell CA Zertifizierungsstelle installiert wird. Mit einem Klick auf Yes erzeugt Makecert dann die Zertifizierungsstelle im lokalen Zertifikatsspeicher.

Ist diese Aktion abgeschlossen, geht es im nächsten Schritt um das Erzeugen des aktuellen Zertifikats. Das wird dann verwendet, um die Skripts zu signieren. Das folgende Kommando erzeugt ein Zertifikat mit der Bezeichnung „PowerShell Certificate“, das von der Powershell CA Zertifizierungsstelle autorisiert wurde:

makecert -n "CN=PowerShell Certificate" `
  -eku 1.3.6.1.5.5.7.3.3 -pe `
  -iv PowerShellCA.pvk `
  -ic PowerShellCA.cer -ss My -a sha1

Wie schon beim vorherigen Befehl beinhaltet auch dieses Kommando eine Reihe von Makecert-Optionen. In der Tabelle 1 sind auch die Erklärungen zu diesen Optionen zu sehen.

Besonders zu erwähnen ist hier die Angabe bei der Option -ss: Hier ist ein My anstelle von Root eingetragen – wie das beim Erzeugen der Zertifizierungsstelle erfolgt ist. Der Wert My besagt, dass das Zertifikat im Zertifikatsspeicher im persönlichen Ordner des aktuellen Benutzers abgelegt wird. Dieser aktuelle Benutzerspeicher wird standardmäßig verwendet. Man kann aber auch mit Hilfe der Option -sr LocalMachine den Befehl veranlassen, das Zertifikat im Zertifikatsspeicher des lokalen Computers abzulegen.

Wenn dann das Kommando Makecert läuft, um das Zertifikat zu erzeugen, muss der Anwender erneut sein Kennwort eingeben. Dabei handelt es sich aber im das Kennwort für den privaten Schlüssel, das man angegeben hatte, als die Zertifizierungsstelle erzeugt wurde. Das Zertifikat wird dann im „Vertrauenswürdigen Stammzertifizierungsstellen“-Speicher des aktuellen Nutzers erzeugt.

Dieses Zertifikat kann man sich dann mit Hilfe der MMC (Microsoft Management Console) und dem Zertifikat-Snap-in anzeigen lassen. Das verdeutlicht Bild 4. Hier ist darauf zu achten, dass das Powershell-Zertifikat in der rechten darstellungsfläche des MMC-Fensters liegt. Um sich die Details zu dem Zertifikat anzeigen zu lassen, ist ein doppelter Mausklick auf das Zertifikat nötig und dann öffnet sich die Dialogbox zu dem Zertifikat.

Falls das Snap-in für die Zertifikate nicht zur Verfügung steht, muss es der Administrator der MMC-Konsole hinzufügen. Wie das funktioniert, zeigt der Microsoft-Beitrag „How To Create Custom MMC Snap-in Tools Using Microsoft Management Console“.

Nachdem man auf diese Weise verifiziert hat, dass das Zertifikat erzeugt wurde, kann man mit dem Signieren der Skripts beginnen.

Listing der zu signierenden Datei; mit den Callouts A und B

Signieren eines Powershell-Skripts

Beim Signieren eines Skripts handelt es sich um einen relativ einfachen Vorgang. Mit Hilfe des Cmdlet Set-AuthenticodeSignature kann der Administrator diese Aufgabe meistern und muss dazu lediglich die zu signierende Skriptdatei und das Zertifikat, mit dem der Code signiert werden soll, angeben.

Angenommen der Systembetreuer möchte die Skriptdatei

C:\Audit\securityAudit.ps1

signieren – die im Bereich des Callout A im Listing 1 gezeigt ist. Diese Datei ist zum Download an diesen Artikel angehängt und steht für Abonnenten zur Verfügung. Dieses Skript holt die aktuellsten 20 Ereignisse aus dem Sicherheitsprotokoll.

Die folgenden Anweisungen geben zuerst die Skriptdatei und das Zertfikat an. Erst danach wird das Cmdlet Set-AuthenticodeSignature gestartet:

$file = "C:\Audit\SecurityAudit.ps1"
$cert = Get-ChildItem cert:\CurrentUser\My `
  -CodeSigningCert
Set-AuthenticodeSignature $file $cert

In der ersten Anweisung wird der komplette Dateiname als String an die Variable $file zugewiesen. Im zweiten Befehl kommt das Cmdlet Get-ChildItem zum Einsatz, um das für das Signieren des Codes nötige Zertifikat aus dem Zertifikatsspeicher zu holen und um es der Variablen $cert zuzuweisen. Um das Zertifikat zu holen, muss man den Pfad wie folgt angeben:

cert:\CurrentUser\My

Das Prefix cert: ist das Laufwerk, auf dem der Zertifikatsspeicher liegt. Darauf folgt „CurrentUser“, das auf den richtigen Ort im Zertifikatsspeicherverweist. Und das My bezieht sich auf die Zertifikate im persönlichen Ordner.

Wenn man das Cmdlet Get-ChildItem für das Abholen des Zertifikats einsetzt, sollte man auch noch den Parameter -CodeSigningCert mit übergeben und so nur die Zertifikate holen, die über die Code-Signierungs-Authorität verfügen.
Wenn im Zertifikatsspeicher My mehr als nur ein Zertifikat mit dieser Eigenschaft liegt, dann wird die Variable $cert all diese Zertifikate enthalten. In diesem Fall muss man das gewünschte Zertifikat angeben, wenn man sich auf die Variable $cert bezieht.

Eine Möglichkeit dazu ist das Hinzufügen des Objektindex nach dem Variablennamen. Dabei verwendet man $cert[0], um das erste Code-Signierungs-Zertifikat aufzurufen, $cert[1] für das zweite und so weiter.  Wenn man aber sicher weiß, dass es nur ein Zertifikat mit dieser Eigenschaft gibt, braucht man die eckigen Klammern nicht bemühen.

Nachdem die Werte für die Variablen $file und $cert gesetzt sind, kann man zum Signieren des Codes übergehen. Die dritte Anweisung im Beispiel verwendet dazu das Cmdlet Set-AuthenticodeSignature mit den beiden Variablen ($file und $cert) als den zwei Argumenten für dieses Cmdlet.

Wird das Cmdlet dann gestartet, erfolgt mit Hilfe des angegebenen Zertifikats das digitale Signieren der Datei. Der Administrator kann dann überprüfen, ob dieses Signieren erfolgreich war. Dazu braucht er nur di Inhalte der Datei ansehen. Am Ende der Datei wird man einen Block von kommentiertem Code sehen – das ist die digitale Signatur. Der Callout B im Listing 1 zeigt, wie diese Signatur aussehen könnte. Nachdem die Datei so signiert wurde, kann man das Skript ausführen.

In diesem Fall muss man angeben, ob das Skript sicher ist und erst dann wird es ausgeführt. Dabei stehen einige Optionen zur Wahl: Der Administrator kann entscheiden, dass das Skript niemals ausgeführt werden darf, dass es dieses mal nicht ausgeführt werden soll, dass es nur einmal ausgeführt werden darf oder dass es immer laufen darf.

Darf die Datei niemals oder immer ausgeführt werden, wird man später nicht mehr um eine Eingabe gebeten. Ansonsten wird auch bei den folgenden Starts des Skripts wieder nachgefragt, was passieren soll.

Einsatz einer pfx-Datei für das Signieren eines Skripts

Kommt ein privates Zertifikat für das Signieren der Dateien zum Einsatz, ist es immer noch für ein böswilliges Programm möglich, das Zertifikat zu nutzen, um das Skript zu signieren. Damit würde dann auch ein unerwünschtes Skript starten können. Um dieses Problem zu lösen und um noch mehr Schutz für die Systeme zu ermöglichen, kann man das Zertifikat für das Signieren des Codes in eine .pfx-Datei exportieren. Dann kann der Administrator diese Datei verwenden, um seine Skripts zu signieren.

Um das Zertifikat zu exportieren, muss er das Zertifikat-Snap-in öffnen und zum Zertifikat gehen (wie das auch im Bild 4 zu sehen ist). Mit einem rechten Mausklick auf das Zertifikat, mit dem der Code signiert werden soll, ist dann auf „Alle Aufgaben“ (All Tasks) und dann auf Exportieren (Export) im Kontextmenü zu gehen.

Danach startet der entsprechende Zertifikatsexport-Assistent (Certificate Export Wizard). Um die Datei zu exportieren, muss man nur den Schritten im Assistentenfolgen. Dabei ist sicherzustellen, dass auch der private Schlüssel zusammen mit dem Zertifikat exportiert und dass man dabei einen hohen Schutz haben will.

Es lässt sich zudem angeben, ob alle Zertifikate im Zertifizierungspfad einbezogen werden sollen, ob der private Schlüssel gelöscht werden soll und ob die erweiterten Eigenschaften exportiert werden sollen. Zudem muss der Administrator noch ein Kennwort und den Ort der Datei angeben. Für dieses Beispiel wurde die Datei an

C:\Audit\PS_Cert.pfx

gespeichert.

Nachdem das Zertifikat so exportiert wurde, darf man nicht vergessen, es im Zertifikatsspeicher zu löschen und man muss die .pfx-Datei an einem sicheren Platz aufbewahren. Nachdem der Assistent gelaufen ist, kann man die Datei signieren. Wie schon zuvor definieren die ersten beiden Anweisungen die notwendigen Variablen wie etwa:

$file = "C:\Audit\\SecurityAudit.ps1"
$cert = Get-PfxCertificate C:\Audit\PS_Cert.pfx

Im ersten Befehl wird der Ort der Skriptdatei an die Variable $file zugewiesen. Dann kommt das Cmdlet Get-PfxCertificate zum Einsatz, um die .pfx-Datei zu holen und um sie in der Variablen $cert abzulegen.

Wenn der Administrator die zweite Anweisung startet, muss er wieder ein Kennwort eingeben. Dabei handelt es sich um das Kennwort, das er angegeben hat, als er das Zertifikat in die Datei exportiert hat. Wie auch vorher kann er dann mit dem Cmdlet Set-AuthenticodeSignature das Signieren der Datei erledigen. Wenn er das Cmdlet startet, muss er das Skript und die .pfx-Dateien wie folgt angeben:

Set-AuthenticodeSignature $file $cert

Damit ist dann die Datei digital signiert. Wie sich leicht erkennen lässt, ist es eine recht einfache Sache, wenn man erst einmal das Zertifikat erstellt und es dann – optional – in die .pfx-Datei exportiert hat. Doch damit wird das betreffende System viel sicherer. Trotzdem darf man sich nicht auf diese Vorkehrungen allein verlassen – der Administrator kann gar nicht sorgfältig genug sein, wenn es um das Schützen der eigenen Powershell-Skripts geht.

Robert Sheldon/Rainer Huttenloher

Lesen Sie auch