Archive for the ‘Security’ Category

Systemrechte unter Windows erlangen

17 August 2017

Schon seit Ewigkeiten ist bekannt wie man Systemrechte unter Windows erlangen kann. Aber das Ziel ist es – wie immer – es mit möglichst wenig Aufwand und am besten mit Bordmitteln zu erlangen. Zwar bringt jedes aktuelle Windows mittlerweile ein Recoverypartition mit aber man hat nicht immer unbedingt Zugriff im Bootvorgang darauf (z. B. bei gehosteten Webservern).

Der übliche Vorgang ist das Kopieren von CMD.EXE auf UTILMAN.EXE, OSK.EXE oder SETHC.EXE. Dies gelingt normalerweise nur, wenn man von einer Recoverypartition oder von WinPE bzw. eben WinRE-CD gebootet hat. Aus diesem Grund verwenden viele Sysinternals PSExec: https://docs.microsoft.com/en-us/sysinternals/downloads/psexec.

Mit Adminrechten und etwas Gefummel in der Registry, bekommt man es auch mit Bordmitteln hin. Da es komfortabler ist dies per Script zu erledigen hier die nötigen Powershell-Anweisungen. Zuerst kopieren wir Utilman.exe und cmd.exe:

copy "C:\Windows\System32\utilman.exe" "C:\Windows\System32\utilman.exe.bak"
copy "C:\Windows\System32\cmd.exe" "C:\Windows\System32\cmd.exe.bak"

Nun benötigen wir den Zugriff auf den Registryschlüssel HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\ um dort die Werte PeningFileRenameOperations und AllowProtectedRenames zu setzen.

$smreg = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\"

AllowProtectedRenames ist wichtig, sonst klappt der Vorgang nicht. Dies ist mal wieder einer von so vielen Registrywerten die nicht klar dokumentiert sind. Er wird im MSDN nur einmal im Zusammenhang mit VSS und der Wiederherstellung von Dateien der Windows File Protection genannt: https://msdn.microsoft.com/en-us/library/windows/desktop/aa381498(v=vs.85).aspx.

set-ItemProperty -Path $smreg -Type DWORD  -Name AllowProtectedRenames -Value 1

Jetzt muss man die zu kopierenden Dateien angeben. Da es aber kein klassischer Copy-Befehl sondern ein Move-Befehl ist, wo die Quelldatei nach dem Vorgang weg ist, verwenden wir die vorher erstellte Kopie von CMD.EXE. Dabei ist darauf zu achten, dass vor den Dateinamen noch eine spezielle Notation \??\ nötig ist. Leider gibt es keine Informationen darüber wann !\??! verwendet wird. Aber damit hat es immer funktioniert. Wichtig ist noch, dass es ein String-Array sein muss, damit die Werte in der Registry korrekt als REG_Multi_Sz gespeichert wird.

[string[]]$moveFiles= @("\??\C:\Windows\System32\cmd.exe.bak", "!\??\C:\Windows\System32\Utilman.exe")
set-ItemProperty -Path $smreg -Type MultiString -Name PendingFileRenameOperations -Value $moveFiles

Nun muss nur noch der Rechner neu gestartet werden und schon steht einem eine Eingabeaufforderung mit Systemrechten zur Verfügung. Bei Verwendung von UTILMAN.EXE klickt man am Anmeldebildschirm einfach auf den Kreis mit den zwei Pfeilen und der Einblendung “Erleichterte Bedienung”.

Um die Änderung rückgängig zu machen, könnte man nun dieses Script verwenden, welche einfach die Kopie von Utilman.exe zurückkopiert:

$smreg = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\"
set-ItemProperty -Path $smreg -Type DWORD  -Name AllowProtectedRenames -Value 1
[string[]]$moveFiles= @("\??\C:\Windows\System32\Utilman.exe.bak", "!\??\C:\Windows\System32\Utilman.exe")
set-ItemProperty -Path $smreg -Type MultiString -Name PendingFileRenameOperations -Value $moveFiles

Es geht aber noch einfacher, auch im Falle eines Fehlers zwischendrin, bemüht man einfach die Windows File Protection und beauftragt diese mit der Wiederherstellung der betreffenden Dateien:

# Falls was schiefgeht:
SFC /SCANFILE=C:\Windows\System32\cmd.exe
SFC /SCANFILE=C:\Windows\System32\utilman.exe

Jetzt nochmal das ganze Script um oben beschriebene Funktionalität zu erreichen:

copy "C:\Windows\System32\utilman.exe" "C:\Windows\System32\utilman.exe.bak"
copy "C:\Windows\System32\cmd.exe" "C:\Windows\System32\cmd.exe.bak"
$smreg = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\"
set-ItemProperty -Path $smreg -Type DWORD  -Name AllowProtectedRenames -Value 1
[string[]]$moveFiles= @("\??\C:\Windows\System32\cmd.exe.bak", "!\??\C:\Windows\System32\Utilman.exe")
set-ItemProperty -Path $smreg -Type MultiString -Name PendingFileRenameOperations -Value $moveFiles

Hier noch ein guter Blogartikel mit Registrybildern, wer es von Hand machen möchte: https://blog.cscholz.io/windows-72008-movefileex/. Eine weitere Methode anstatt PendingFileRenameOperations:
https://guyrleech.wordpress.com/2014/07/16/reasons-for-reboots-part-2-2/, einfachere Beschreibung: https://superuser.com/questions/1204878/how-can-i-tell-windows-to-overwrite-a-system-file-on-the-next-reboot.

Eine weitere Variante an Systemrechte zu gelangen ist hier beschrieben: https://newyear2006.wordpress.com/2013/01/01/eingabeaufforderung-mit-lokalen-systemdienst-rechten-unter-windows-8-und-windows-server-2012/.

Werbeanzeigen

Software für Hardware Token Provider auf Rechner über Remote Desktop zu installieren, ist keine gute Idee

3 März 2016

Wir befinden uns im Jahr 2016. Im Jahr 2006 erblickte Windows Vista die Welt und brachte damals für die Windowswelt nach Windows XP größere Veränderungen mit. Aber selbst heute bekommt man noch Auswirkungen von damals zu spüren, durch unsachgemäße Fehlermeldungen und faule Programmierer!

Aktuelles Beispiel, es sollte der SafeNet Authentication Client Version 9.0 installiert werden, damit ein Zertifikat auf einem USB eToken gespeichert werden kann.

Es wurden mehrere Rechner probiert jeweils mit Windows 7 SP1 und Windows 8.1 mit allen aktuellen Updates, auch 64-Bit und 32-Bit war dabei. Am Ende wurde auch die Installation nur in Englisch durchgeführt, wäre nicht das erste Mal, dass es nur so funktioniert. Aber jedes Mal kam es zu der Fehlermeldung:

There appears to be a problem with the Smart Card Resource Manager configuration on this computer. \nThe installation will be completed, but may not work correctly.\nPlease contact your System Administrator to solve the problem.

Wie die Meldung schreibt funktioniert danach die Sache einfach nicht.

Der Support verwies nur auf dieses Dokument und den Admin: https://kb.safenet-inc.com/kb/index?page=content&id=S438&actp=LIST_POPULAR

Wenn man sich die Log-Dateien anschaut, dann taucht in der eTCoreInst.LOG, welche sich im %TEMP%-Verzeichnis befindet dieser Eintrag auf:

(ThID:2360): Err in RMTable::snapshot, SCardEstablishContext fails, rv=0x8010001d
(ThID:2360): RMTable::snapshot: exit , rv=0x8010001d
(ThID:2360): Err in RMTable::listReaders, snapshot() fails, rv=0x8010001d
(ThID:2360): RMTable::listReaders: exit , rv=0x8010001d
(ThID:2360): Trace in snapshotScardDevice, Cannot list readers

Recherchiert man damit etwas, stolpert man über diesen Artikel: https://blogs.msdn.microsoft.com/alejacma/2011/05/19/scardestablishcontext-fails-with-scard_e_no_service-error/

Da steht dann etwas von:

SCardEstablishContext API is returning that error because it gets an Access Denied error when trying toopen an event called “Global\Microsoft Smart Card Resource Manager Started” with OpenEvent API. The default security for that event on Vista and Windows 7 specifies that only SYSTEM, LOCAL SERVICE and INTERACTIVE users have access to it. NETWORK SERVICE or non-interactive users won’t be able to access the event.

OK, dann ist alles klar. Das ist ein aus XP-Zeiten geerbtes Problem. D. h. man muss physisch angemeldet sein, damit es funktioniert.

Alle obigen Versuche, wo es nicht klappte, wurden per Remote Desktop durchgeführt! Siehe da, einmal physisch am Rechner und schon funktionierte es!!

Gilt für alle, die Zertifikate von DigiSafe, GlobalSign, TrustZone, Entrust und wie sie alle heißen benutzen, welche die Hardware von Safenet einsetzen. Daneben gibt es auch noch Banken…

Noch ein interessanter Blogeintrag, wie man ein normales Zertifikat auf den USB Token bekommt kann: https://blogs.msdn.microsoft.com/ieinternals/2015/01/28/authenticode-in-2015/.

Powershell 9 Arten ein externes Programm zu starten, mir sind aber 11 bekannt oder noch mehr?

7 Januar 2013

Dieser Artikel http://www.admin-source.de/BlogDeu/433/powershell-9-arten-ein-externes-programm-executable-zu-starten, dessen Grundlage, dieser Technet-Wiki-Eintrag ist http://social.technet.microsoft.com/wiki/contents/articles/7703.powershell-running-executables.aspx, spricht von 9 Arten externe Programme unter Powershell starten zu können.

Allerdings muss man bei Version 2 von Powershell sagen, es gibt eigentlich 10 Arten, denn es gibt das tolle Kommando Invoke-WSManAction http://technet.microsoft.com/en-us/library/hh849865.aspx. Mit den richtigen Parametern gefüttert:

Invoke-WSManAction -Action create -ResourceURI wmicimv2/win32_process -ValueSet @{commandline="notepad.exe";currentdirectory="C:\"}

führt dies auch zum Start von Notepad.exe!

Jetzt mag der eine oder andere einwenden, dass ja nur Win32_Process aufgerufen wird aber wird nicht auch bei den anderen Möglichkeiten im Hintergrund immer nur CreateProcess() aufgerufen?

Ach übrigens mit Powershell 3 gibt es noch eine weitere Variante! Die da wäre Invoke-CIMAction http://technet.microsoft.com/en-us/library/jj590759.aspx. Dies sind dann so aus:

$cim=Invoke-CimMethod -ClassName Win32_process -MethodName "Create" -Arguments @{ Commandline="notepad.exe";CurrentDirectory="C:\" }

Wenn ich allerdings jetzt nochmal genau darüber nachdenke, dann fallen mir noch weitere Möglichkeiten ein, wie z. B. ScheduledJobs, BackgroundJobs also Jobs allgemein, mittels Win32_ScheduleJob oder indem man einen Druckertreiber mit entsprechendem Monitorprogramm installiert und dann was ausdruckt. Dann wäre da noch die Möglichkeit über Win32_Service. Und es gibt noch soviel mehr, wenn man seine Kreativität walten lässt.

Man könnte ja auch Powershell benutzen, um mehr zu erfahren

gwmi -List | where if ($_.methods -ne $null) {($_.methods).name -contains "Create"}

listet unter anderem

Win32_Process
Win32_BaseService
Win32_Service
Win32_TerminalService
Win32_SystemDriver
Win32_ScheduledJob

Bei CIM wären die Invoke-Aktionen aber CIM ist noch so undurchschaubar. Gute, wenn auch alte Infos über CIM gibt es hier: http://klaus.jaehne.de/papers/cim/node6.html.

Ich glaub jetzt bin ich irgendwie übers Ziel rausgeschossen. Halten wir also fest: Es gibt mehr Methoden, als wir zu wissen glauben.

Wifi Netzwerksicherheitsschlüssel unter Windows per Powershell auslesen

2 Januar 2013

Es gibt unter Windows zwar wohl eine Methode sich den hinterlegten Netzwerksicherheitsschlüssel für Wifi Netzwerke anzuzeigen aber die Methode ist nur über die GUI-Oberfläche möglich und ist recht umständlich, wenn man die Keys mehrerer Netze abfragen möchte.

Es gibt natürlich auch allerhand Utilities wie z. B. WirelessKeyview von Nirsoft http://www.nirsoft.net/utils/wireless_key.html. Aber wer vertraut per solch sicherheitskritischen Dingen schon gern unbekannter Software. Zumal diese oft von Virenscannern als Malware klassifiziert werden: http://blog.nirsoft.net/2012/10/10/amazing-difference-between-antivirus-false-alerts-on-32-bit-and-64-bit-builds-of-exactly-the-same-tool/.

Was liegt also näher, als die Sache selber mit Bordmitteln zu lösen. Das Thema ist allerdings etwas komplexer weshalb ich als Vorbereitung in den vergangenen Tagen bereits verschiedene Artikel geschrieben habe.

Zuerst werden die Berechtigungen vom Systemdienst benötigt: https://newyear2006.wordpress.com/2013/01/01/eingabeaufforderung-mit-lokalen-systemdienst-rechten-unter-windows-8-und-windows-server-2012/.

Dann ist wichtig, wie man mit SecureStrings in Powershell umgeht: https://newyear2006.wordpress.com/2012/12/30/spa-mit-net-securestring-und-powershell-oder-sicheres-speichern-und-einlesen-von-passwrtern/

Noch etwas mehr Hintergrund, wie man in den tiefen von SecureString die DPAPI benutzt: https://newyear2006.wordpress.com/2012/12/30/in-den-niederungen-von-securestring-mittels-powershell/

Dann braucht man noch die Idee für solch einen Artikel: http://securityxploded.com/wifi-password-secrets.php

So nun zum Script, am besten speichert man es unter WifiUtil.PS1:

# Warmup
ConvertTo-SecureString -AsPlainText -Force Hallo| ConvertFrom-SecureString

function Convert-HexStringToByteArray {
# http://www.sans.org/windows-security/2010/02/11/powershell-byte-array-hex-convert
################################################################
#.Synopsis
# Convert a string of hex data into a System.Byte[] array. An
# array is always returned, even if it contains only one byte.
#.Parameter String
# A string containing hex data in any of a variety of formats,
# including strings like the following, with or without extra
# tabs, spaces, quotes or other non-hex characters:
# 0x41,0x42,0x43,0x44
# \x41\x42\x43\x44
# 41-42-43-44
# 41424344
# The string can be piped into the function too.
################################################################
[CmdletBinding()]
Param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True)] [String] $String )
#Clean out whitespaces and any other non-hex crud.
$String = $String.ToLower() -replace ‚[^a-f0-9\\\,x\-\:]‘,“

#Try to put into canonical colon-delimited format.
$String = $String -replace ‚0x|\\x|\-|,‘,‘:‘

#Remove beginning and ending colons, and other detritus.
$String = $String -replace ‚^:+|:+$|x|\\‘,“

#Maybe there’s nothing left over to convert…
if ($String.Length -eq 0) { ,@() ; return }

#Split string with or without colon delimiters.
if ($String.Length -eq 1)
{ ,@([System.Convert]::ToByte($String,16)) }
elseif (($String.Length % 2 -eq 0) -and ($String.IndexOf(":") -eq -1))
{ ,@($String -split ‚([a-f0-9]{2})‘ | foreach-object { if ($_) {[System.Convert]::ToByte($_,16)}}) }
elseif ($String.IndexOf(":") -ne -1)
{ ,@($String -split ‚:+‘ | foreach-object {[System.Convert]::ToByte($_,16)}) }
else
{ ,@() }
#The strange ",@(…)" syntax is needed to force the output into an
#array even if there is only one element in the output (or none).
}

function Get-WLANKeyFromProfile {
[CmdletBinding()]
param(
  [xml]$x)

if ($x -ne $null)
{
  $key=$x.WLANProfile.MSM.security.sharedKey.keyMaterial
         $eb=Convert-HexStringToByteArray $key
  $mbl=[System.Security.Cryptography.ProtectedData]::Unprotect($eb,$null,"LocalMachine")
  $key=[System.Text.Encoding]::ASCII.GetString($mbl)
  $key.Substring(0,$key.Length-1) # 0x0

}
}

function Get-SSIDFromProfile {
[CmdletBinding()]
param(
  [xml]$x)

if ($x -ne $null)
{
  $key=$x.WLANProfile.SSIDConfig.SSID.name
  $key
}
}

# C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces

$wifi=Get-ChildItem -Recurse|Select-String -pattern "01000000D08C9DDF0115D1118C7A00C04FC297EB"
$wifi | Add-Member -Type ScriptProperty –Name SSID -Value { Get-SSIDFromProfile $this.XMLProfileFile }
$wifi | Add-Member -Type ScriptProperty -Name WlanKey -Value { Get-WLANKeyFromProfile $this.XMLProfileFile }
$wifi | Add-Member -Type ScriptProperty -Name XMLProfileFile -Value { [xml](Get-Content $this.path) }

Wenn man auf dem Desktop des Systemdienst ist, geht man in das Verzeichnis

CD C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces

und ruft dort Powershell auf und importiert obiges Script mittels

Import-Module WifiUtil.PS1

Man hat nun in $Wifi alle auf dem System bekannten Wifi-Keys mit Angaben zu den SSIDs. Mittels Powershell 3.0 und Out-Gridview gibt es die perfekte Darstellung:

$wifi | Out-GridView

oder man verwendet die klassische Methode:

$wifi | select –Property SSID,WlanKey

So das war aber erst der Anfang mit dieser Methode lässt sich noch viel mehr anstellen.

Eingabeaufforderung mit lokalen Systemdienst-Rechten unter Windows 8 und Windows Server 2012

1 Januar 2013

Gerade erst gebloggt und doch schon nicht mehr aktuell. Blöd sowas aber wer konnte damit rechnen, dass Microsoft eine bestehende Funktionen die seit Ewigkeiten da ist, degradiert. Es geht um diesen Artikel: https://newyear2006.wordpress.com/2012/12/29/eingabeaufforderung-mit-lokalen-systemdienst-rechten-unter-windows/

Ausgehend von

sc create ntauthcmd binpath= "cmd /K start" type= own type= interact

und mittels

sc start ntauthcmd

konnte bisher eine Kommandozeile mit lokalen Systemdienstrechten gestartet werden und mehr oder weniger einfach darauf zugegriffen werden.

Dies funktioniert unter Windows 8 und Windows Server 2012 nicht mehr! Zumindest von Haus aus.

Wenn man unter diesen Systemen den Dienst startet, wird kein Icon in der Taskleiste angezeigt. Die Frage ist generell ob die Methode überhaupt noch funktioniert?

Mittels

TASKLIST /FI "IMAGENAME eq cmd.exe"

bekommt man jedoch schnell Klarheit. Denn mit jedem Aufruf von sc start ntauthcmd wird ein weiterer cmd.exe Eintrag hinzugefügt. Auch taucht die cmd.exe unter Services und mit Nummer 0 auf, also scheint dies noch perfekt zu funktionieren. Auch der Taskmanager führt die CMD.EXEs als Hintergrundprozesse.

Aber wie kommt man nun in die 0er Session?

Auf der Suche nach der Lösung hatte ich mir natürlich den Dienst UI0Detect, auch bekannt als “Erkennung interaktiver Dienste” oder in englisch “Interactive Service Detection”, angeschaut. Zunächst dachte ich, die Lösung wäre ganz einfach, denn dieser Dienst ist unter Windows 8 einfach auf manuellen Start eingestellt.

Mittels

sc qc UI0Detect

bekommt man die passenden Infos über den Dienst, hier die Ausgabe bei Windows 7

[SC] QueryServiceConfig ERFOLG

SERVICE_NAME: UI0Detect
        TYPE               : 110  WIN32_OWN_PROCESS (interactive)
        START_TYPE         : 3   DEMAND_START
        ERROR_CONTROL      : 1   NORMAL
        BINARY_PATH_NAME   : C:\Windows\system32\UI0Detect.exe
        LOAD_ORDER_GROUP   :
        TAG                : 0
        DISPLAY_NAME       : Erkennung interaktiver Dienste
        DEPENDENCIES       :
        SERVICE_START_NAME : LocalSystem

Was soll das? DEMAND_START ist Starttyp Manuell in der GUI! Also kein Unterschied, dennoch läuft das Ding auf Windows 7 automatisch aber bei Windows 8 nicht. Wer es startet? Keine Ahnung.

Aber man kann ja den Dienst einfach mal selber starten. Was scheren einen schon unzählige Programme die leider immer noch nicht sauber mit der Trennung von Diensten und UI umgehen können. Also

sc start UI0Detect

abgesetzt und dies erhalten

SERVICE_NAME: ui0detect
        TYPE               : 110  WIN32_OWN_PROCESS  (interactive)
        STATE              : 4  RUNNING
                                (STOPPABLE, PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0
        PID                : 6804
        FLAGS              :

Na also sieht doch gut aus, blöd nur dass

sq query ui0detect

dies zurückgibt

SERVICE_NAME: ui0detect
        TYPE               : 110  WIN32_OWN_PROCESS  (interactive)
        STATE              : 1 STOPPED
        WIN32_EXIT_CODE    : 1  (0x1)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

Verdammt, es könnte alles so einfach sein. Unter der GUI heißt die offizielle Meldung:

Der Dienst "Erkennung interaktiver Dienste" auf "Lokaler Computer" konnte nicht gestartet werden.

Fehler 1: Unzulässige Funktion.

Supi und jetzt?

Die Suchmaschine meines Vertrauens förderte gleich beim ersten Eintrag einen brauchbaren Link hervor, nachdem man sie mit den richtigen Parametern gefüttert hatte. https://www.google.de/search?q="interactive+service+detection"+"error+1"

Der Link ist http://www.coretechnologies.com/WindowsServices/FAQ.html#UI0DetectFailsToStart und beschreibt genau obiges Problem. Dabei wird ein Registrierungseintrag beschrieben  HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows mit Namen NoInteractiveServices beschrieben.

Also diesen mal kurz abgefragt mittels

reg query HKLM\SYSTEM\CurrentControlSet\Control\Windows /v N
oInteractiveServices

ergibt unter Windows 7

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows
    NoInteractiveServices    REG_DWORD    0x0

und unter Windows 8:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows
    NoInteractiveServices    REG_DWORD    0x1

Also tatsächlich ein Unterschied. Also diesen Wert mittels

reg add HKLM\SYSTEM\CurrentControlSet\Control\Windows /v N
oInteractiveServices /t REG_DWORD /d 0

geändert und nochmal den Dienst gestartet und flutsch, alles funktioniert wieder so wie gewohnt!

So das war mal wieder viel Geschreibsel für nichts. Aber die entscheidenden Zeilen unter Windows 8 sind:

sc create ntauthcmd binpath= "cmd /K start" type= own type= interact

reg add HKLM\SYSTEM\CurrentControlSet\Control\Windows /v NoInteractiveServices /t REG_DWORD /d 0

sc start ntauthcmd

Dabei ist anzumerken, dass die Änderung an der Registrierung nur einmalig gemacht werden muss. Danach reagiert Windows 8 wie Windows 7 und reagiert auf künftige sc start ntauthcmd automatisch.

Auf der Suche nach der Lösung bin ich noch über diese Blogeinträge gestolpert, welche einem das Thema mit UI0Detect und mögliche Problemlösungen nahebringen: http://blogs.msdn.com/b/patricka/archive/2010/04/27/what-is-interactive-services-detection-and-why-is-it-blinking-at-me.aspx und http://blogs.msdn.com/b/patricka/archive/2011/03/14/troubleshooting-interactive-services-detection.aspx. Wenn ich mir dabei die Kommentare so anschaue, dann scheint das immer noch aktuell ein Thema zu sein, dabei ist die Umstellung, wo mit Vista kam, jetzt schon so alt. Vielleicht hat MS die Funktion doch zu früh deaktiviert.

In den Niederungen von SecureString mittels Powershell

30 Dezember 2012

Im ersten Blogbeitrag zu SecureString bin ich auf die allgemeine Anwendung von SecureStrings in Powershell und indirekt im .Net-Framework eingegangen. https://newyear2006.wordpress.com/2012/12/30/spa-mit-net-securestring-und-powershell-oder-sicheres-speichern-und-einlesen-von-passwrtern/

Wenn man mit den SecureStrings herumspielt, fällt einem schnell auf, dass diese quasi immer einen gleichen Header verwenden. Dieser beginnt immer mit der folgenden Zeichenfolge:

01000000d08c9ddf0115d1118c7a00c04fc297eb

Ab hier wird es nun interessant. Wenn man Google bemüht, kommen nicht all zu viele Treffer bei raus: https://www.google.de/search?q=01000000d08c9ddf0115d1118c7a00c04fc297eb. Aber unter den wenigen Treffern, sind wahre Perlen dabei! Denn es geht um nichts anderes als um eine eindeutige Kennung für DPAPI BLOBs welche von Windows seit Windows 2000 verwendet werden um Passwörter sicher in der Registrierung, Festplatte und Speicher abzulegen. DPAPI steht für Data Protection API. http://msdn.microsoft.com/en-us/library/ms995355.aspx.

Bleiben wir zunächst bei Powershell. Michal Grzegorzewski hat auf seiner Seite die Einträge mittels Debugger auseinandergenommen, hier die englische Übersetzung mittels Google: http://translate.googleusercontent.com/translate_c?depth=1&hl=de&prev=_dd&rurl=translate.google.de&sl=auto&tl=en&u=http://zine.net.pl/blogs/mgrzeg/archive/2011/03/11/dpapi-internals-a-securestring-w-powershellu.aspx&usg=ALkJrhhOdJ83W5rHYAcEnuML-v-KZ1KqNw. Dabei hat er festgestellt, dass Powershell die Methode Protect aus der ProtectedData Klasse aus dem .Net-Framework verwendet. http://msdn.microsoft.com/de-de/library/system.security.cryptography.protecteddata.protect.aspx. Protect verwendet wiederum CryptProtectData aus Crypt32.dll. http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261(v=vs.110).aspx. Ein Punkt der bei Powershells ConvertFrom-SecureString und ConvertTo-SecureString zu kurz kommt, ist die Sache dass es sich um benutzerbezogene Verschlüsselungen handelt. D. h. ein Passwort welches mittels ConvertFrom-SecureString als verschlüsselter Standardstring konvertiert und gespeichert wurde, kann nur von dem selben Benutzer auf dem ursprünglichen Rechner wieder gelesen und verwendet werden! Um die Sache dann noch weiter zu komplizieren, sei noch darauf hingewiesen, dass das verwendete Passwort des Benutzers auch noch eine Rolle spielt, was wiederum beim Zurücksetzten von Passwörtern wichtig werden kann: http://support.microsoft.com/kb/309408. Zu welchen Probleme das benutzerbezogene Speichern führen kann, schildert schön dieser Artikel: http://blog.codeassassin.com/2012/05/17/powershell-remoting-user-profiles-delegation-and-dpapi/.

Im zweiten Bereich seines ersten Blogeintrags unter “DAPI Internals” streift Grzegorzewski noch die Geschichte von Masterkeys und dass auf jedem Windows immer die vorhergehenden Keys gespeichert sein müssen, um auf ältere, verschlüsselte Daten zurückgreifen zu können. Thema CREDHIST. Aber das Thema lassen wir außen vor. Im zweiten Blog-Artikel geht er dann noch näher auf die Internas von DAPI Blobs ein http://translate.google.de/translate?hl=de&sl=auto&tl=en&prev=_dd&u=http%3A%2F%2Fzine.net.pl%2Fblogs%2Fmgrzeg%2Farchive%2F2011%2F03%2F30%2Fdpapi-internals-a-securestring-w-powershellu-cz-2.aspx. Dort nennt er Picod and Bursztein, welche das Tool libDPAPIck.dll, zu finden unter http://dpapick.com/ entwickelt haben. Das Tool erlaubt das Lesen von EFS-verschlüsselten Laufwerken von Linux oder anderen Offlinesystemen aus.

So nach dem kleinen Ausflug, wieder zurück zum Thema SecureStrings und Powershell. Im ersten Artikel https://newyear2006.wordpress.com/2012/12/30/spa-mit-net-securestring-und-powershell-oder-sicheres-speichern-und-einlesen-von-passwrtern/ wurde ja schon gezeigt, wie man ein Passwort wiederherstellen und sichtbar machen kann. Nun wollen wir die von Grzegorzewski verwendete Methode mittels Powershell auf den verschlüsselten Standardstring anwenden.

Wir haben also aus unserem ersten Artikel die Zeichenfolge

01000000d08c9ddf0115d1118c7a00c04fc297eb01000000d13e0b3adb59f6469325c229ee5b0d520000000002000000000003660000c0000000100
00000d57efc5b5ccd1e52e71296ec9b91a3530000000004800000a0000000100000006b946cd8b1c877db8c8ae6f39f4932bd10000000858fb31747
0ef57f405d954a5857409a14000000e2154da0885013dc5dbf21097c61c1d429f1e8ce

Dabei handelt es sich um einen Hexstring. Für den Aufruf von Unprotect http://msdn.microsoft.com/en-us/library/system.security.cryptography.protecteddata.unprotect wird als Parameter ein Bytearray benötigt. Dazu benötigen wir als erstes eine Hilfsfunktion, welche den verschlüsselten Standardstring in ein ByteArray umwandelt, diese gibt es hier: http://www.sans.org/windows-security/2010/02/11/powershell-byte-array-hex-convert und nennt sich Convert-HexStringToByteArray. Also los gehts:

# $es enthält obigen String
Import-Module .\HexHelper.PS1
$eb=Convert-HexStringToByteArray $es
$mb=[System.Security.Cryptography.ProtectedData]::Unprotect($eb,$null,"CurrentUser")
[System.Text.Encoding]::Unicode.GetString($mb)

Zurück bekommen wir dann das ursprüngliche “Hallo”.

Dabei gibt es noch ein paar Dinge zu beachten. Der Aufruf von Unprotect funktioniert erst, wenn davor ConvertFrom-SecureString bereits benutzt wurde! Falls dies nicht der Fall ist, bekommt man die Fehlermeldung

Der Typ [System.Security.Cryptography.ProtectedData] wurde nicht gefunden: Vergewissern Sie sich, dass die Assembly, die diesen Typ usw…

Zum Warmlaufen kann man diesen Befehl vorher verwenden:

Read-Host – AsSecureString | ConvertFrom-SecureString

Wie oben bereits geschrieben, die Strings sind immer benutzerbezogen, wenn man also den String auf einer anderen Maschine versucht zu verarbeiten, erhält man die Meldung:

Ausnahme beim Aufrufen von "Unprotect" mit 3 Argument(en):  “Schlüssel ist im angegebenen Status nicht gültig."

Spaß mit .Net SecureString und Powershell oder sicheres Speichern und Einlesen von Passwörtern

30 Dezember 2012

Wer sich per .Net dem Thema SecureString annähert wird sich irgendwann fragen: “Warum bekomm ich den hinterlegten String nicht einfach zurück?”. Tja die Sache liegt in der Natur des SecureString selber. Er soll sicher sein!

Fangen wir mal mit einer einfachen Geschichte an, man möchte ein Passwort sicher eingeben:

PS> $pw=Read-Host –AsSecureString
*****
PS>

Sieht schon mal gut aus. Meine Passworteingabe lautete Hallo und es wurden aber nur Sternchen angezeigt. Wer jetzt denkt, man bekommt bei Abfrage von $pw das Passwort der täuscht sich:

PS> $pw
System.Security.SecureString

OK, dann halt über ein Property?

PS> $pw | Get-Member

   TypeName: System.Security.SecureString

Name         MemberType Definition
—-         ———- ———-
AppendChar   Method     void AppendChar(char c)
Clear        Method     void Clear()
Copy         Method     securestring Copy()
Dispose      Method     void Dispose(), void IDisposable.Dispose()
Equals       Method     bool Equals(System.Object obj)
GetHashCode  Method     int GetHashCode()
GetType      Method     type GetType()
InsertAt     Method     void InsertAt(int index, char c)
IsReadOnly   Method     bool IsReadOnly()
MakeReadOnly Method     void MakeReadOnly()
RemoveAt     Method     void RemoveAt(int index)
SetAt        Method     void SetAt(int index, char c)
ToString     Method     string ToString()
Length       Property   int Length {get;}

Na supi! Nur Length ist als Property vorhanden. Na dann vielleicht ToString()?

PS> $pw.ToString()
System.Security.SecureString

Nene, das Ding soll ja sicher sein!

Übrigens was gibt Length zurück?

PS> $pw.Length
5

Das würde passen, “Hallo” hat genau fünf Buchstaben. Kurz gesagt, es gibt keine direkte Möglichkeit den String zurückzubekommen.

Wofür braucht man nun das Ding? Die aktuelle Beschreibung auf MSDN (V4.5), gibt keinen Tipp. http://msdn.microsoft.com/en-us/library/system.security.securestring.aspx

Also mal etwas recherchiert:

Sicher gibt es noch mehr Stellen, aber die kleine Auswahl sollte erst mal reichen.

OK, der Anwendungszweck gegeben, aber was kann ich damit anfangen? Vor allem im Zusammenhang mit Powershell? Ein erster Hinweis steht in der SecureString Doku auf MSDN:

Note that SecureString has no members that inspect, compare, or convert the value of a SecureString. The absence of such members helps protect the value of the instance from accidental or malicious exposure. Use appropriate members of the System.Runtime.InteropServices.Marshal class, such as the SecureStringToBSTR method, to manipulate the value of a SecureString object.

Aha, man muss also in die Niederungen der InteropServices absteigen. Dann legen wir gleich mal los:

PS> $BSTR=[System.Runtime.InteropServices.marshal]::SecureStringToBSTR($pw)
PS> $BSTR
39018744
PS> $pws = [System.Runtime.InteropServices.marshal]::PtrToStringAuto($BSTR)
PS> $pws
Hallo
PS> [System.Runtime.InteropServices.marshal]::ZeroFreeBSTR($BSTR)

Hier findet nun die ganze Show statt. Zuerst holt man sich mittels SecureStringToBSTR die ursprüngliche Eingabe und einen Pointer auf die Stelle, wo der String nun temporär abgelegt wird. Bei PtrToStringAuto wird der temporäre String ausgelesen, zurückgegeben und in $pws gespeichert. Mit ZeroFreeBSTR wird der temporäre String wieder freigegeben, bzw der temporäre Speicherplatz mit Nullen überschrieben (Secure!). Blöd nur, dass wir das Passwort nun in $pws haben, denn ab nun ist die Sache nicht mehr sicher!!

Hier noch der Link zu SecureStringToBSTR: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.securestringtobstr.aspx

Damit schließt sich der Kreis. Aber Moment, da geht noch mehr.

Passwörter in Dateien speichern
Es gibt nämlich die tollen Funktionen ConvertTo-SecureString und ConvertFrom-SecureString. Wer glaubt, damit ließe sich obige Prozedur abkürzen, der täuscht sich. Denn diese dienen nur dazu den SecureString zu serialisieren bzw. in einen Standard-String zu wandeln, um ihn z. B. in einer Datei speichern zu können.

Machen wir genau dies und speichern unseren String mal in einer Datei, dazu wandeln wir ihn zuerst, lassen ihn ausgeben und prüfen ob es ein String ist:

PS> $pwe = ConvertFrom-SecureString –SecureString $pw
PS> $pwe
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000d13e0b3adb59f6469325c229ee5b0d520000000002000000000003660000c0000000100
00000d57efc5b5ccd1e52e71296ec9b91a3530000000004800000a0000000100000006b946cd8b1c877db8c8ae6f39f4932bd10000000858fb31747
0ef57f405d954a5857409a14000000e2154da0885013dc5dbf21097c61c1d429f1e8ce
PS> $pwe.GetType()

IsPublic IsSerial Name                                     BaseType
——– ——– —-                                     ——–
True     True     String                                   System.Object

PS> Set-Content –Value $pwe –Path pwenc.txt
PS> type .\pwenc.txt
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000d13e0b3adb59f6469325c229ee5b0d520000000002000000000003660000c0000000100
00000d57efc5b5ccd1e52e71296ec9b91a3530000000004800000a0000000100000006b946cd8b1c877db8c8ae6f39f4932bd10000000858fb31747
0ef57f405d954a5857409a14000000e2154da0885013dc5dbf21097c61c1d429f1e8ce

Perfekt. Damit hat man eine saubere Möglichkeit gefunden Passwörter sicher abzuspeichern. Um sicher zu gehen, dass dem so ist, muss man die Sache natürlich auch wieder rückwärts machen können.

SecureString-Passwörter aus Dateien auslesen
Die Geschichte ist einfach erzählt

PS> $pw=Get-Content pwenc.txt | ConvertTo-SecureString

Damit kann man $pw wieder bei SecureStringToBSTR, wie oben gezeigt anwenden und bekommt das gespeicherte Passwort im Klartext zurück.