MSXFAQ MeetNow aktiv: Komm doch einfach dazu.

PowerShell, UAC und Exchange

Für einige Aktionen benötigen Skripte lokale Admin-Berechtigungen, die aber dank User Account Control (UAC) nicht immer vorhanden sind. UAC ist wichtig, um Systeme zu schützen aber stört wieder Prozesse, die erhöhte Rechte brauchen.

Auslöser: Exchange Management Shell auf DC

Wie wissen sicher seit dem Artikel Recipient Management PowerShell und Exchange Management Rolle, dass Sie mit einer lokalen Exchange PowerShell ganz ohne Exchange Server und RBAC die Exchange Eigenschaften im lokalen Active Directory verwalten können. Über diesen Weg konnten Firmen seit April 2022 die Exchange Online Eigenschaften über das lokale AD ohne lokalen Exchange Server verwalten. Sie installieren die Exchange Management Shell und laden einfach das Recipient Management Shell Snapin mit:

Add-PSSnapin Microsoft.Exchange.Management.PowerShell.RecipientManagement

Bei einem Projekt mit einem Provisioning-Service ist aber aufgefallen, das ich als Administrator problemlos ein "Enable-RemoteMailbox" ausführen konnte aber das Dienstkonto dabei einen Fehler generiert hat.

Ich tippe mal darauf, dass die Exchange Powershell beim Weglassen der "PrimarySmtpAddress" versucht hat, selbst eine gültige Adresse zu ermitteln und dazu die "inproxy.dll" dynamisch nachladen will und das als "nicht lokaler Admin" nicht möglich ist. Diese DLL nutzt Exchange OnPremises, um anhand der Empfängerrichtlinien eine primäre SMTP-Adresse zu vergeben.

Es hilft übrigens nicht, das Feld "mail" mit einer primären Adresse zu versehen. Siehe dazu auch Provisioning mit IsExchangeCloudManaged und ADSync mit Exchange Online Only

Den Fehler haben wir schon früher mit Exchange 2010 gehabt, wo diese DLL wohl mal verloren gegangen ist. Aber in meinem Beispiel gab es gar keinen Server, sondern nur die Exchange Management Rolle

Je nach Umgebung bekommen Sie vielleicht och andere Fehler, wie:

#Englische Fehlermeldung
Enable-RemoteMailbox: No provisioning handler is installed.

#Deutsche Fehlermeldung
Enable-RemoteMailbox: kein bereitstellungshandler installiert

Sobald ich aber die PowerShell als "Administrator" gestartet habe, waren die Fehler weg.

Die Befehle "Set-RemoteMailbox" und haben ohne Fehler funktioniert.

Damit war meine Neugier geweckt, wie ich denn z.B. aus einem Provisioning-Skript gezielt eine AdminPowerShell starten kann und wann Windows überhaupt die Unterscheidung macht.

UAC und Admin PowerShell

Ich habe erst mal eine Testserie gemacht, bei der verschiedene Benutzer sich auf einem Windows Server 2016 lokal angemeldet und eine PowerShell gestartet haben. UAC war wie folgt eingestellt.

Wenn ich etwas tue, wozu administrative Berechtigungen erforderlich sind, dann kommt eine Rückfrage. zumindest habe ich das gedacht.

User

Lokale Rechte

Shell als Admin

Dom/Administrator (SID endet auf 500)

Administratoren

Ja ohne Rückfrage!

./Administrator (SID endet auf 500)

Administratoren

Ja ohne Rückfrage!

Dom/Admin (SID endet nicht auf 500)

Administratoren

"Als Admin Starten" und Rückfrage

./Admin (SID endet nicht auf 500)

Administratoren

"Als Admin Starten" und Rückfrage

./User (SID

Benutzer

"Als Admin Starten" + Credentials

Domain/User

Benutzer über DomainUser

"Als Admin Starten" + Credentials

Das Verhalten war mir vorher gar nicht so aufgefallen. Vermutlich liegt es allein daran, dass ich die "BuildIn"-Administratoren, sei es lokal oder in der Domain eigentlich nie nutze.

Als Buildin-Admin habe ich auf die Schnelle auch keinen Weg gefunden, eine PowerShell oder CMD-Shell ohne Adminrechte zu starten. Das geht wohl nicht.

Ich interpretiere die Ergebnisse derart, dass die "Default Administratoren" mit der SID selbst bei aktiviertem UAC keine Rückfrage bekommen, sondern immer direkt eine PowerShell oder CMD-Shell als Administrator starten. Hier wirkt UAC also gar nicht. Nur Konten, die auch in der direkt oder über eine Domaingruppe in der lokalen Gruppe der Administratoren Mitglied sind und sich damit Admin-Rechte geben könnten, werden durch UAC entsprechend kurz darauf aufmerksam gemacht, dass ein Prozess hier privilegierte Berechtigungen anfordert. Benutzer, die keine Admin-Rechte haben, können natürlich auch ein "als Administrator starten" versuchen aber werden dann nach Anmeldedaten gefragt.

Ist ihnen das auch schon aufgefallen, dass UAC im Standard für Buildin-Administratoren nicht wirkt?

Das Exchange Problem war damit gelöst aber ich habe noch ein paar Informationen zu UAC gesammelt.

UAC Status ermitteln

Ob auf einem Windows Computer der Schutz per UAC aktiv ist, kann ich per PowerShell einfach durch Lesen des entsprechenden Registrierungsschlüssels ermitteln.

(Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System") `
| fl ConsentPromptBehaviorAdmin,ConsentPromptBehaviorUser,PromptOnSecureDesktop,EnableLUA

Die Werte je nach Einstellung:

Einstellung

ConsentPromptBehaviorAdmin

ConsentPromptBehaviorUser

PromptOnSecureDesktop

EnableLUA

Immer benachrichtigen (App Installation und Windows Setting)

2

3

1

1

Benachrichten bei App Installation (Default)

5

3

1

1

Benachrichten bei App Installation (Desktop nicht ausblenden

5

3

0

1

Nicht benachrichtigen

0

3

0

1

Die Benachrichtigung kommt natürlich immer nur beim Start des Prozesses aber nie während der Prozess schon läuft. Das Betriebssystem überwacht also nicht alle Aktionen des Programms während der Laufzeit sondern nur, wenn ein neuer Prozess gestartet wird.

Habe ich Admin Rechte?

Ein laufendes PowerShell-Script kann sehr einfach prüfen, ob es mir administrativen Berechtigungen gestartet wurde. Wir holen uns in der PowerShell direkt den aktuellen Benutzer und prüfen die Rolle:

$ich = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
$ich.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

 

Ein einfaches "True" zeigt an, dass dieses Skript zumindest lokaler Administrator ist.

Eine Abfrage mit "RunAs /ShowTrustLevels" hat bei mir immer nur Folgendes geliefert. Das galt selbst für den "Default Domain Administrator" auf dem Domain Controller in einer AdminPowershell

Ich könnte ja ein Programm mit "RunAs" und dem Parameter "/trustlevel" privilegiert starten. Aber wenn ich einen Benutzer mit angebe, möchte er acuh immer ein Kennwort.

UAC für Skripte anfordern

Ich möchte UAC als wichtige Schutzfunktion auf Servern ungern abschalten aber ich möchte genauso wenig meine Skripte und Code als "Buildin DomainAdmin" oder "Buildin LocalAdmin" starten. Ein eigenes Dienstkonto ist OnPremises immer noch eine gute Idee und das Konto kann ja gerne in der lokalen Gruppe der Administratoren sein, wenn der Prozess auf dem Server dies benötigt. Aber dann muss ich mir überlegen, wie ich den Prozess oder das Skript direkt mit Admin-Berechtigungen starten kann. Mit der Maus ein "Run as Admin" kann ein anderer Code ja nicht anklicken. Aber es gibt Optionen.

  • LNK-Datei
    Ich muss garnicht direkt die EXE oder das Script starten. Ich kann genauso eine LNK-Datei anlegen, welche auf den Code verweist und dort "Ausführen als Admin" angeben.

    Allerdings umgehen wir damit nicht das UAC-Prompt
  • Geplanter Task
    Wenn Sie ihr Skript im Taskplaner anlegen, dann können Sie schon beim Anlegen des Tasks ein "Run with highest privileges" auswählen

    Das Skript oder Programm startet dann dann tatsächlich mit vollen Admin-Rechten ohne UAC-Rückfrage
  • Windows Service
    Bei der dritten Variante nutzen sie einen Windows Service mit einem Dienstkonto. Hier können Sie zwar keine Checkbox für "Run as Admin" setzen, aber die benötigen Sie auch nicht. Beim Start als Dienst kommt nach meinen Versuchen keine UAC-Abfrage aber das Skript oder Programm hat auch Admin-Rechte.

Wenn ein Programm schon mal Adminrechte hat, kann es auch weitere Prozesse starten, die ebenfalls die Berechtigungen ohne weitere UAC-Abfrage vererbt bekommen.

Seit PowerShell 4 kann ich mit einer Direktive vorgeben, dass ich Administrator sein muss. Hier ein kleines Beispielskript:

#Requires -RunAsAdministrator

Write-Host "testadmin"

Der erste Aufruf erfolgt noch ohne "Requires" und läuft durch. Für den zweiten Aufruf habe ich das Skript entsprechend ergänz und wenn ich kein Admin bin, dann bricht das Skript sofort ab:

Das ist keine Lösung um UAC zu umgehen, sondern nur ein Weg, wie Sie dem aufrufenden Benutzer einfach darauf hinweisen, dass er das Skript als Admin starten muss.

Skript als Admin mit "RunAs" starten

Aber es gibt doch einen Weg, wie Sie aus einem PowerShell-Skript sich die Admin-Rechte beschaffen können. Wenn ich im Grund schon Administrator bin, aber UAC die Admin-Rechte nicht in meinem Session-Token hinterlegt habe, dann kann ich in der PowerShell einfach eine neue Shell starten

Start-Process PowerShell -Verb RunAs

Das dahinterliegende Fenster hat das vordere Fenster gestartet.

Sie können natürlich auch aus einer CMD-Shell direkt eine PowerShell als Admin starten.

Powershell.exe -command "Start-Process PowerShell" -Verb "RunAs"

In allen Fällen kommt aber der UAC-Dialog hoch. Es ist so nicht möglich, mal heimlich einen Prozess mit privilegierten Rechten zu starten. Aber sie können einen Administrator natürlich das Leben etwas einfacher machen, indem ihr Skript prüft, ob es schon die erforderlichen Rechte hat und dann sich selbst einfach noch einmal neu startet. Hier ein Auszug

$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object System.Security.Principal.WindowsPrincipal($identity)
if (! $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
   # Skript als Admin neu starten
   $scriptfile = Resolve-Path $Script:MyInvocation.MyCommand.Path
   $arguments = @("-NoProfile", "-NoLogo", "-ExecutionPolicy", "RemoteSigned", 
                  "-File", "$scriptfile")
   Start-Process pwsh -ArgumentList $arguments -Verb RunAs -Wait -PassThru
   #Aktuelles Skript beenden   
   Exit
}

UAC umgehen Sie damit aber nicht und denken Sie daran, dass Sie auch die Parameter neu mit übergeben müssen. Eine Verarbeitung über eine Pipeline gelingt so auch nicht.

Ich bin dann eher der Freund das Skript direkt zu beenden, damit der Administrator gleich eine PowerShell als Admin ausführt.

UAC mit __COMPAT_LAYER unterdrücken?

Bei der Recherche bin ich auf die Umgebungsvariable "__COMPAT_LAYER" gestoßen. Ich kann in einer Shell diese Variable setzen, so das nachfolgend gestartete Prozesse dann in diese im "Kompatibilitätsmode" ausgeführt werden. Mögliche Werte sind:

RUNASADMIN    Das Programm wird mit Administratorberechtigung und der UAC-Eingabeaufforderung ausgeführt.
RUNASINVOKER  Das Programm wird mit den Berechtigungen eines übergeordneten Prozesses ohne UAC-Eingabeaufforderung ausgeführt.
RUNASHIGHEST  Das Programm wird mit der höchsten Berechtigungsstufe des Benutzers ausgeführt (die UAC-Eingabeaufforderung wird angezeigt).

WIN8RTM       Kompatibilitätsmodus für Windows 8
WIN7RTM       Kompatibilitätsmodus für Windows 7
VISTARTM      Kompatibilitätsmodus für Windows Vista
VISTASP1      Kompatibilitätsmodus für Windows Vista (Service Pack 1)
VISTASP2      Kompatibilitätsmodus für Windows Vista (Service Pack 2)
WINXPSP3      Kompatibilitätsmodus für Windows XP (Service Pack 3)
WIN2000       Kompatibilitätsmodus für Windows 2000
NT4SP5        Kompatibilitätsmodus für Windows NT 4.0 (Service Pack 5)
WIN98         Kompatibilitätsmodus für Windows 98
WIN95         Kompatibilitätsmodus für Windows 95

256Color      Mit 256 Farben ausführen
640x480       Bildschirmauflösung 640x480
DisableThemes Visuelle Design deaktivieren

Mehrere Werte können durch Leerzeichen getrennt angegeben werden.

# Setzen des RunAsInvoker in PowerShell
[Environment]::SetEnvironmentVariable("__COMPAT_LAYER", "RunAsInvoker")
REM Setzen des RunAsInvoker in der Eingabeaufforderung
set __COMPAT_LAYER=RunAsInvoker

Bei EXE, COM und einigen anderen Dateierweiterungen können Sie diese Konfiguration direkt im Windows Explorer an der Datei vornehmen:

 

Aber auch hier kommt natürlich UCA zum Einsatz. Es hilft ihnen also nicht, dass ein Programm UAC-Dialoge umgehen kann.

Zusammenfassung

Wir kommen auf den ersten Abschnitt zurück, bei dem ein "Enable-RemoteMailbox" eine INPROXY-DLL nachladen wollte, was nicht ohne administrative Berechtigungen geht. In dem Fall gibt es mehrere Alternativen:

  • Enable-RemoteMailbox mit mehr Parametern ohne Admin-Rechte
    In meinem Fall habe ich einfach noch den Parameter "-PrimarySMTPAddress" addiert, so dass der Exchange Code keine DLL mehr zur Generierung dieser Adresse gebraucht hat. So konnte ich mit einem Dienstkonto die Aktion ohne privilegierte Rechte auf dem Computer selbst ausführen.
  • Ausführen als Task
    Ich könnte den Code als Task im Windows Taskplaner ohne Zeitplan aber mit "Run as Administrator" einrichten. Um die Funktion auszuführen könne ich die gewünschten Parameter in eine XML/JSON-Datei ablegen und dann den Task anstarten. Das Skript müsste dann nur die Informationen quasi aus der "Job-Datei" auslesen und ausführen.
  • Logik als Dienst implementieren
    Wenn ich gleich richtig programmieren möchte, dann kann ich ihn auch als Dienst umsetzen, welcher die privilegierten Tätigkeiten umsetzt. Ich brauche dann auch nur eine Schnittstelle, über die eine weniger privilegierte Software mit dem Dienst kommunizieren kann. Der Dienst sollte natürlich genau die übergebenen Informationen prüfen. Das ist im Grund das gleiche, was auch Exchange mit RBAC - Role Based Access Control macht. Als nicht direkt berechtigter Benutzer beauftrage ich Exchange etwas für mich zu tun. Exchange prüft dann meine Rollen, ehe er die Aktion umsetzt
  • UAC abschalten
    Der schlechteste Option wäre es aber, UAC abzuschalten, nur damit alle Skripte mit höheren Berechtigungen gestartet werden. Leider kann ich keine Skripte oder Programme explizit als "vertrauenswürdig" einstufen, so dass diese keine UAC-Abfrage triggern.

Mein Exchange Problem in meiner Testumgebung konnte ich so lösen, dass ich einfach alle Daten komplett selbst generiert und mitgegeben habe. Damit musste die Recipient Management PowerShell gar nicht mehr eine DLL in ihren Prozessraum nachladen, was den UAC-Dialog angetriggert hat.

Weitere Links