Aufruflisten bieten das Wer
Eines der wichtigsten Unterscheidungsmerkmale von Elastic für die Telemetrie von Windows-Endpunkten sind Aufruflisten.
Die meisten Erkennungen beruhen auf dem, was passiert – und das ist oft unzureichend, da die meisten Verhaltensweisen einen doppelten Zweck haben. Mit Aufrufstapeln fügen wir die fein abgestufte Möglichkeit hinzu, auch zu bestimmen , wer die Aktivität ausführt. Diese Kombination gibt uns eine beispiellose Fähigkeit, bösartige Aktivitäten aufzudecken. Durch die Einspeisung dieser umfassenden Telemetriedaten in die On-Host-Regel-Engine von Elastic Defendkönnen wir schnell auf neue Bedrohungen reagieren.
Aufruflisten sind eine schöne Lüge
In der Informatik ist ein Stack eine Last-in-First-out-Datenstruktur. Ähnlich wie bei einem Stapel physischer Gegenstände ist es nur möglich, das oberste Element hinzuzufügen oder zu entfernen. Ein Aufrufstapel ist ein Stack, der Informationen über die derzeit aktiven Unterprogrammaufrufe enthält.
Auf x64-Hosts kann diese Aufrufliste nur mithilfe von Ausführungsablaufverfolgungsfunktionen auf der CPU genau generiert werden, z. B. Intel LBR, Intel BTS, Intel AET, Intel IPT und x64 Architectural LBR. Diese Ablaufverfolgungsfeatures wurden für die Erstellung von Leistungsprofilen und Debuggen entwickelt, können aber auch in einigen Sicherheitsszenarien verwendet werden. Was jedoch allgemeiner verfügbar ist, ist ein ungefährer Aufrufstapel, der über einen Mechanismus namens Stack Walking aus dem Datenstapel eines Threads wiederhergestellt wird.
In der x64-Architektur verweist das "Stack Pointer Register" (rsp) wenig überraschend auf eine Stack-Datenstruktur, und es gibt effiziente Anweisungen zum Lesen und Schreiben der Daten auf diesem Stack. Darüber hinaus überträgt die call -Anweisung die Steuerung an ein neues Unterprogramm, speichert aber auch eine Rückgabeadresse an der Speicheradresse, auf die der Stack-Zeiger verweist. Eine ret Anweisung ruft später diese gespeicherte Adresse ab, so dass die Ausführung an die Stelle zurückkehren kann, an der sie aufgehört hat. Funktionen in den meisten Programmiersprachen werden in der Regel mit diesen beiden Anweisungen implementiert, und sowohl Funktionsparameter als auch lokale Funktionsvariablen werden in der Regel aus Leistungsgründen auf diesem Stapel zugeordnet. Der Teil des Stacks, der sich auf eine einzelne Funktion bezieht, wird als Stack-Frame bezeichnet.
Beim Stack-Walking handelt es sich um die Wiederherstellung der Rückgabeadressen aus den heterogenen Daten, die auf dem Thread-Stack gespeichert sind. Rückgabeadressen müssen für den Kontrollfluss irgendwo gespeichert werden – daher werden diese vorhandenen Daten beim Stack Walking kombiniert, um sich einem Aufrufstapel anzunähern . Dies ist für die meisten Debug- und Leistungsprofilerstellungsszenarien vollständig geeignet, aber für die Sicherheitsüberwachung etwas weniger hilfreich. Das Hauptproblem ist, dass Sie nicht rückwärts zerlegen können. Sie können immer die Rückgabeadresse für eine bestimmte Aufrufseite ermitteln, aber nicht umgekehrt. Der beste Ansatz, den Sie wählen können, besteht darin, jede der 15 möglichen vorhergehenden Befehlslängen zu überprüfen und zu sehen, welche sich in genau eine Aufrufanweisung zerlegt. Selbst dann haben Sie nur eine vorherige Aufrufseite wiederhergestellt, nicht unbedingt die exakte vorherige Aufrufstelle. Dies liegt daran, dass die meisten Compiler die Optimierung von Tail-Aufrufen verwenden, um unnötige Stack-Frames auszulassen. Dies führt zu ärgerlichen Szenarien für die Sicherheit , z. B. dass es keine Garantie dafür gibt, dass sich die Win32StartAddress-Funktion auf dem Stapel befindet, obwohl sie aufgerufen wurde.
Was wir normalerweise als Aufrufstapel bezeichnen, ist also eigentlich ein Rücksendeadressenstapel.
Malware-Autoren nutzen diese Mehrdeutigkeit, um zu lügen. Sie erstellen entweder Trampolin-Stack-Frames durch legitime Module, um Aufrufe zu verbergen, die von bösartigem Code stammen, oder sie zwingen Stack Walking dazu, andere Rückgabeadressen vorherzusagen als die, die die CPU ausführen wird. Natürlich war Malware schon immer nur ein Versuch zu lügen, und Antimalware ist nur der Prozess, um diese Lüge zu entlarven.
“... aber mit der Letzten, die Wahrheit wird herauskommen.«
- William Shakespeare, Der Kaufmann von Venedig, 2. Akt, 2. Szene
Ansprechende Aufruflisten
Bisher ist ein Stack Walk nur eine Liste von numerischen Speicheradressen. Um sie für die Analyse nutzbar zu machen, müssen wir sie mit Kontext anreichern. (Hinweis: Wir schließen derzeit keine Kernel-Stack-Frames ein.)
Die minimale sinnvolle Anreicherung besteht darin, diese Adressen in Offsets innerhalb von Modulen umzuwandeln (z. ntdll.dll+0x15c9c4). Dies würde jedoch nur die ungeheuerlichste Malware abfangen – wir können tiefer gehen. Die wichtigsten Module unter Windows sind diejenigen, die die nativen und Win32-APIs implementieren. Die binäre Anwendungsschnittstelle für diese APIs erfordert, dass der Name jeder Funktion im Exportverzeichnis des enthaltenden Moduls enthalten ist. Dies sind die Informationen, die Elastic derzeit verwendet, um seine Endpunkt-Aufruf-Stacks anzureichern.
Eine genauere Anreicherung könnte durch die Verwendung der öffentlichen Symbole (falls verfügbar) erreicht werden, die auf der Infrastruktur des Anbieters (insbesondere Microsoft) gehostet werden. Diese Methode bietet zwar eine höhere Genauigkeit, ist aber mit höheren Betriebskosten verbunden und für unsere Air-Gapped-Kunden nicht praktikabel.
Als Faustregel für Microsoft-Kernel und native Symbole gilt, dass die exportierte Schnittstelle jeder Komponente ein großgeschriebenes Präfix wie Ldr, Tp oder Rtl hat. Private Funktionen erweitern dieses Präfix um ein p. Standardmäßig sind private Funktionen mit externer Verknüpfung in der öffentlichen Symboltabelle enthalten. Ein sehr großer Offset kann auf eine sehr große Funktion hinweisen, aber auch nur auf eine unbenannte Funktion, für die Sie keine Symbole haben. Eine allgemeine Richtlinie wäre, alle dreistelligen und größeren Offsets in einer exportierten Funktion als wahrscheinlich zu einer anderen Funktion gehörend zu betrachten.
| Aufrufliste | Stack Walk | Stack Walk-Module | Stack Walk-Exporte (elastischer Ansatz) | Stack Walk Öffentliche Symbole |
|---|---|---|---|---|
| 0x7ffb8eb9c9c2 0x12d383f0046 0x7ffb8eb1a9d8 0x7ffb8eb1aaf4 0x7ffb8ea535ff 0x7ffb8da5e8cf 0x7ffb8eaf14eb | 0x7ffb8eb9c9c4 0x7ffb8c3c71d6 0x7ffb8eb1a9ed 0x7ffb8eb1aaf9 0x7ffb8ea53604 0x7ffb8da5e8d4 0x7ffb8eaf14f1 | ntdll.dll+0x15c9c4 kernelbase.dll+0xc71d6 ntdll.dll+0xda9ed ntdll.dll+0xdaaf9 ntdll.dll+0x13604 kernel32.dll+0x2e8d4 ntdll.dll+0xb14f1 | ntdll.dll! NtProtectVirtualMemory+0x14 kernelbase.dll! VirtualProtect+0x36 ntdll.dll! RtlAddRefActivationContext+0x40d ntdll.dll! RtlAddRefActivationContext+0x519 ntdll.dll! RtlAcquireSRWLockExclusive+0x974 kernel32.dll! BaseThreadInitThunk+0x14 ntdll.dll! RtlUserThreadStart+0x21 | ntdll.dll! NtProtectVirtualMemory+0x14 kernelbase.dll! VirtualProtect+0x36 ntdll.dll! RtlTpTimerCallback+0x7d ntdll.dll! TppTimerpExecuteCallback+0xa9 ntdll.dll! TppWorkerThread+0x644 kernel32.dll! BaseThreadInitThunk+0x14 ntdll.dll! RtlUserThreadStart+0x21 |
Vergleich der Anreicherungsstufen der Aufrufliste
Im obigen Beispiel hat der Shellcode bei 0x12d383f0000 absichtlich einen Tail-Aufruf verwendet, damit seine Adresse nicht im Stack-Walk angezeigt wird. Diese Lüge durch Unterlassung ist sogar offensichtlich, wenn man nur den Pirschgang kennt. Elastic meldet dies mit der Heuristik proxy_call , da die Malware eine Timer-Callback-Funktion registriert hat, um den Aufruf an VirtualProtect von einem anderen Thread aus weiterzuleiten.
Leistungsstarke Aufruflisten
Die Aufruflisten der Systemaufrufe, die wir mit der Ereignisablaufverfolgung für Windows (ETW) überwachen, weisen eine erwartete Struktur auf. Am unteren Ende des Stacks befindet sich der Thread StartAddress – in der Regel ntdll.dll! RtlUserThreadStart. Darauf folgt der Win32-API-Thread-Eintrag - kernel32.dll! BaseThreadInitThunk und dann das erste Benutzermodul. Ein Benutzermodul ist Anwendungscode, der nicht Teil der Win32-API (oder der nativen) API ist. Dieses erste Benutzermodul sollte mit der Win32StartAddress des Threads übereinstimmen (es sei denn, diese Funktion hat einen Tail-Aufruf verwendet). Weitere Benutzermodule werden folgen, bis das endgültige Benutzermodul einen Aufruf einer Win32-API ausführt, die einen nativen API-Aufruf ausführt, der schließlich zu einem Systemaufruf an den Kernel führt.
Vom Standpunkt der Erkennung aus ist das wichtigste Modul in dieser Aufrufliste das Endbenutzermodul. Elastic zeigt dieses Modul an, einschließlich seines Hashs und aller Codesignaturen. Diese Details helfen bei der Triage von Warnungen, aber noch wichtiger ist, dass sie die Granularität drastisch verbessern, mit der wir das Verhalten legitimer Software ermitteln können, die sich manchmal wie Malware verhält. Je genauer wir den Normalwert ermitteln können, desto schwieriger ist es für Malware, sich einzumischen.
{
"process.thread.Ext": {
"call_stack_summary": "ntdll.dll|kernelbase.dll|file.dll|rundll32.exe|kernel32.dll|ntdll.dll",
"call_stack": [
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtAllocateVirtualMemory+0x14" }, /* Native API */
{ "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualAllocExNuma+0x62" }, /* Win32 API */
{ "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualAllocEx+0x16" }, /* Win32 API */
{
"symbol_info": "c:\\users\\user\\desktop\\file.dll+0x160d8b", /* final user module */
"callsite_trailing_bytes": "488bf0488d4d88e8197ee2ff488bc64883c4685b5e5f415c415d415e415f5dc390909090905541574156415541545756534883ec58488dac2490000000488b71",
"callsite_leading_bytes": "088b4d38894c2420488bca48894db8498bd0488955b0458bc1448945c4448b4d3044894dc0488d4d88e8e77de2ff488b4db8488b55b0448b45c4448b4dc0ffd6"
},
{ "symbol_info": "c:\\users\\user\\desktop\\file.dll+0x7b429" },
{ "symbol_info": "c:\\users\\user\\desktop\\file.dll+0x44a9" },
{ "symbol_info": "c:\\users\\user\\desktop\\file.dll+0x5f58" },
{ "symbol_info": "c:\\windows\\system32\\rundll32.exe+0x3bcf" },
{ "symbol_info": "c:\\windows\\system32\\rundll32.exe+0x6309" }, /* first user module - typically the ETHREAD.Win32StartAddress module */
{ "symbol_info": "c:\\windows\\system32\\kernel32.dll!BaseThreadInitThunk+0x14" }, /* Win32 API */
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlUserThreadStart+0x21" /* Native API - the ETHREAD.StartAddress module */
}
],
"call_stack_final_user_module": {
"path": "c:\\users\\user\\desktop\\file.dll",
"code_signature": [ { "exists": false } ],
"name": "file.dll",
"hash": { "sha256": "0240cc89d4a76bafa9dcdccd831a263bf715af53e46cac0b0abca8116122d242" }
}
}
}
Beispiel für eine angereicherte Aufrufliste
Anreicherungen von Aufruflisten-Endbenutzermodulen:
| name | Der Dateiname der call_stack_final_user_module. Kann auch "Nicht gesichert" sein, was auf privaten ausführbaren Speicher hinweist, oder "Unbestimmt", was auf eine verdächtige Aufrufliste hinweist. |
|---|---|
| Pfad | Der Dateipfad der call_stack_final_user_module. |
| hash.sha256 | Das sha256 des call_stack_final_user_module oder das protection_provenance Modul, falls vorhanden. |
| code_signature | Codesignatur des call_stack_final_user_module oder des protection_provenance Moduls, falls vorhanden. |
| allocation_private_bytes | Die Anzahl der Bytes in diesem Speicherbereich, die sowohl +X als auch nicht gemeinsam nutzbar sind. Werte ungleich Null können auf Code-Hooking, Patching oder Aushöhlung hinweisen. |
| protection | Der Speicherschutz für den handelnden Bereich von Seiten ist enthalten, wenn es sich nicht um RX handelt. Entspricht MEMORY_BASIC_INFORMATION. Schützen. |
| protection_provenance | Der Name des Speicherbereichs, der die letzte Änderung des Schutzes dieser Seite verursacht hat. "Ungesichert" kann auf Shellcode hinweisen. |
| protection_provenance_path | Der Pfad des Moduls, das die letzte Änderung des Schutzes dieser Seite verursacht hat. |
| Grund | Die anomale call_stack_summary, die zu einer "unbestimmten" protection_provenance führte. |
Ein schnelles Glossar für die Aufrufliste
Bei der Untersuchung von Aufruflisten gibt es einige native API-Funktionen, mit denen man sich vertraut machen sollte. Ken Johnson, jetzt bei Microsoft, hat uns einen Katalog von Rückrufen vom NTDLL-Kernelmodus zum Benutzermodus zur Verfügung gestellt, um uns den Einstieg zu erleichtern. Im Ernst, Sie sollten hier eine Pause einlegen und das zuerst lesen.
Wir haben RtlUserThreadStart vorhin kennengelernt. Sowohl er als auch sein gleichgeordnetes Element RtlUserFiberStart sollten immer nur am unteren Rand einer Aufrufliste angezeigt werden. Dies sind die Einstiegspunkte für Benutzerthreads bzw. Fibers. Die erste Anweisung in jedem Thread ist jedoch tatsächlich LdrInitializeThunk. Nach dem Ausführen der Benutzermoduskomponente der Threadinitialisierung (und ggf. Verarbeitung) überträgt diese Funktion die Steuerung über NtContinue an den Einstiegspunkt, wodurch der Anweisungszeiger direkt aktualisiert wird. Das bedeutet, dass es in zukünftigen Stackwalks nicht mehr auftaucht.
Wenn Sie also eine Aufrufliste sehen, die LdrInitializeThunk enthält, bedeutet dies, dass Sie sich ganz am Anfang der Ausführung eines Threads befinden. Hier arbeitet die Shim Engine für die Anwendungskompatibilität, hier installieren sich Hook-basierte Sicherheitsprodukte bevorzugt selbst und hier versucht Malware, sich vor diesen anderen Sicherheitsprodukten auszuführen. Marcus Hutchins und Guido Miggelenbrink haben beide hervorragende Blogs zu diesem Thema geschrieben. Dieses Startup-Rennen gibt es nicht für Sicherheitsprodukte, die Kernel-ETW für die Telemetrie verwenden.
{
"process.thread.Ext": {
"call_stack_summary": "ntdll.dll|file.exe|ntdll.dll",
"call_stack": [
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!ZwProtectVirtualMemory+0x14" },
{ "symbol_info": "c:\\users\\user\\desktop\\file.exe+0x1bac8" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlAnsiStringToUnicodeString+0x3cb" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitShimEngineDynamic+0x394d" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitializeThunk+0x1db" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitializeThunk+0x63" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitializeThunk+0xe" }
],
"call_stack_final_user_module": {
"path": "c:\\users\\user\\desktop\\file.exe",
"code_signature": [ { "exists": false } ],
"name": "file.exe",
"hash": { "sha256": "a59a7b56f695845ce185ddc5210bcabce1fff909bac3842c2fb325c60db15df7" }
}
}
}
Beispiel für die Ausführung vor dem Einstiegspunkt
Das nächste Paar ist KiUserExceptionDispatcher und KiRaiseUserExceptionDispatcher. Der Kernel verwendet ersteres, um die Ausführung an einen registrierten strukturierten Ausnahmehandler im Benutzermodus zu übergeben, nachdem eine Ausnahmebedingung für den Benutzermodus aufgetreten ist. Letzteres löst ebenfalls eine Ausnahme aus, jedoch stattdessen im Namen des Kernels. Diese zweite Variante wird in der Regel nur von Debuggern, einschließlich Application Verifier, abgefangen und hilft zu identifizieren, wenn der Code im Benutzermodus die Rückgabecodes von Systemaufrufen nicht ausreichend überprüft. Diese Funktionen werden in der Regel in Aufruflisten angezeigt, die sich auf die anwendungsspezifische Absturzbehandlung oder die Windows-Fehlerberichterstattung beziehen. Manchmal wird er jedoch von Malware als Pseudo-Breakpoint verwendet, z. B. wenn sie den Speicherschutz so verändern möchte, dass ihr Shellcode sofort nach einem Systemaufruf wieder ausgeblendet wird.
{
"process.thread.Ext": {
"call_stack_summary": "ntdll.dll|file.exe|ntdll.dll|file.exe|kernel32.dll|ntdll.dll",
"call_stack": [
{
"symbol_info": "c:\\windows\\system32\\ntdll.dll!ZwProtectVirtualMemory+0x14",
"protection_provenance": "file.exe", /* another vendor's hooks were unhooked */
"allocation_private_bytes": 8192
},
{ "symbol_info": "c:\\users\\user\\desktop\\file.exe+0xd99c" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlInitializeCriticalSectionAndSpinCount+0x1c6" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlWalkFrameChain+0x1119" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!KiUserExceptionDispatcher+0x2e" },
{ "symbol_info": "c:\\users\\user\\desktop\\file.exe+0x12612" },
{ "symbol_info": "c:\\windows\\system32\\kernel32.dll!BaseThreadInitThunk+0x14" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlUserThreadStart+0x21" }
],
"call_stack_final_user_module": {
"name": "file.exe",
"path": "c:\\users\\user\\desktop\\file.exe",
"code_signature": [ { "exists": false }],
"hash": { "sha256": "0e5a62c0bd9f4596501032700bb528646d6810b16d785498f23ef81c18683c74" }
}
}
}
Beispiel für eine Fluktuation des Schutzes über den Ausnahmehandler
Als nächstes kommt KiUserApcDispatcher, das zum Bereitstellen von Benutzer-APCs verwendet wird. Dies ist eines der beliebtesten Tools von Malware-Autoren, da Microsoft nur einen begrenzten Einblick in ihre Verwendung bietet.
{
"process.thread.Ext": {
"call_stack_summary": "ntdll.dll|kernelbase.dll|ntdll.dll|kernelbase.dll|cronos.exe",
"call_stack": [
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtProtectVirtualMemory+0x14" },
{ "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualProtect+0x36" }, /* tail call */
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!KiUserApcDispatcher+0x2e" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!ZwDelayExecution+0x14" },
{ "symbol_info": "c:\\windows\\system32\\kernelbase.dll!SleepEx+0x9e" },
{
"symbol_info": "c:\\users\\user\\desktop\\file.exe+0x107d",
"allocation_private_bytes": 147456, /* stomped */
"protection": "RW-", /* fluctuation */
"protection_provenance": "Undetermined", /* proxied call */
"callsite_leading_bytes": "010000004152524c8d520141524883ec284150415141baffffffff41525141ba010000004152524c8d520141524883ec284150b9ffffffffba0100000041ffe1",
"callsite_trailing_bytes": "4883c428c3cccccccccccccccccccccccccccc894c240857b820190000e8a10c0000482be0488b052fd101004833c44889842410190000488d84243014000048"
}
],
"call_stack_final_user_module": {
"name": "Undetermined",
"reason": "ntdll.dll|kernelbase.dll|ntdll.dll|kernelbase.dll|file.exe"
}
}
}
Beispiel für Schutzfluktuation über APC
Der Windows-Fenstermanager ist in einem Gerätetreiber im Kernelmodus (win32k.sys) implementiert. Meist. Manchmal muss der Fenstermanager etwas aus dem Benutzermodus heraus tun, und KiUserCallbackDispatcher ist der Mechanismus, um dies zu erreichen. Es handelt sich im Grunde um einen umgekehrten Systemaufruf, der auf user32.dll Funktionen abzielt. Das Überschreiben eines Eintrags in der KernelCallbackTable eines Prozesses ist eine einfache Möglichkeit, einen GUI-Thread zu kapern, sodass jedes andere Modul, das auf diesen Aufruf folgt, verdächtig ist.
Die Kenntnis des Zwecks jedes dieser Einstiegspunkte vom Kernelmodus zum Benutzermodus hilft erheblich bei der Bestimmung, ob ein bestimmter Aufrufstapel natürlich ist oder ob er missbraucht wurde, um alternative Ziele zu erreichen.
Aufruflisten verständlich machen
Um die Verständlichkeit zu verbessern, kennzeichnen wir das Ereignis auch mit verschiedenen Prozessen. Ext.api.behaviors, die wir identifizieren. Diese Verhaltensweisen sind nicht unbedingt bösartig, aber sie heben Aspekte hervor, die für die Alarmtriage oder die Bedrohungssuche relevant sind. Dazu gehören für Aufruflisten:
| native_api | Ein Aufruf erfolgte direkt an die native API und nicht an die Win32-API. |
|---|---|
| direct_syscall | A syscall instruction originated outside of the Native API layer. |
| proxy_call | Die Aufrufliste kann auf einen Proxy-API-Aufruf hinweisen, um die wahre Quelle zu maskieren. |
| Shellcode | Ausführbarer Nicht-Bildspeicher der zweiten Generation, der als vertrauliche API bezeichnet wird. |
| image_indirect_call | An entry in the call stack was preceded by a call to a dynamically resolved function. |
| image_rop | Einem Eintrag in der Aufrufliste ging keine Aufrufanweisung voraus. |
| image_rwx | Ein Eintrag in der Aufrufliste ist beschreibbar. Der Code sollte schreibgeschützt sein. |
| unbacked_rwx | Ein Eintrag in der Aufrufliste ist kein Bild und beschreibbar. Auch JIT-Code sollte schreibgeschützt sein. |
| truncated_stack | The call stack seems to be unexpectedly truncated. This may be due to malicious tampering. |
In einigen Kontexten können diese Verhaltensweisen allein ausreichen, um Malware zu erkennen.
Spoofing – Umgehung oder Haftung?
Das Spoofing von Absenderadressen ist seit vielen, vielen Jahren eine gängige Hacking - und Malware-Technik . Dieser einfache Trick ermöglicht es injiziertem Code, sich den Ruf eines legitimen Moduls mit wenigen Konsequenzen zu leihen. Das Ziel einer umfassenden Aufruf-Stack-Inspektion und Verhaltensbaselines besteht darin, Malware keinen Freifahrtschein mehr zu geben.
Offensive Researcher haben diese Bemühungen unterstützt, indem sie nach Ansätzen für vollständiges Call-Stack-Spoofing gesucht haben. Vor allem:
- Spoofing von Call Stacks, um EDRs zu verwirren von William Burgess
- SilentMoonwalk: Implementierung eines dynamischen Call Stack Spoofers von Alessandro Magnosi, Arash Parsa und Athanasios Tserpelis
SilentMoonwalk ist nicht nur eine hervorragende offensive Forschung, sondern auch ein hervorragendes Beispiel dafür, wie Lügen dich in doppelt so viele Schwierigkeiten bringen kann – aber nur, wenn du erwischt wirst. Viele Techniken zur Umgehung der Verteidigung beruhen auf Sicherheit durch Verschleierung – und sobald sie von Forschern aufgedeckt werden, können sie zu einer Belastung werden. In diesem Fall umfasste die Untersuchung Ratschläge zu den Aufdeckungsmöglichkeiten, die durch den Umgehungsversuch entstehen .
{
"process.thread.Ext": {
"call_stack_summary": "ntdll.dll|kernelbase.dll|kernel32.dll|ntdll.dll",
"call_stack": [
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtAllocateVirtualMemory+0x14" },
{ "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualAlloc+0x48" },
{
"symbol_info": "c:\\windows\\system32\\kernelbase.dll!CreatePrivateObjectSecurity+0x31",
/* 4883c438 stack desync gadget - add rsp 0x38 */
"callsite_trailing_bytes": "4883c438c3cccccccccccccccccccc48895c241057498bd8448bd2488bf94885c90f84660609004885db0f845d060900418bd14585c97411418bc14803c383ea",
"callsite_leading_bytes": "cccccccccccccccccccccccccccccc4883ec38488b4424684889442428488b442460488944242048ff15d9b21b000f1f44000085c00f8830300900b801000000"
},
{ "symbol_info": "c:\\windows\\system32\\kernelbase.dll!Internal_EnumSystemLocales+0x406" },
{ "symbol_info": "c:\\windows\\system32\\kernelbase.dll!SystemTimeToTzSpecificLocalTimeEx+0x2d1" },
{ "symbol_info": "c:\\windows\\system32\\kernelbase.dll!WaitForMultipleObjectsEx+0x982" },
{ "symbol_info": "c:\\windows\\system32\\kernel32.dll!BaseThreadInitThunk+0x14" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlUserThreadStart+0x21" }
],
"call_stack_final_user_module": {
"name": "Undetermined", /* gadget module resulted in suspicious call stack */
"reason": "ntdll.dll|kernelbase.dll|kernel32.dll|ntdll.dll"
}
}
}
Beispiel für eine SilentMoonwalk-Aufrufliste
Eine Standardtechnik zum Aufspüren versteckter Artefakte besteht darin, sie mit mehreren Techniken aufzulisten und die Ergebnisse auf Diskrepanzen zu vergleichen. So funktioniert RootkitRevealer. Dieser Ansatz wurde auch in Get-InjectedThreadEx.exe verwendet, bei dem der Threadstapel sowohl nach oben als auch nach unten geklettert wird.
Unter bestimmten Umständen können wir eine Aufrufliste auf zwei Arten wiederherstellen. Wenn es Diskrepanzen gibt, wird die weniger zuverlässige Aufrufliste als call_stack_summary_original ausgegeben.
{
"process.thread.Ext": {
"call_stack_summary": "ntdll.dll",
"call_stack_summary_original": "ntdll.dll|kernelbase.dll|version.dll|kernel32.dll|ntdll.dll",
"call_stack": [
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtContinue+0x12" },
{ "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitializeThunk+0x13" }
],
"call_stack_final_user_module": {
"name": "Undetermined",
"reason": "ntdll.dll"
}
}
}
Originalbeispiel für eine Zusammenfassung der Aufrufliste
Call Stacks sind für alle da
Standardmäßig finden Sie nur Aufruflisten in unseren Warnungen, aber dies ist über die erweiterte Richtlinie konfigurierbar.
| events.callstacks.emit_in_events | Wenn diese Option festgelegt ist, werden Aufruflisten in reguläre Ereignisse aufgenommen, in denen sie erfasst werden. Andernfalls werden sie nur in Ereignissen eingeschlossen, die Verhaltensschutzregeln auslösen. Beachten Sie, dass diese Einstellung das Datenvolumen erheblich erhöhen kann. Standardwert: false |
|---|
Weitere Einblicke in Windows-Aufruflisten finden Sie in den folgenden Artikeln der Elastic Security Labs:
- Den Einsatz erhöhen: Erkennen von In-Memory-Bedrohungen mit Kernel-Aufrufstapeln
- Vorhang lüften mit Aufrufstapeln
- Verdoppelung: Erkennen von In-Memory-Bedrohungen mit Kernel-ETW-Aufrufstapeln
- In-the-Wild Windows LPE 0-days: Insights & Detection Strategies
- Modalitäten für schlechtes Verhalten: Erkennen von Werkzeugen, nicht von Techniken
- Die Wahrheit im Schatten finden
