Aus Linux-Magazin 03/2019

Kernel- und Treiberprogrammierung mit dem Linux-Kernel – Folge 102

© psdesign1, Fotolia

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.

Abbildung 1: Systemarchitektur des Audit-Subsystems im Kernel und Userland.

Abbildung 1: Systemarchitektur des Audit-Subsystems im Kernel und Userland.

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.

Tabelle 1

Vergleichsoperatoren für Audit-Regeln

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

Tabelle 2

Regeln in Listen einsortieren

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.

Abbildung 2: Datenflussdiagramm der Admin-Programme.

Abbildung 2: Datenflussdiagramm der Admin-Programme.

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.

Abbildung 3: Der Audit-Daemon schreibt haufenweise Informationen ins Log.

Abbildung 3: Der Audit-Daemon schreibt haufenweise Informationen ins Log.


Abbildung 4: Das Werkzeug &raquo;aureport&laquo; bereitet Audit-Records mundgerecht auf.

Abbildung 4: Das Werkzeug »aureport« bereitet Audit-Records mundgerecht auf.

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

  1. BSI, “Zertifizierte Produkte – Betriebssysteme”: https://www.bsi.bund.de/DE/Themen/ZertifizierungundAnerkennung/Produktzertifizierung/ZertifizierungnachCC/ZertifizierteProdukte/Betriebssysteme/Betriebssystem_node.html

  2. Paul Menzel, “Performance messen mit dem Kerneltool Perf”: Linux-Magazin 02/19, S. 24

  3. Quade, Kunst, “Kern-Technik” 40 – Netlink-Interface: Linux-Magazin 07/08, S. 96

  4. Florian Roth, “Best Practice Configuration”:https://gist.github.com/Neo23x0/9fe88c0c5979e017a389b90fd19ddfee

  5. Audit Interface (Dokumentation zum Linux-Kernel): https://www.kernel.org/doc/htmldocs/kernel-api/audit.html

  6. Bruno Morrison, “Analysis of the Linux Audit System”: https://www.ma.rhul.ac.uk/static/techrep/2015/RHUL-MA-2015-13.pdf

  7. Benjamin Knust, “Linux Auditing”: https://www.heinlein-support.de/sites/default/files/slac16_kernelaudit_pb_layout_final_1.pdf

Der Autor

Eva-Katharina Kunst ist seit den Anfängen von Linux Fan von Open Source. Jürgen Quade ist Professor an der Hochschule Niederrhein und führt auch für Unternehmen Schulungen zu den Themen Treiberprogrammierung und Embedded Linux durch.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben