Das Treiben gewiefter Eindringlinge muss nicht unentdeckt bleiben: Der Linux-Kernel besitzt mit dem Audit-Subsystem einen Mechanismus, der das ganze System oder einzelne Dateien und User auf neuralgische Aktivitäten hin überwacht. Wie das funktioniert und wie das Setup gelingt, erklärt die Kern-Technik.
Die Sicherheit der Enterprise-Versionen von Red Hat und Suse Linux ist nach den “Common Criteria for Information Technology Security Evaluation” durch das BSI zertifiziert [1]. Diese “Allgemeinen Kriterien” fordern eine Möglichkeit, sicherheitsrelevante Aktivitäten des Systems, zum Beispiel den Zugriff auf sensitive Dateien, gezielt zu überwachen. Zu diesem Zweck haben die Linux-Entwickler bereits 2004 ein Audit-Subsystem in den Kernel integriert (Abbildung 1).
Es überwacht für das System wichtige Ereignisse wie das Starten oder Beenden von Jobs, das Lesen und Schreiben von Dateien, Ändern von Zugriffsrechten und Besitzverhältnissen, Modifikationen an der Netzwerkkonfiguration, den Aufbau von Netzwerkverbindungen, fehlgeschlagene Authentifizierungsversuche oder den Aufruf von Systemcalls.
Das Audit-Subsystem speichert dazu den Ereignistyp, den exakten Auftrittszeitpunkt, Ursprung und Urheber des Ereignisses, das Ergebnis und die Identität aller Beteiligten.
Sensitive Ereignisse blind mitzuprotokollieren macht das System nicht per se sicherer, aber es ermöglicht dem aufmerksamen Admin eine intensive und fokussierte Überwachung, sodass er Securityvorfälle schneller identifizieren und auf sie reagieren kann. Natürlich ließe sich ein Teil auch durch Tracing [2] realisieren. Aber das Audit-Subsystem knöpft sich gezielt die sicherheitsrelevanten Bereiche vor, ist selbst vor Manipulation leidlich abgesichert und zudem für den Admin leichter zu konfigurieren.
Mit Handwerkszeug
Um diese leichte Handhabung zu gewährleisten, liefern die Kernelhacker passend zum Subsystem eine Kiste voller Userland-Werkzeuge. Mit ihnen definiert der Admin ähnlich wie bei einer Firewall Regeln und hinterlegt sie im Kernel. Als Schnittstelle zwischen Kernel und Userland-Werkzeugen dient ein Netlink-Interface [3] – was der Admin aber nicht im Details wissen muss. Wohl aber muss er, um in Besitz der Werkzeugkiste zu kommen, (unter Ubuntu 16.04) das Paket Auditd installieren:
sudo apt-get install auditd
Auch ohne Extra-Programme nutzen die meisten Linux-Distributionen das Audit-Interface bereits. So ploppen auf einem Ubuntu 16.04 im Kernel-Log (»/var/log/kern.log«) immer wieder »audit«-Meldungen auf, die beispielsweise App Armor (bei anderen Distributionen SE Linux) initiieren (Listing 1).
Mehr Meldungen erhält derjenige, der beim Booten dem Kernel die Option »audit=1« übergibt oder nachträglich über ein Kommando aus der Werkzeugkiste aktiviert: »auditctl -e 1«. Dann läuft das Audit-Subsystem zur Hochform auf und gibt sich redselig. Dass ein Angreifer das Subsystem mit »auditctl -e 0« zurück in den Silent-Mode schaltet, kann der paranoide Admin durch »auditctl -e 2« verhindern. Den Manipulationsschutz, der allein per Reboot wieder aufzuheben ist, wird er natürlich erst seiner finalen Konfiguration angedeihen lassen.
Das Konfigurationswerkzeug »auditctl« dient nicht nur der Aktivierung, Deaktivierung und Sicherung, mit ihm definiert der Admin vor allem die Überwachungsregeln – zum einen Systemcall-Regeln, zum anderen Filesystem-Regeln (auch Watches genannt). Unabhängig von ihrer Art listet »auditctl -l« alle hinterlegten Regeln auf, die Option »-s« den aktuellen Status und die Grundkonfiguration des Subsystems.
Listing 1
Audit-Meldungen im Kernel-Log
01 Dec 29 09:16:56 localhost kernel: [24105042.321337] audit: type=1400 audit(1546075016.905:41): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/sbin/mysqld" pid=17752 comm="apparmor_parser" 02 Dec 29 09:16:57 localhost kernel: [24105042.447942] audit: type=1400 audit(1546075017.033:42): apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/proc/17770/status" pid=17770 comm="mysqld" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Filesystem-Regeln
Das Audit-Subsystem protokolliert Watches, also Zugriffe auf Dateien respektive Verzeichnisse. Die Regeln dazu folgen einer Grundstruktur:
auditctl -w Path -p Permission -k Key
Der Parameter »Path« entspricht der zu überwachenden Datei. Die Option »-p« hilft die Zugriffsarten einzugrenzen, also ob das Lesen (»r«), Schreiben (»w«), Ausführen (»x«) oder Ändern der Datei-Attribute (»a«) relevant ist. Den nach »-k« angegebenen String speichert das System beim Triggern der Regel mit ab, was später bei der Analyse das Auffinden erleichtert.
Um also beispielsweise lesende und schreibende Zugriffe auf die die Passwort-Hashes enthaltende Datei »/etc/shadow« zu überwachen, dient die Regel:
auditctl -w /etc/shadow -p rw -k PASSWORDHASHES
Wer hingegen auf einem Debian (oder Derivat) diese Regel nutzt
auditctl -w /usr/bin/dpkg -p x -k PACKAGE_MANAGEMENT
findet im Protokoll jeden Dpkg-Aufruf zur Installation von Standardsoftware.
Systemcall-Regeln
Das Audit-Subsystem ist aber auch in der Lage, jeden einzelnen Systemcall zu protokollieren. Dass das riesige Datenmengen produziert und auch zu Performance-Einbußen führt, liegt nahe. Daher ist gerade in diesem Bereich eine sinnvolle Filterung unerlässlich. Grundsätzlich kann das Audit-Subsystem jeden einzelnen Systemcall, also den Dienst, den eine Applikation vom Kernel anfordert, überwachen. Dafür ist entweder die Nummer oder der Name des Systemcall relevant.
Nummern und Namen der Systemcalls lassen sich für einen PC in der Include-Datei »/usr/include/x86_64-linux-gnu/asm/unistd_64.h« herausfinden. Hier ist beispielsweise zu lesen, dass der Systemcall »read()« auf einem PC mit 64-Bit-Architektur (Biarch) die Nummer 0, »write()« die Nummer 1 und »open()« die Nummer 2 hat.
Alternativ informiert das Programm »ausyscall« aus der Audit-Werkzeugkiste mit der Option »–dump« darüber. Es gibt die Zuordnung des Systemcall-Namens zur Nummer für die Plattform aus, auf der das Kommando startet. Die grundsätzliche Struktur einer Systemcall-Regel sieht so aus:
auditctl -a Action,List -S Syscall -F Field=Value -k Key
Für »Action« sind entweder »always« oder »never« einzusetzen, für »Syscall« entweder der Name oder die Systemcall-Nummer. Die mehrfach anwendbare Option »-F« erlaubt detaillierte Filterbedingungen. Per »-F« lassen sich etwa dedizierte User-IDs oder Jobs (PIDs) filtern oder auch, ob Linux einen Systemcall erfolgreich oder fehlerbehaftet abgeschlossen hat. Dabei darf der Benutzer unterschiedliche Vergleichsoperatoren inklusive einer Bitmaske und eines Bittests benutzen (Tabelle 1).
Der Parameter »List« wählt einen Filterpunkt aus, entweder »task«, »exit«, »user« oder »exclude« (Tabelle 2), wobei die ersten beiden wohl am häufigsten eingesetzt werden. Die Regeln unter »exit« wertet das Audit-System am Ende eines Systemcalls aus. Filter unter »task« arbeitet es während des Startens einer Task (Systemcalls »fork()« respektive »clone()«) ab.
Verursacht eine Applikation Audit-Meldungen – auch das ist möglich –, arbeitet das System »user«-Regeln ab, »exclude« schließlich filtert Ereignisse aus, die das Audit-System nicht aufzeichnen soll.
So auditiert beispielsweise die nachfolgende Regel alle fehlgeschlagenen Zugriffe auf Dateien:
auditctl -a always,exit -F arch=b64 -F success=0 -S open
Die Option »-F arch=b64« legt fest, dass es sich um einen 64-Bit-Systemcall handelt. Dadurch gelingt es Auditctl, auf einem Biarch-System die richtige Systemcall-Nummer auszuwählen. Ohne die Option akzeptiert das Subsystem die Regel zwar, warnt aber auf einem 64-Bit-System den Anwender. Denn möglicherweise sind die Systemcall-Nummern für aufrufbare 32-Bit-Systemcalls ja anders.
|
Vergleichsoperator |
Name |
Operation |
|---|---|---|
|
= |
gleich |
left==right |
|
!= |
ungleich |
left!=right |
|
< |
kleiner |
left<right |
|
<= |
kleiner gleich |
left<=right |
|
> |
größer |
left>right |
|
>= |
größer gleich |
left>=right |
|
& |
Bitmaske (Und) |
left&right |
|
&= |
Bittest (Und) |
(left&right)==right |
|
Liste |
Beschreibung |
|---|---|
|
user |
Ereignisse aus dem Userland filtern |
|
task |
Ereignisse filtern, welche die Taskerzeugung betreffen |
|
watch |
Dateisystem-Events filtern |
|
exit |
Systemcall-Events filtern |
|
exclude |
Filterregeln, die das Subsystem vor der Ausgabe des Audit-Record abarbeitet |
|
entry |
veraltet, nicht mehr einsetzen |
Automatisierung
Klassischerweise liegen alle Regeln in der Datei »/etc/audit/audit.rules« (Abbildung 2). Der Audit-Daemon »auditd« liest die Datei beim Hochfahren des Systems aus und übergibt die Regeln dem Kernel über das Netlink-Interface. Aber Achtung: Der Daemon akzeptiert »audit.rules« nur, wenn sie die Besitzverhältnisse »root:root« und die Zugriffsrechte (»0400«) aufweist!
Als Regel reicht es aus, nur die Optionen und nicht das Kommando »auditctl« selbst anzugeben. Kommentarzeilen entstehen wie bei einer Skriptdatei über ein vorangestelltes »#«. Listing 2 zeigt den Auszug aus einer umfangreichen Best-Practice-Regeldatei [4]. Es ist erkennbar, dass eine Regel direkt mehrere Systemcalls abdecken kann, was die Performance signifikant erhöht.
Listing 2
Beispiel-audit.rules (Auszug)
01 [...] 02 ## Kernel parameters 03 -w /etc/sysctl.conf -p wa -k sysctl 04 05 ## Kernel module loading and unloading 06 -a always,exit -F perm=x -F auid!=-1 -F path=/sbin/insmod -k modules 07 -a always,exit -F perm=x -F auid!=-1 -F path=/sbin/modprobe -k modules 08 -a always,exit -F perm=x -F auid!=-1 -F path=/sbin/rmmod -k modules 09 -a always,exit -F arch=b64 -S finit_module -S init_module -S delete_module -F auid!=-1 -k modules 10 -a always,exit -F arch=b32 -S finit_module -S init_module -S delete_module -F auid!=-1 -k modules 11 ## Modprobe configuration 12 -w /etc/modprobe.conf -p wa -k modprobe 13 14 ## KExec usage (all actions) 15 -a always,exit -F arch=b64 -S kexec_load -k KEXEC 16 -a always,exit -F arch=b32 -S sys_kexec_load -k KEXEC 17 18 ## Special files 19 -a exit,always -F arch=b32 -S mknod -S mknodat -k specialfiles 20 -a exit,always -F arch=b64 -S mknod -S mknodat -k specialfiles 21 22 ## Mount operations (only attributable) 23 -a always,exit -F arch=b64 -S mount -S umount2 -F auid!=-1 -k mount 24 -a always,exit -F arch=b32 -S mount -S umount -S umount2 -F auid!=-1 25 -k mount
Die Lieferung verwerten
Abbildung 3 zeigt einen Ausschnitt aus der Datei »/var/log/audit/audit.log«. Unübersichtlich? Definitiv! Daher haben die Macher einige scharfkantige Werkzeuge in die Toolbox gelegt. Reports, also Zusammenfassungen wie in Abbildung 4, schreibt das Tool »aureport«.
Listing 3 zeigt beispielhaft einige Aufrufe. Zeile 1 listet alle Logins auf, Zeile 2 nur die fehlgeschlagenen und damit potenziellen Einbruchsversuche. Zeile 3 bringt alle Systemcalls des aktuellen Tages an selbigen. Weitere Anwendungsmöglichkeiten beschreiben die Manpage von »aureport« beziehungsweise der Aufruf von »aureport –help«.
Um gezielt zu suchen, hält die Toolbox »ausearch« bereit. Listing 4 zeigt zwei Anfragen. Die erste sucht nach fehlgeschlagenen Logins und die zweite grenzt die Funde auf den spezifischen User mit der UID 1000 ein. Abgesehen vom bereits erwähnten »ausyscall« bleibt – last but not least – »autrace«. Damit gelingt es, einzelne Programme oder Befehle nachzuverfolgen.
Listing 3
Aufrufe von aureport
01 aureport -l 02 aureport -l --failed 03 aureport -ts today -i -s --summary 04 aureport -ts today -u
Listing 4
Suchanfragen im Audit-Log
01 ausearch -m USER_AUTH,USER_ACCT --success no 02 ausearch --message USER_LOGIN --success no --interpret --uid 1000
Im Kernel
Das Audit-Subsystem ist tief im Kernel verankert. Entsprechend findet sich der zugehörige Quellcode sehr zentral im Linux-Quellcodeverzeichnis »kernel/«. Dort sind vor allem die Dateien »audit.c«, »auditsc.c« (“sc” für Systemcall) und »auditfilter.c« relevant. Sie implementieren die eigentliche Überwachung.
Der Code in »audit.c« exportiert darüber hinaus die Funktionen »audit_log_start()«, »audit_log_end()«, »audit_log_format()« und »audit_log()«, über die jede Kernelkomponente eigene Audit-Meldungen generieren darf. Das gilt auch für Module, die der Kernel während der Laufzeit per »insmod« oder »modprobe« nachgeladen bekommt. Das Parametrieren dieser Funktionen erfolgt ähnlich zu Dateizugriffsfunktionen: »audit_log_start()« entspricht einem »fopen()«, »audit_log_format()« dem »fwrite()« respektive »fprintf()« und »audit_log_end()« schließlich einem »fclose()«.
»audit_log_start()« gibt allerdings keinen Filepointer, sondern einen Zeiger auf ein Audit-Buffer-Objekt zurück. Dahinter verbirgt sich ein Speicherbereich, in den »audit_log_format()« die Daten ähnlich wie bei »fprintf()« formatiert ablegt. Sind alle Daten abgeschickt, sorgt »audit_log_end()« für das eigentliche Schreiben des Audit-Records.
Ob das Subsystem Audit-Nachrichten überhaupt erzeugen soll oder nicht, steuert der Kernel über die globale Variable »audit_enable«. Hat sie den Wert 1, ist das Audit-Subsystem aktiv und erwartet die Nachrichten. Daher sind Ausgabefunktionen klassischerweise über eine If-Bedingung vom Wert der Variablen abhängig gemacht.
Listing 5 zeigt, wie Kernelprogrammierer innerhalb eines Moduls Audit-Nachrichten erzeugen. Die Variablendefinitionen in den Zeilen 2 und 3 sind nicht unbedingt notwendig. Sie deuten nur an, dass Audit weitere Funktionen bereitstellt, die den Zugriff auf interne Informationen möglich machen. Eine Liste der Funktionen findet sich unter [5]. Um sie nutzen zu können, integriert der Modulprogrammierer die Headerdatei »linux/audit.h«.
Listing 5
Audit-Nachrichten im Kernel generieren
01 struct audit_buffer *ab;
02 uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current));
03 unsigned int sessionid = audit_get_sessionid(current);
04 [...]
05
06 ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_KERNEL_OTHER);
07 if (!ab)
08 return 0;
09 if (audit_enable) {
10 audit_log_format(ab, "auid=%u ses=%u" ,loginuid, sessionid);
11 audit_log_format(ab, "hello=%s", "World");
12 audit_log_end(ab);
13 }
Fallen stellen
Kernel-intern steuern zwei Variablen das Audit-Verhalten: »audit_fail« und »audit_pid«. Beide sind für die Überwachung des Userland-Teils in Form des Audit-Daemon zuständig, damit ein Angreifer das Audit-Subsystem nicht einfach stummschaltet. »audit_pid« speichert die PID des Daemon und zeigt so die Existenz eines Userland-Prozesses an.
»audit_fail« legt das Verhalten fest, wenn der Audit-Daemon nicht erreichbar ist. Bei einem Wert von 1 schickt das Subsystem seine Nachrichten per »printk()« ins Syslog. Bei 2 knockt sich das System mit einer Kernelpanic selbst aus, wenn der Audit-Daemon nicht mehr antwortet – rien ne va plus. Ist der Variableninhalt 0, interessiert es den Kernel nicht, ob der Audit-Daemon erreichbar ist.
Machtvolles Werkzeug
Das Audit-Subsystem ist ein mächtiges Überwachungswerkzeug, dessen Möglichkeiten diese Kern-Technik nur in Teilen würdigt. Bislang unerwähnt blieb zum Beispiel die Bibliothek Libaudit, mit der User-Programme sehr einfach auf das Audit-Subsystem zugreifen und mit deren Hilfe sie Audit-Nachrichten erzeugen – vorausgesetzt sie haben die Rechte dazu (»CAP_AUDIT_*«).
Nachzutragen ist auch, dass die Basiskonfiguration des Auditd in der Datei »/etc/audit/auditd.conf« erfolgt oder dass er sein Remote-Logging über einen weiteren Prozess, den »audidsp«, realisiert. Hierauf neugierig gewordene Admins und Kernelprogrammierer finden unter [6] und [7] Details.
Viele Sicherheitsmeldungen generieren, das ist für den aufmerksamen Admin also kein Problem, dabei den Überblick zu bewahren, schon eher.
Infos
-
BSI, “Zertifizierte Produkte – Betriebssysteme”: https://www.bsi.bund.de/DE/Themen/ZertifizierungundAnerkennung/Produktzertifizierung/ZertifizierungnachCC/ZertifizierteProdukte/Betriebssysteme/Betriebssystem_node.html
-
Paul Menzel, “Performance messen mit dem Kerneltool Perf”: Linux-Magazin 02/19, S. 24
-
Quade, Kunst, “Kern-Technik” 40 – Netlink-Interface: Linux-Magazin 07/08, S. 96
-
Florian Roth, “Best Practice Configuration”:https://gist.github.com/Neo23x0/9fe88c0c5979e017a389b90fd19ddfee
-
Audit Interface (Dokumentation zum Linux-Kernel): https://www.kernel.org/doc/htmldocs/kernel-api/audit.html
-
Bruno Morrison, “Analysis of the Linux Audit System”: https://www.ma.rhul.ac.uk/static/techrep/2015/RHUL-MA-2015-13.pdf
-
Benjamin Knust, “Linux Auditing”: https://www.heinlein-support.de/sites/default/files/slac16_kernelaudit_pb_layout_final_1.pdf









