Wer nach einem Einbruch den Arbeitsspeicher eines Rechners untersuchen muss, darf sich der tatkräftigen Unterstützung des Python-Framework Volatility versichern. Es analysiert wichtige Speicherstrukturen und hilft dabei, die flüchtigen Spuren eines Angriffs zu lesen.
Wer an IT-Forensik denkt, hat meist die Analyse von nicht-flüchtigen Datenträgern, wie Festplatten oder SSDs vor Augen. Doch auch der flüchtige, also der volatile, Arbeitsspeicher ist einen Blick wert. Er enthält meist wichtige Spuren, etwa zu Prozessen oder Netzwerkverbindungen, und gibt so Hinweise auf einen erfolgreichen Angriff.
Vor der eigentlichen Detektivarbeit steht das Anfertigen eines RAM-Images. Diesen so genannten Memory Dump gilt es dann zu analysieren. Mit Linux-Bordmitteln können Anwender bereits einiges herausfinden und lernen – allerdings ist das auch recht zeitaufwändig. Unterstützung gibt es daher von Volatility [1]. Das in Python geschriebene Framework identifiziert die wichtigsten Speicherstrukturen eines Betriebssystems und bereitet die Inhalte in für Menschen lesbarer Form auf. Sein großes Plus sind die vielen Plugins, welche die verschiedensten Analyse-Tätigkeiten unterstützen (Tabelle 1).
|
Plugin |
Funktion |
|---|---|
|
Prozesse |
|
|
linux_apihooks |
Checkt auf Userland-API-Hooks |
|
linux_bash |
Extrahiert aus dem Prozess-Speicher die Bash-History |
|
linux_check_creds |
Schaut, ob sich Prozesse Credential Structures teilen |
|
linux_dump_map |
Schreibt ausgewählte Memory Mappings auf eine Disk |
|
linux_dynamic_env |
Holt dynamische Umgebungsvariablen eines Prozesses wieder |
|
linux_getcwd |
Zeigt pro Prozess dessen aktuelles Verzeichnis |
|
linux_kernel_opened_files |
Vom Kernel geöffnete Files |
|
linux_library_list |
Bibliotheken, die ein Prozess geladen hat |
|
linux_librarydump |
Kopiert die Shared Libraries eines Prozesses auf Disk |
|
linux_list_raw |
Listet Prozesse mit verdächtigen Sockets |
|
linux_malfind |
Schaut nach verdächtigen Process Mappings |
|
linux_memmap |
Zeigt die Memory Map von Linux-Tasks |
|
linux_plthook |
Scannt PLT von ELF-Binaries auf unnötige Hooks |
|
linux_proc_maps |
Memory Maps eines Prozesses |
|
linux_procdump |
Schreibt das Executable eines Prozesses auf Disk |
|
linux_process_hollow |
Prüft auf Anzeichen für Process Hollowing |
|
linux_psaux |
Prozesse mit Startzeit und ganzer Kommanozeile |
|
linux_psenv |
Prozesse mit ihren statischen Umgebungsvariablen |
|
linux_psscan |
Scannt den RAM von Prozessen |
|
Kernel und Scheduling |
|
|
linux_check_idt |
Schaut, ob sich die IDT geändert hat |
|
linux_check_inline_kernel |
Sucht nach Inline-Kernelhooks |
|
linux_check_modules |
Vergleicht die Module-Liste mit Sys-FS-Angaben |
|
linux_check_syscall |
Check, ob sich die Systemcall-Tabelle geändert hat |
|
linux_check_tty |
Sucht nach Hooks auf TTY-Devices |
|
linux_hidden_modules |
Durchforstet den RAM nach versteckten Kernelmodulen |
|
linux_info_regs |
Wie »info registers« in GDB |
|
linux_lsmod |
Geladene Kernelmodule |
|
linux_moddump |
Extrahiert geladene Kernelmodule |
|
linux_pslist |
Aktive Tasks aus der »task_struct->task«-Liste |
|
linux_pslist_cache |
Wie »linux_pslist«>, aber aus »kmem_cache« |
|
linux_pstree |
Zeigt Eltern-Kind-Beziehung zwischen Prozessen |
|
linux_psxview |
Sucht versteckte Prozesse |
|
linux_threads |
Zeigt die Threads eines Prozesses |
|
imagecopy |
Kopiert einen physischen Addressbereich in ein Image |
|
Files und Dateisysteme |
|
|
linux_check_fop |
Findet von Rootkits modifizierte File Operation Structures |
|
linux_dentry_cache |
Sammelt Files aus dem Dentrycache |
|
linux_enumerate_files |
Liste mit File-Referenzen aus den Dateisystem-Cache |
|
linux_elfs |
Findet ELF-Binaries in Process Mappings |
|
linux_find_file |
Listet Dateien und stellt sie aus dem RAM wieder her |
|
linux_lsof |
Listet Filedeskriptoren |
|
linux_mount |
Gemountete Dateisysteme bzw. Devices |
|
linux_mount_cache |
Wie »linux_mount«, aber aus dem »kmem_cache« |
|
linux_recover_filesystem |
Stellt RAM-gecachtes Dateisystem wieder her |
|
linux_tmpfs |
Stellt Tmp-FS wieder her |
|
linux_truecrypt_passphrase |
Stellt gecachte Truecrypt-Passphrases wieder her |
|
mbrparser |
Scannt nach Master Boot Records |
|
Netzwerk |
|
|
linux_arp |
Zeigt die ARP-Tabelle |
|
linux_check_afinfo |
Prüft Operation Function Pointers der Netzwerkprotokolle |
|
linux_ifconfig |
Aktive Interfaces |
|
linux_netfilter |
Listet Netfilter-Hooks |
|
linux_netscan |
Untersucht Netzwerk-Structures |
|
linux_netstat |
Listet offene Sockets |
|
linux_route_cache |
Stellt Routing-Cache wieder her |
|
linux_sk_buff_cache |
Stellt Pakete aus »sk_buff kmem_cache« wieder her |
Abgebildet
Der erste Schritt besteht im Erzeugen eines Speicherabbildes. Läuft das eventuell kompromittierte System in einer virtuellen Maschine, kann der Virtual Machine Manager einen Snapshot erstellen, den geeignete Tools dann auseinandernehmen [2],[3]. Ein Bare-Metal-Linux stellt Forensiker vor größere Herausforderungen: Jede Änderung am System ändert auch den Speicherinhalt, was Spuren vernichten könnte.
Der Ansatz, den Speicher im Labor abzukühlen und in einem Spezialgerät auszulesen [4], ist nicht praktikabel. Hat der Rechner einen Firewire-Anschluss, ließe sich eine Sicherheitslücke ausnutzen und der Speicher über den Bus kopieren [5]. Das wird auch für Thunderbolt und PCIe diskutiert, klappt allerdings unter Linux noch nicht so recht [6],[7],[8].
Damit bleibt nur, doch eine Speicherveränderung in Kauf zu nehmen. Bei älteren Linux-Versionen ging das recht simpel, denn »dd« konnte »/dev/mem« lokal auf USB-Sticks oder per »netcat« übers Netzwerk kopieren. Neuere Distributionen schränken die Leserechte allerdings erheblich ein, was aus Sicherheitssicht eine gute Idee ist. Hier hilft beispielsweise der Linux Memory Grabber [9] weiter.
Abgegriffen
Das Skript »lmg« setzt voraus, dass ein Linux-Rechner verfügbar ist, der ein zum Zielsystem passendes Kernelmodul kompilieren kann, erfordert also detaillierte Kenntnisse über das Ziel und einen Rootaccount. In vielen Fällen müssen Forensiker hier passen, es sei denn, ein eigenes System ist betroffen. Dann hilft die Anleitung unter [9] dabei, zu einem Speicherabbild zu gelangen. Zwar verändert die Installation des Kernelmoduls den Speicherinhalt des Zielsystems, und »lmg« greift noch dazu auf Binarys des Zielsystems zurück, was bei einem geknackten System nicht ganz ohne ist, doch dürfte es in den meisten Fällen die einzige gangbare Lösung sein.
Wer bezüglich modifizierter Binarys besonders vorsichtig sein will, könnte sie auf dem USB-Stick mitbringen, vor der Installation eine weitere Speicheränderung in Kauf nehmen und den Pfad so ändern, dass die Programme vom Stick zum Einsatz kommen. Erste Hinweise, ob eine solche Maßnahme nötig ist, erhalten Admins von einem Host-based Intrusion Detection System [10]. Besonders sichere Lösungen wie etwa die in [11] vorgeschlagene gibt es für Linux derzeit nicht. Das ist bei forensischen Untersuchungen ärgerlich, dafür im Alltag durchaus ein Sicherheitsgewinn, denn auch Angreifer könnten solche Hintertüren ausnutzen.
Spurensicherung
Der Admin, der nun im Besitz eines Speicher-Images seines möglicherweise kompromittierten Rechners ist, darf sich jetzt der eigentlichen Analyse zuwenden. Wer Volatility aus den Quellen installieren möchte, benötigt zahlreiche Python-Module. Alternativ gibt es auf der Projektseite [1] fertige Binarys für gängige Betriebssysteme. Anwender entpacken lediglich die Zip-Datei, und das Tool ist einsatzbereit. Wer den langen Namen unhandlich findet, erstellt flugs einen Symlink:
ln -s volatility_2.6_lin64_standalone vol
Außerdem bietet es sich an, ein Unterverzeichnis namens »profiles« anzulegen. Hier ist Platz für Profile der zu analysierenden Systeme. Um ein Speicherabbild auswerten zu können, benötigt Volatility Informationen zum Layout des Speichers. Ein Profil muss also immer exakt zur Kernelversion passen. Das Wiki [12] verlinkt fertige Linux-Profile und zeigt, wie Benutzer mittels »dwarfdump« die nötigen Informationen über Kerneldatenstrukturen und Debugsymbole generieren, um eigene Profile zu erzeugen.
Gut eingerichtet
Für alle Tests in diesem Artikel kommt ein fertiges Speicherabbild von [13] zum Einsatz. Nach dem Download und Auspacken der Zip-Datei finden Anwender ein Unterverzeichnis namens »linux«. Das dort enthaltene Archiv »book.zip« sollte in den zuvor angelegten Ordner »profiles« wandern – ob per »mv«, »cp« oder als Hardlink ist Geschmacksache; lediglich ein Symlink funktionierte auf dem Testrechner nicht. Der Befehl
./vol --plugin=profiles/ --info | grep -i book
sollte die folgende Ausgabe liefern:
Volatility Foundation Volatility Framework 2.6 Linuxbookx64 - A Profile for Linux book x64
Findet Volatility das Profil nicht, ist womöglich das »plugin«-Verzeichnis falsch angegeben. Wer mehrere Ordner durchsuchen möchte, trennt diese mit einem Doppelpunkt voneinander ab.
Tippfaule Anwender legen außerdem eine Konfigurationsdatei für Volatility an, was ihnen die Eingabe langer Parameter erspart und es ermöglicht, die Einstellungen später zu rekonstruieren. Das Tool erwartet seine Einrichtung entweder in »~/.volatilityrc« oder im aktuellen Verzeichnis in der Datei »volatilityrc«. Listing 1 zeigt ein Beispiel, welches das Pluginverzeichnis, ein Profil sowie ein Abbild definiert. Alternativ geben Benutzer das Image beim Aufruf hinter »-f« an:
./vol --plugin=profiles/ --profile=Linuxbookx64 -f AMF_MemorySamples/linux/linux-sample-1.bin
Die Reihenfolge der Parameter ist wichtig: Steht »–profile« vor »–plugin«, findet das Tool das Profil nicht. Wer in der Konfigurationsdatei einen Pfad zu den Profilen angibt, sollte das Profil auch benennen, denn die Kommandozeilenoptionen haben Vorrang vor der Einrichtungsdatei.
Listing 1
volatilityrc
01 [DEFAULT] 02 PLUGIN=profiles/ 03 LOCATION=file://AMF_MemorySamples/linux/linux-sample-1.bin 04 PROFILE=Linuxbookx64
Vertrauensfrage
Ein erster Blick ins Speicherabbild mit dem Befehl »./vol linux_pslist« liefert die Liste à la »ps«. Analog dazu kennt das Tool »linux_psaux«, »linux_psenv« und »linux_pstree« (Abbildung 1). Die Listen kommen aus einer (in »sched.h« definierten) Datenstruktur, in welcher der Kernel Prozesse verwaltet.
Kernelprozesse lassen sich leicht daran erkennen, dass sie keinen Adressübersetzungs-Eintrag in der Directory Table Base (DTB) haben. Doch ist diese Ausgabe vertrauenswürdig? Könnte ein Angreifer sie manipulieren? Ein einfacher Ansatz zum Tarnen findet sich in Sourcen älterer Kernelversionen in der Datei »base.c«:
res = access_process_vm(task, mm->arg_start, buffer, len, 0);
Für einen Eintrag in der Prozessliste liest der Kernel also die Umgebungsvariablen der Shell aus. Ein Prozess, der sein Environment manipuliert, könnte also unter anderem Namen auftauchen.
Da Forensiker immer davon ausgehen müssen, dass Angreifer die Ziele mindestens so gut kennen wie sie selbst, gilt es also, die ermittelte Prozessliste zu verifizieren. Dazu bietet Volatility das Kommando »linux_psscan«, das nicht die Kerneldatenstrukturen nach Prozessen durchsucht, sondern den ganzen Speicher, wo sich ein Angreifer schlechter verstecken kann. Der manuelle Abgleich von »linux_pslist« und »linux_psscan« ist aber nicht erforderlich, denn »linux_psxview« liefert eine übersichtliche Tabelle, wo Prozesse aufgetaucht sind.
Wer die Kommandos auf das Beispielimage loslässt, stellt fest, dass »linux_psscan« zahlreiche Prozesse anzeigt, die »linux_pslist« nicht findet. Das können Threads sein, die das Volatility-Plugin »linux_threads« identifizieren kann. Doch nicht immer gibt es sofort eine plausible Erklärung. Der Prozess »swapper« beispielsweise fehlt in beiden Listen, was als Teil des Schedulers – er verwaltet »idle« – sicher zulässig ist.

Abbildung 1: Die von Volatility mit dem Kommando »linux_pstree« erzeugte Ausgabe ist ähnlich der von »pstree«.
Rootkits aufspüren
Manchmal ist es hilfreich, sich zunächst einen Überblick zu verschaffen. Admins, die sich fragen, ob der Rechner mit einem Rootkit infiziert ist [14], können ebenfalls zu Volatility greifen. Die Toolsammlung hat gleich mehrere Plugins, die dazu Auskunft geben können. Der Befehl
./vol --plugin=profiles/ linux_check_idt
etwa liefert die Interrupt-Descriptor-Tabelle (Abbildung 2). Wenn hier ein Eintrag umgeleitet (Hooked) ist, spricht einiges für ein manipuliertes System. Gleiches gilt für die Exception-Handler und die Systemcalls. Genau wie »lsof« schaut »linux_lsof« nach offenen Dateien eines Prozesses. »linux_chk_tty« und »linux_keyboard_notifier« hingegen prüfen, ob ein Angreifer auf typische Weise einen Keymapper installiert hat. Volatility ermittelt zudem die ARP-Table, offene Netzwerkverbindungen, gemountete Dateisysteme oder eingesetzte Kernelmodule – es gibt für fast alles Plugins (Komplettübersicht im Wiki über Github).
Warum sollte ein Admin also zu Volatility greifen, wenn er auf der Suche nach Rootkits ist, und nicht zu einem speziellen Malware-Scanner, der die Festplatte analysiert? Ein Rootkit, dass im Speicher ist, kann die Prüfung nicht mehr stören. So erhöhen sich die Chancen, den Angreifer zu entdecken. Ist ein suspekter Prozess identifiziert, gibt »linux_proc_maps« den Speicherinhalt zur besseren Analyse aus.
Interpretationspflicht
Zum Nachweis von Nutzer-Fehlverhalten eignet sich die Analyse der Bash-History (Abbildung 3). Volatility entdeckt auch Kommandos, wenn zum Vertuschen der letzten Eingaben die Länge der History auf 0 und ihr Speicherort nach »/dev/null« verlegt wurde.
Die wahre Herausforderung beim Einsatz von Volatility ist wie bei allen Analysetools weniger der Einsatz der korrekten Parameter – viel schwieriger ist es, die Ausgaben des Programms richtig zu interpretieren. Hier hilft nur Übung und eine gute Kenntnis des Systems mit allen seinen Datenstrukturen. (jk/hej)
Infos
-
Volatility: https://www.volatilityfoundation.org
-
Volatility und VMware: https://github.com/volatilityfoundation/volatility/wiki/VMware-Snapshot-File
-
Volatility und Virtualbox: https://github.com/volatilityfoundation/volatility/wiki/Virtual-Box-Core-Dump
-
Sergei Skorobogatov, “Low temperature data remanence in static RAM”:https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-536.pdf
-
Michael Becher, Maximillian Dornseif, Christian N. Klein, “FireWire, all your memory are belong to us”:https://cansecwest.com/core05/2005-firewire-cansecwest.pdf
-
Ulf Frisk, “DMA attacking over USB-C and Thunderbolt 3”: http://blog.frizk.net/2016/10/dma-attacking-over-usb-c-and.html
-
Inception: https://github.com/carmaa/inception
-
Joe FitzPatrick, Miles Crabill, “Stupid PCIe Tricks featuring NSA Playset: PCIe”:https://www.youtube.com/watch?v=OD2Wxe4RLeU
-
Linux Memory Grabber: https://github.com/halpomeranz/lmg
-
Tobias Eggendorfer, “Haltet den Dieb!”: Linux-Magazin 10/15, S. 22
-
Johannes Stuttgen, Michael Cohen, “Anti-Forensic Resilient Memory Acquisition”:http://dfrws.org/sites/default/files/session-files/paper-anti-forensic_resilient_memory_acquisition.pdf
-
Volatility-Profile für Linux: https://github.com/volatilityfoundation/volatility/wiki/Linux
-
The Art of Memory Forensics: https://www.memoryanalysis.net/amf
-
Jürgen Quade, “Spitzel im Inneren”: Linux-Magazin 10/12, S. 34








