PowerShell Serialization

Wenn Sie per PowerShell remote auf anderen Systemen arbeiten, dann müssen Daten übertragen werden. Wenn dies nicht nur einfache Variablen (z.B. String, Bytes, Integer) sondern komplexe Objekte sind, dann werden diese vor der Übertragung "serialisiert" und danach wieder zusammengesetzt. Mit dem Nov 2023 Security Update für Exchange 2016/2019 hat Microsoft auf Exchange Servern nun die Signierung dieser Daten aktiviert. Höchste Zeit, sich mit der Thematik selbst zu beschäftigen.

Was ist "Serialisierung"

Programme und Skripte verarbeiten Daten die im Speicher liegen. Wir kennen alle einfache Daten wie "Zeichenketten (Strings), "Ganzzahlen (Int,double)", "Fließzahlen (float)" aber auch komplexere Daten wie Arrays, Hashtabellen oder Objekte, die neben Daten auch Methoden enthalten. Werden diese Daten nun zwischen verschiedenen Sitzungen (Sessions) übergeben, z.B. Pipeline oder Remote PowerShell, dann müssen Sie geeignet zur Übertragung konvertiert werden. Auf dem gleichen lokalen Computer kann man die Speicherstruktur ja einfach "kopieren" oder "ByRef" direkt einen Verweis darauf übergeben.

Sobald sich die beiden Prozesse aber weiter entfernt befinden, mit unterschiedlichen Berechtigungen laufen o.ä.. müssen die "Serialisierung" beim Versender und die "Deserialisierung" beim Empfänger stattfinden. Oder wie MIcrosoft schreibt:

Data serialization is the process of converting the state of an object into a form (stream of bytes) that can be persisted or transmitted to memory, a database, or a file. PowerShell, for example, uses serialization when passing .NET objects between sessions. After an object was submitted or stored, it can be reconstructed back to its previous format. This process is called deserialization.
Quelle: https://learn.microsoft.com/en-us/exchange/plan-and-deploy/post-installation-tasks/security-best-practices/exchange-serialization-payload-sign?view=exchserver-2019#what-is-data-serialization

Serialization is the process of converting the state of an object into a form (stream of bytes) that can be persisted or transmitted to memory, a database, or a file. PowerShell, for example, uses serialization (and its counterpart deserialization) when passing objects between sessions
Quelle: https://techcommunity.microsoft.com/t5/exchange-team-blog/released-january-2023-exchange-server-security-updates/ba-p/3711808

Davon bekommen Sie als Entwickler aber eigentlich nichts mit. Wobei das nicht ganz stimmt.

Properties und Methoden

Sie fragen sich vermutlich immer noch, um was es hier eigentlich geht. Am Beispiel von Exchange lassen ich am besten die verschiedenen Varianten einer Serialisierung und die Folgen für ihren Code ablesen. Ich nutze dazu das Commandlet "Get-Mailboxstatistics" um die Größe eines Exchange OnPremises Postfachs zu ermitteln. Zuerst starte ich es auf dem Server, um mit die "TotalItemSize" einer Mailbox anzeigen zu lassen. Zuerst gehe ich direkt auf den Exchange Server und starte die Exchange PowerShell über das Startmenü und hole mit die Größe der Mailbox eines Testbenutzers.

(get-mailboxstatistics mailbox1).totalitemsize

Als Mensch sehen Sie gleich, dass das Postfach gerade mal 46MB hat. Aber um per Skript den Wert zu erhalten, müssen Sie auf das Property "Value" zugreifen.

Interessant ist, dass dieses Property auch noch Methoden hat, z.B. ToGB(), bei der Hier durch die Abrundung natürlich 0 rauskommt. Der "Type" der nach dem "ToGB()" herauskommt, ist ein UINT64.

Value selbst ist aber vom Type "ByteQuantifiedSize"

Mit der Methode "ToString()" können Sie den Wert immer direkt konvertieren aber müssen dann mit dem String weiterrechnen.

Der Umweg über den String muss aber nicht schlecht sein, denn nun habe ich auf einem anderen Server, auf dem keine Exchange Management Tools installiert waren, mit eine Exchange Remote PowerShell verbunden und die gleichen Befehle ausgeführt. Auf den ersten Blick sieht noch alles gleich aus. Ich sehe "TotalItemSize" als Objekt mit den Properties "IsUnlimited" und "Value" und den Wert "Value" kann ich mir auch anzeigen lassen.

Aber der Versuch diesen Wert mit "ToGB()" umrechnen zu lassen, schlägt mit folgendem Fehler fehl.

InvalidOperation: Method invocation failed because [Deserialized.Microsoft.Exchange.Data.ByteQuantifiedSize] does not contain a method named 'togb'.

Das Objekt ist ein "Deserialized"-Objekt und hat nur eine Teilmenge der Properties und Methoden.

Das ist natürlich sehr unschön, wenn Sie per PowerShell mit Exchange oder anderen Diensten verschiedene Auswertungen machen und mit den Werten "rechnen" wollen. Es gibt im Internet sehr viele Fundstellen zu Menschen, die genau mit diesen Problemen sich rumschlagen und eigentlich keine Lösung wissen. Die "ToString()"Ausgabe von "ByteQuantifiedSize" ist nämlich ein sehr hässlicher String und eine einfache Typ-Conversion funktioniert auch nicht

Alle Versuche mit einem Format-Angabe als Parameter bei ToString() haben zumindest bei mir nicht funktioniert.

Letztlich trenne ich den String bei der ersten Kammer auf, entferne das Leerzeichen zwischen dem Wert und der Einheit und konvertiere den String wieder zu einer Zahl.

PS C:\> (get-mailboxstatistics frank.carius4).totalitemsize.value.tostring().split("(").replace(" ","")[0] -as [uint64]

Damit kann ich dann weiter rechnen.

Wichtig ist aber zu verstehen, dass ein per Remote PowerShell und Serialisierung und Deserialisierung übertragenes Objekte zwar noch die Properties aber nicht mehr die Methoden hat. Hinter den Methoden liegt ja der Code, der nicht übertragen wird. Ich habe sogar schon Rückgaben direkt als String gesehen.

Daher sollten Sie immer genau wissen, ob sie ein Skript auf dem Server, auf einem Client mit den Exchange Management Tools oder Remote ausführen

Hinweis: Die Exchange Online PowerShell 3 liefert auch keinen "Code" mit, sondern nur deserialisierte Objekte ohne Metehoden.

Sie haben nun die Wahl, ob sie den Type prüfen, ehe Sie "toGB()" und andere Methoden nutzen oder direkt erst einen String draus machen und dann wieder zurückkonvertieren. Wobei hier auch wieder Sprachabhängigkeiten auftreten können.

Exchange Serial Signing

Wenn über über so eine Remote Session Daten oder sogar komplexe Objekte übertragen werden, dann könnte dies ein Einfallstor für Schadcode sein. Leider ist ja nicht immer sichergestellt, dass alle PowerShell Verbindungen auch immer per TLS hergestellt werden. Das sollte zwar so sein aber selbst dann könnte es ja einen Man in the Middle geben. Daher signiert der Exchange Server den serialisierten Datenstrom mit dem "Private Key" des "Exchange Authentication Zertifikat". Diese Zertifikat gibt es genau einmal pro Exchange Organisation und wird von allen Exchange Servern gemeinsam genutzt und zwischen den Server auch repliziert.

Es ist per Default 5 Jahre gültig und sollte von ihnen immer mal wieder aktualisiert werden. Die Exchange Management Shell weist Sie auch auf ein abgelaufenes Zertifikat hin:

Microsoft hat auf PSS-Exchange auch extra ein Skript bereit gestellt, um dieses Zertifikat zu prüfen und ggfls. zu erneuern:

Ich habe schon mehrere Umgebungen gesehen, in denen das Zertifikat abgelaufen war und es keine sichtbaren Probleme gegeben hat. Die kommen erst mit dem Nov 2023 Update oder wenn Sie den Exchange Hybrid Mode mit Exchange Online nutzen.

Für die meisten Exchange Administratoren war die Bereitstellung von "Certificate Signing of PowerShell Serialization Payload" unbemerkt geblieben, denn mit dem Security Update "Jan 2023" wurde die Funktion zwar schon bereitgestellt aber noch nicht aktiviert. Das war auch OK, denn in den ersten Monaten sind doch die ein oder anderen Commandlets aufgefallen, die damit noch nicht fehlerfrei funktioniert haben. Auch Exchange 2013 war bis April 2013 noch "supported" aber unterstützte diese Funktion gar nicht.

Aber im November Updates und lange nach Supportende von Exchange 2013 wurde die Funktion nun per Default eingeschaltet.

Wenn Sie noch nicht das Nov 2023 Update installiert haben, konnten Sie die Signierung der PowerShell Serialization Payload schon wie folgt aktivieren:

# PowerShell Serialization Payload Signierung einschalten
New-SettingOverride `
   -Name "EnableSigningVerification" `
   -Component Data `
   -Section EnableSerializationDataSigning `
   -Parameters @("Enabled=true") `
   -Reason "Enabling Signing Verification"

Wenn Sie das Nov 2023 Update installiert haben aber die Funktion dennoch deaktivieren möchten, dann geht das natürlich auch

# PowerShell Serialization Payload Signierung ausschalten
New-SettingOverride `
   -Name "EnableSigningVerification" `
   -Component Data `
   -Section EnableSerializationDataSigning `
   -Parameters @("Enabled=false") `
   -Reason "Enabling Signing Verification"

Weitere Links