Umsetzung mit Powershell

Um möglichst schnell eine Lösung zu schaffen, habe ich mittels Powershell ein Skript gebaut, welches auf dem Server (oder einem PC) mit entsprechenden Berechtigungen ausgeführt werden muss. Es gibt drei Parameter:

Parameter Default Beschreibung

basedir

c:\temp\cryptoprotect

Legt fest, ab welchem Verzeichnispfad soll der Schutz angewendet werden. Wer hier mehrere Freigaben hat, muss das Skript eben mehrfach mit dem jeweils passenden Pfad starten.

Blockgroup

NAWNBFC\CryptoProtect

Geben Sie die Gruppe an, die ein explizites "DENY:WRITE" bekommt. Die Gruppe müssen Sie vorher anlegen. Es kann problemlos eine lokale Gruppe sein. Im ersten Schritt muss die gruppe nicht mal gefüllt sein um das Skript zu starten. Sie können dann einfach mal schauen, ob die Rechte wie erwartet gegeben wurden. Um die Logik dann "scharf" zu machen, müssen Sie einfach z.B. die Benutzer addieren, die nicht mehr diese Dateien ansprechen dürften. Das könnten im Extremfall dann auch "Domänenbenutzer" sein.

idledays

Version 1.1: 60 Tage
Version 1.0: 30 Tage

Wie viele Tage soll die Datei nicht mehr angefasst worden sein, ehe die Gruppe mit "DENY WRITE" addiert wird. Das ist quasi die Karenzzeit, ehe eine Datei gegen Veränderungen geschützt wird

Eine Kurzfassung des Code sehen Sie hier:

param (
   [string]$basedir = "C:\temp\cryptoprotect",
   [string]$blockgroup = "nawnbfc\cryptoprotect",
   [int]$fileage = 60
)

Write-Host "Prepare Deny Entry"
$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None 
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None 
$objType =[System.Security.AccessControl.AccessControlType]::Deny
$colRights = [System.Security.AccessControl.FileSystemRights]"Write, Delete" 
$group = New-Object System.Security.Principal.NTAccount($blockgroup)

$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `
   ($group, $colRights, $InheritanceFlag, $PropagationFlag, $objType) 


foreach ($file in $(Get-ChildItem $StartingDir -recurse)) {
   $acl=get-acl $file.FullName
   $acl.AddAccessRule($rule)
   set-acl $File.Fullname $acl
}

Das Skript selbst ist natürlich etwas umfangreicher mit entsprechenden Bildschirmausgaben, überspringt Verzeichnisse, zählt die Dateien, addiert das Recht nur, wenn es noch nicht gesetzt ist und schreibt ein Protokollfile.

Und es gab noch einen anderen "Stolperstein": Das Comandlet "Get-ACL" holt sich die komplette ACL. Diese enthält aber nicht nur die Berechtigungen, sondern auch z.B. den "Owner"

Unfortunately Get-ACL is missing some features. It always reads the full security descriptor even if you just want to modify the DACL. That’s why Set-ACL also wants to write the owner even if you have not changed it. Using the GetAccessControl method allows you to specify what part of the security descriptor you want to read”
Quelle: http://bilalaslam.com/2010/12/15/powershell-workaround-for-the-security-identifier-is-not-allowed-to-be-the/ 

Die Folge davon ist, dass es mit einem Set-ACL auch versucht, den Owner zu setzen. Das möchte ich zum Einen nicht und zum Anderen kommt eine unschöne Fehlermeldung "The security identifier is not allowed to be the owner of this object". Die Lösung ist aber ganz einfach, indem ich auf "Get-ACL" verzichte:

#Alt
$acl = Get-Acl $filefullname

#Neu
$acl = $file.GetAccessControl("Access")

So enthält die ACL nur die Zugriffsrechte und ein Set-ACL schreibt auch nur diese Rechte.

Aber beim Set-ACL ist mir dann auch noch das ein oder andere Problem aufgefallen. Ich habe einmal bei allen Dateien den ACE (Access Control Entry) addieren lassen und dennoch meinte das Skript bei einem Wiederholungslauf noch Dateien zu finden. Durch den Zeitverzug mag das denkbar sein, aber bei jedem lauf waren es fast gleich viel. Über das Logfile konnte ich dann sehen, dass tatsächlich die Dateien nicht mit Set-ACL behandelt wurden. Auffällig wäre hier nur der Dateiname

Office 365%20Vorstellung%20-%20Frank%20Carius`[1`].ppt

Vermutlich haben die verschiedenen Sonderzeichen eine Ersetzung in Powershell bewirkt. Es gab aber auch keinen Fehler beim Set-ACL. Statt aber nun den Dateinamen aufwändig zu behandeln, habe ich wie bei Get-ACL auch das Set-ACL durch die Methode $file.SetAcccessContol ersetzt.

# Alt
#set-acl -path $Fullname -aclobject $acl  # Update File

#Neu
$_.SetAccessControl($acl)

Die Owner sind dabei natürlich ebenfalls unverändert geblieben, selbst wenn ich beim Lesen der ACL das Attribut "Access" nicht mit angegeben habe.

Laufzeit

Ich bin eigentlich erst davon ausgegangen, dass ein Update von Berechtigungen etc. eine ziemlich aufwändige Sache ist. Wenn das Skript aber lokal auf dem Server läuft und die Gruppe schnell aufzulösen ist, dann scheint die Vergabe von Berechtigungen auch bei Massenänderungen sehr einfach zu sein. Meine Daten waren:

Anzahl Dateien Geänderte ACLs Dauer Dateien/Sek Bemerkung

3000

3000

0,5 min

100

Zuerst habe ich die Änderungen gegen einen Teilbaum laufen lassen.

274129

274129

50 min

91,3

Dann gegen das komplette Verzeichnis

274129

0

35 min

130

Beim zweiten Lauf gibt es nichts zu ändern und daher ist das Skript schneller durch.

Ein Durchsatz von um die 100 Dateien/Sek ist stattlich und kann durchaus akzeptiert werden. Allerdings ergibt dies bei dieser Größe schon eine beachtliche Laufzeit. Wobei das Skript vielleicht noch etwas schneller wäre, wenn die Debug-Ausgaben auf dem Bildschirm unterbleiben würden.

Aufruf per Taskplaner

Es ist nicht immer einfach direkt im Taskplaner ein Powershell-Script samt Parameter korrekt zu starten. Ich habe mir daher angewöhnt, per Taskplaner einfach nur eine CMD-Datei zu starten, die dann den oder die eigentlichen Powershell-Skripte starten. So sieht meine CMD aus.

@echo off
echo starting CryptoProtect

powershell.exe -noninteractive -file C:\Tasks\cryptprotect\cryptprotect.ps1 -basepath D:\group\

Version

Das Skript hat mehrere Versionen durchlaufen:

Datum Version Beschreibung

Vorher

0.x

Eine ganze Menge von Versionen, mit denen ich mich dem Thema genähert habe. Zuerst einmal zur Auswertung, dann wie man Rechte setzen kann und erste Tests auf ausgewählten Verzeichnissen.

29. Feb 2016

1.0

Initiale Version, die primär erst einmal das DENY gesetzt hat.

16. Mrz 2016

1.1

  • Default Alter von 30 Tagen auf 60 Tage geändert
  • Einbau einer "ExcludeList" um bestimmte Extensions (z.B. *.ONE) auszuschließen
  • Umbau des Logging
    Die For-Schleife sendet nun kein Objekt mehr per Pipeline an "export-csv". Das scheint nicht immer funktioniert zu haben. Nun schreibe ich selbst direkt eine CSV-Datei, aber noch nicht auf Performance optimiert per Out-File -append. Da ich aber nur "Änderungen" schreibe, ist da zu verschmerzen.
  • Transcript
    Alle Ausgaben auf dem Bildschirm werden in ein Transcript geschrieben. Das erlaubt später eine einfachere Analyse bei Fehlern. Bedenken sie aber, das diese Files recht groß werden können. Bei ca. 300 Bytes/Datei werden bei 300.000 Dateien schon mal 100MByte geschrieben.
  • Policy-Logik
    Die Version 1.1 hat einfach Dateien nach einem Alter "geschützt" und danach nie mehr angerührt. Die Version 1.1 bestimmt zuerst den Schutzstatus, den die Datei haben sollte (Anhand Alter und Exclusion) um dann die Berechtigungsgruppe ggfls., zu addieren aber auch wieder zu entfernen. Wer also als "IdleTime" z.B. 9999 eingibt, kann Das Deny-Recht von allen Dateien bis 1988 wieder entfernen.

Download und Einsatz

Sind sie bereit das Skript einzusetzen ?. Hier ist der Download

Die aktuelle Version 1.1:  cryptprotect.1.1.zip

Ältere Version: cryptprotect.1.0.zip

Es gibt keine Installationsroutine. Hier sind sie als Administrator gefragt.

  1. Verzeichnis anlegen
    Ich lege meine Skripte je nach Anwendungsfall immer in ein eigenes Verzeichnis, z.B. "C:\Tasks\cryptoprotect"
  2. DENY-Gruppe anlegen
    Legen Sie dann eine lokale oder Domänen-Gruppe an, die in die ACLs mit dem DENY addiert wird.
    Achtung: Version 1.1 kann auch Rechte entfernen. Benutzer Sie bitte UNBEDINGT eine eigene Gruppe für dieses Skript und nicht z.B. "Domänen Benutzer"
  3. Download der beiden Dateien und Entpacken nach "C:\Tasks\cryptoprotect"
  4. Anpassen der Dateien
    In der CMD-Datei müssen Sie unbedingt den BasePath anpassen. Wenn Sie die anderen Parameter für die DENY-Gruppe, Tage etc. nicht mit angeben, dann müssen Sie diese im Powershell-Script als Defaults ändern
  5. Taskplaner
    Das erste Mal würde ich das Skript interaktiv auf einen kleinen Teilzweig starten um die Funktion zu verifizieren. Selbst den ersten Lauf über alle Dateien würde ich interaktiv laufen lassen und dann die CMD als "Geplanter Task" mit den entsprechenden Berechtigungen laufen lassen-
  6. Mitglieder der Gruppe pflegen
    Damit die neuen ACLs letztlich aber aktiv werden, müssen Sie nun natürlich entsprechende Benutzer oder Benutzergruppen in diese DENY-Gruppe addieren.

Damit sollte das Skript regelmäßig nach "neuen ausreichend alten" Dateien Ausschau halten und diese mit der zusätzlichen ACL versehen. Alle Aktivitäten werden dabei in ein Logfile geschrieben, was das aktuelle Datum hat. Ältere Logfiles werde NICHT gelöscht. Hier müssen Sie also ggfls. selbst mal aufräumen. Per Default werden aber nur die Einträge geschrieben, die sich geändert haben. Nach dem ersten Durchlauf sollten die Logs also deutlich kleiner ausfallen.

Aktuell sendet das Skript keine Eventlog-Einträge oder Mails im Fehlerfall und generiert auch keinen Error-Code, der vom Taskplaner ausgewertet werden könnte. Das plane ich für später.

Ergebnis

Nach der Änderung der Rechte und Erweitern der Gruppenmitgliedern ist es den Mitarbeitern nicht mehr möglich, schädlich auf die Datei zuzugreifen. Das gilt auf jeden Fall für Dateiserver, auf denen die Freigabe nur mit dem Recht "Lesen/Ändern" freigegeben wurde. Wenn ein Schadcode von einem Anwender gestartet wird, der "Administrator" ist und über C$ o.ä. auf die Dateien kommt oder der Datenshare mit "Vollzugriff" vergeben ist, dann ist das ein konzeptionelles Problem, was angegangen werden muss. Bei korrekter Planung sind folgende Aktionen nicht mehr möglich.

  • Ändern der Datei
    Das war mein primäres Ziel: So kann auch kein Trojaner eine Datei verändern und damit verschlüsseln.
  • Löschen
    Damit ein Trojaner eine Datei nicht einfach neu anlegt, verschlüsselt und die alte Datei löscht, darf die Datei auch nicht gelöscht werden
  • Move
    Ein netter aber aus meiner Sicht positiver Nebeneffekt ist nun auch, dass die Dateien nicht mehr "verschoben" werden können. Das ist auf größeren Dateifreigaben durchaus ein Thema, wenn Anwender im Explorer per "Drag and Drop" ganze Verzeichnisse verschieben.

Diese Einschränkungen gelten natürlich nur für die Dateien, die länger als die 60 Tage (Default) nicht mehr verändert und durch das Skript geschützt wurden.

Undo

Ich habe in dem Skript auch eine "Undo-Funktion" eingebaut. Über die "Exclude-Listen" kann man Pfade und Dateien ausnehmen. Das Skript ist dabei so angelegt, dass es diese Pfade dennoch verarbeitet und nicht einfach überspringt. Es macht dann die Einträge auch wieder rückgängig.

Wer jedoch manuell den ein oder anderen Eintrag entfernen will, kann das weiterhin per Windows Explorer machen. Für mehrere Dateien in einem Verzeichnis kann das auch per CACLs sehr einfach erfolgen:

CACLS *.* /e /r msxfaq\sgg-cryptoprotect

Weitere Links