Methoden zur Verbesserung der Zugriffskontrollsicherheit im Linux-Kernel

Methoden zur Verbesserung der Zugriffskontrollsicherheit im Linux-Kernel

Hintergrund

Vor einiger Zeit half unser Projektteam Kunden bei der Lösung einiger Probleme im Bereich der Betriebssystemsicherheit und betraf dabei die drei wichtigsten Betriebssystemplattformen: Windows, Linux und macOS. Egal, um welches Betriebssystem es sich handelt, es handelt sich im Wesentlichen um Software. Wenn Software zum ersten Mal entwickelt wird, kann sie die Bedürfnisse der Benutzer nicht zu 100 % erfüllen. Dasselbe gilt auch für Betriebssysteme. Um die Bedürfnisse der Benutzer so weit wie möglich zu erfüllen, müssen einige Mechanismen bereitgestellt werden, mit denen Benutzer das Betriebssystem anpassen können. Natürlich gibt es neben einigen offiziellen Mechanismen auch schwarze Magie. Die Verwendung dieser schwarzen Magie wird nicht empfohlen, kann jedoch manchmal als Referenz verwendet werden, wenn bestimmte Geschäftsszenarien vorliegen.

Allgemeine Abfang- und Filterfunktionen unter Linux

Dieser Artikel konzentriert sich auf die häufigsten Abfangvorgänge auf der Linux-Plattform:

  • Abfangen dynamischer Bibliotheken im Benutzermodus.
  • Abfangen von Systemaufrufen im Kernelmodus.
  • Stapelbares Abfangen von Dateisystemen.
  • Inline-Hook-Abfangen.
  • LSM (Linux-Sicherheitsmodule)

Dynamische Bibliotheksentführung

Das Hijacking dynamischer Bibliotheken unter Linux basiert hauptsächlich auf der Umgebungsvariable LD_PRELOAD. Die Hauptfunktion dieser Umgebungsvariable besteht darin, die Ladereihenfolge dynamischer Bibliotheken zu ändern, sodass Benutzer dieselben Funktionen selektiv in verschiedene dynamische Bibliotheken laden können. Eine unsachgemäße Verwendung kann jedoch zu schwerwiegenden Sicherheitsproblemen führen. Wir können es verwenden, um andere dynamische Funktionen im Hauptprogramm und in der dynamischen Linkbibliothek zu laden, was uns die Möglichkeit bietet, schädlichen Code in die Programme anderer Personen einzuschleusen.

Angenommen, es gibt die folgende Funktion zur Überprüfung von Benutzername und Passwort:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char passwd[] = "Passwort";
wenn (argc < 2) {
printf("Ungültiges Argument!\n");
zurückkehren;
}
wenn (!strcmp(passwd, argv[1])) {
printf("Richtiges Passwort!\n");
zurückkehren;
}
printf("Ungültiges Passwort!\n");
}

Schreiben wir ein weiteres hookStrcmp-Programm, um sicherzustellen, dass der Vergleich immer korrekt ist.

#include <stdio.h>
int strcmp(const char *s1, const char *s2)
{
/* Gibt immer 0 zurück, was bedeutet, dass die beiden Zeichenfolgen gleich sind*/
gebe 0 zurück;
}

Wenn Sie die folgenden Befehle nacheinander ausführen, wird unser Hook-Programm zuerst ausgeführt.

gcc -Wall -fPIC -shared -o hookStrcmp.so hookStrcmp.c
exportiere LD_PRELOAD="./hookStrcmp.so"

Als Ergebnis können wir feststellen, dass die von uns selbst geschriebene strcmp-Funktion zuerst aufgerufen wird. Dies ist die einfachste Form des Hijackings. Wenn Sie jedoch etwas wie geteuid/getuid/getgid hijacking und dafür sorgen, dass es 0 zurückgibt, ist dies gleichbedeutend mit der Preisgabe von Root-Berechtigungen. Aus Sicherheitsgründen ist die Umgebungsvariable LD_PRELOAD daher generell deaktiviert.

Linux-Systemaufruf-Hijacking

Kürzlich wurde festgestellt, dass der Kernel 4.4.0 über 513 Systemaufrufe enthält (von denen viele noch nie verwendet wurden). Der Zweck des System Call Hijacking besteht darin, die ursprünglichen Systemaufrufe im System zu ändern und durch unsere eigenen Programme zu ersetzen. Alle Systemaufrufe im Linux-Kernel werden in einem Kernel-Array namens sys_call_table abgelegt, und der Wert des Arrays stellt die Einstiegsadresse des Systemaufruf-Serviceprogramms dar. Der gesamte Systemaufrufprozess ist wie folgt:


Wenn ein Systemaufruf im Benutzermodus initiiert wird, gelangt er über den 80-Soft-Interrupt in den Syscall-Handler und dann in die globale Systemaufruftabelle sys_call_table, um den spezifischen Systemaufruf zu finden. Wenn wir die Adresse in diesem Array in unsere eigene Programmadresse ändern, können wir einen Systemaufruf-Hijacking erreichen. Aus Sicherheitsgründen hat der Kernel jedoch einige Einschränkungen für diesen Vorgang vorgenommen:

  • Die Symbole der sys_call_table werden nicht exportiert und können nicht direkt abgerufen werden.
  • Die Speicherseite, auf der sich die sys_call_table befindet, ist schreibgeschützt und kann nicht direkt geändert werden.

Für die beiden oben genannten Probleme lauten die Lösungen wie folgt (es gibt mehr als eine Methode):

  • Holen Sie sich die Adresse der Systemaufruftabelle: grep sys_call_table /boot/System.map-uname -r
  • Das Nur-Lese-Attribut der Seitentabelle wird durch das WP-Bit des CR0-Registers gesteuert. Die Nur-Lese-Seitentabelle kann durch Löschen dieses Bits geändert werden.
/* Seite beschreibbar machen */
int make_rw (vorzeichenlose lange Adresse)
{
vorzeichenlose int-Ebene;
pte_t *pte = lookup_address(address, &level);//Suche die Seitentabellenadresse, in der sich die virtuelle Adresse befindet pte->pte |= _PAGE_RW;//Lege die Lese- und Schreibattribute der Seitentabelle fest return 0;
}
/* Seite schreibgeschützt machen */
int make_ro (vorzeichenlose lange Adresse)
{
vorzeichenlose int-Ebene;
pte_t *pte = Suchadresse(Adresse, &Ebene);
pte->pte &= ~_PAGE_RW; //Schreibgeschütztes Attribut festlegen return 0;
}

Beginnen Sie mit dem Ersetzen von Systemaufrufen

Dieser Artikel implementiert den Systemaufruf, der dem Befehl ls entspricht. Die Systemaufrufnummer lautet _ NR _getdents.

statische int syscall_init_module(void)
{
orig_getdents = sys_call_table[__NR_getdents];
make_rw((unsigned long)sys_call_table); //Seitenattribute ändernsys_call_table[__NR_getdents] = (unsigned long *)hacked_getdents; //Neue Systemaufrufadresse festlegenmake_ro((unsigned long)sys_call_table);
gebe 0 zurück;
}

Wiederherstellung

statisches void syscall_cleanup_module(void)
{
printk(KERN_ALERT "Modul-Systemaufruf entladen.\n");
make_rw((vorzeichenloses langes)sys_call_table);
sys_call_table[__NR_getdents] = (vorzeichenloses langes *)orig_getdents;
make_ro((vorzeichenloses langes)sys_call_table);
}

Verwenden Sie Makefile zum Kompilieren, fügen Sie das Kernelmodul mit insmod ein und führen Sie dann ls aus. Dadurch wird unser Systemaufruf eingegeben. Wir können bestimmte Dateien im Hook-Code löschen, und ls zeigt diese Dateien nicht an, aber diese Dateien sind weiterhin vorhanden.

Gestapeltes Dateisystem

Linux verwendet das virtuelle Dateisystem VFS, um bestimmte Festplattendateisysteme zu vereinheitlichen und zu abstrahieren, wodurch von oben nach unten ein stapelartiger IO-Stapel entsteht. Durch die Analyse des Kernel-Quellcodes wird am Beispiel eines Lesevorgangs der folgende von oben nach unten ausgeführte Prozess ausgeführt:


Der Kernel verwendet viel objektorientierte Programmierung in Form der C-Sprache, also in Form von Funktionszeigern. Beispielsweise ist read die von vfs für Benutzer bereitgestellte Schnittstelle, und der spezifische Aufruf darunter ist die Leseoperation von ext2. Solange wir die verschiedenen von VFS bereitgestellten Schnittstellen implementieren, können wir ein Stapeldateisystem realisieren. Einige gestapelte Dateisysteme wurden in den Linux-Kernel integriert. Wenn Sie beispielsweise Ubuntu installieren, werden Sie aufgefordert, Ihr Home-Verzeichnis zu verschlüsseln. Dies ist eigentlich ein gestapeltes verschlüsseltes Dateisystem (eCryptfs). Das Prinzip ist wie folgt:


Es ist ein Stapeldateisystem implementiert, was bedeutet, dass alle Lese- und Schreibvorgänge in unser Dateisystem gelangen, sodass wir alle Daten abrufen und einige Abfang- und Filtervorgänge durchführen können.

Nachfolgend sehen Sie das einfachste Stack-Dateisystem, das ich implementiert habe. Es bietet die einfachste Möglichkeit zum Öffnen, Lesen und Schreiben von Dateien. Es ist klein, aber vollständig.

https://github.com/wangzhangjun/wzjfs

Inline-Hook

Wir wissen, dass es für eine Funktion im Kernel unmöglich ist, alle ihre Funktionen in dieser Funktion zu implementieren, sie muss ihre untergeordneten Funktionen aufrufen. Wenn diese untergeordnete Funktion den gewünschten gefilterten Informationsinhalt erhalten kann, können wir den Offset der untergeordneten Funktion in der übergeordneten Funktion durch den Offset der neuen Funktion ersetzen. Auf diese Weise springt die übergeordnete Funktion, wenn sie die untergeordnete Funktion aufruft, zur neuen Funktion und führt in der neuen Funktion eine Filterung und Übernahme des Inhalts durch. Im Prinzip können Inline-Hooks also überall einhaken, wo Sie wollen.


Bei Inline-Hooks gibt es zwei wichtige Probleme:

  • So finden Sie den Hakenpunkt.
  • So fügen Sie einen Hook-Funktionseintrag ein.

Zur ersten Frage:

Sie müssen über einige Erfahrung mit Kernel-Quellcode verfügen. Für den Lesevorgang lautet der Quellcode beispielsweise wie folgt:


Wenn hier der Lesesystemaufruf initiiert wird, wird sys read aufgerufen und die vfs-Lesefunktion wird in sys read aufgerufen. Die Parameter von vfs read enthalten die Informationen, die wir filtern müssen, sodass wir vfs_read als Hook-Punkt verwenden können.

Zur zweiten Frage:

Wie haken Sie ein? Hier sind zwei Methoden:

Die erste Methode besteht darin, direkt eine binäre Ersetzung durchzuführen und den Operanden der Aufrufanweisung durch die Adresse der Hook-Funktion zu ersetzen.


Die zweite Methode: der vom Linux-Kernel bereitgestellte Kprobes-Mechanismus.

Das Prinzip besteht darin, den Maschinencode von int 3 (x86) am Hook-Punkt einzufügen, sodass die CPU, wenn sie hier ausgeführt wird, das Sig-Trap-Signal auslöst, und dann die benutzerdefinierte Hook-Funktion in die Rückruffunktion von Sig Trap einzufügen, um den Zweck des Auslösens der Hook-Funktion zu erreichen. Dies ist eigentlich das Prinzip des Debuggers.

LSM

LSM ist die Abkürzung für Linux Secrity Module, was so viel bedeutet wie Linux-Sicherheitsmodul. Es handelt sich um ein allgemeines Linux-Sicherheitsframework mit den Merkmalen hoher Effizienz, Einfachheit und Benutzerfreundlichkeit. Und so funktioniert es:

LSM

Die folgenden Arbeiten werden im Kernel ausgeführt:

  • Fügen Sie bestimmten Kernel-Datenstrukturen Sicherheitsdomänen hinzu.
  • Fügen Sie an verschiedenen kritischen Punkten im Kernel-Quellcode Aufrufe von Sicherheits-Hook-Funktionen ein.
  • Fügen Sie einen allgemeinen Sicherheitssystemaufruf hinzu.
  • Es werden Funktionen bereitgestellt, die es Kernelmodulen ermöglichen, sich als Sicherheitsmodule zu registrieren oder deren Registrierung aufzuheben.
  • Der Großteil der Funktionslogik wird aus Skalierbarkeitsgründen in ein optionales Sicherheitsmodul migriert.

Anwendbare Szenarien

Die oben genannten Hook-Methoden haben unterschiedliche Anwendungsszenarien.

  • Die Entführung dynamischer Bibliotheken ist nicht vollständig. Die entführten Informationen entsprechen möglicherweise nicht unseren Anforderungen. Es ist auch möglich, dass sie vor Ihnen von jemand anderem entführt wurden. Sobald LD_PRELOAD deaktiviert ist, wird es ungültig.
  • Systemaufruf-Hijacking: Die entführten Informationen entsprechen möglicherweise nicht unseren Anforderungen. Beispielsweise können wir die Dateistruktur nicht abrufen, den absoluten Pfad der Datei nicht abrufen usw.
  • Gestapelte Dateisysteme sind auf Mount angewiesen und erfordern möglicherweise einen Systemneustart.
  • Inline-Hooks sind sehr flexibel und können nach Belieben eingebunden werden. Sie werden sofort wirksam, ohne dass ein Neustart erforderlich ist. Allerdings ist die Kompatibilität zwischen verschiedenen Kernel-Versionen schlecht. Sobald einige Funktionen geändert werden, wird der Hook ungültig.
  • LSM, in früheren Kerneln durfte nur ein LSM-Kernelmodul geladen werden. Wenn beispielsweise SELinux geladen war, konnten andere LSM-Module nicht geladen werden. Dieses Problem besteht in der neuesten Kernelversion nicht.

Zusammenfassen

Aus Platzgründen wird in diesem Artikel nur die Abfangtechnologie unter Linux vorgestellt. Später werden wir die Gelegenheit haben, die Abfangtechnologie unter Windows und macOS zu besprechen. Tatsächlich sind ähnliche Audit-Hooks eine zwingende Anforderung in jedem System, nicht nur im Kernel. Wir können beobachten, dass immer mehr VMs und Runtimes, sogar viele Webkomponenten und Front-End-Anwendungen, flexiblere Hook-Methoden bereitstellen. Dies ist die gängigste Lösung unter den beiden großen Sicherheitstrends Transparenz und Echtzeitleistung.

Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Wenn Sie Fragen haben, können Sie eine Nachricht hinterlassen. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM.

Das könnte Sie auch interessieren:
  • Zusammenfassung des allgemeinen Lernens verknüpfter Listen im Linux-Kernel
  • Detaillierte Erläuterung der Beispiele für Kernel-Linked-List in Linux
  • Implementierungsprozess der verknüpften Liste im Linux-Kernel
  • Hinweise zum Proc-Dateisystem des Linux-Kernel-Gerätetreibers
  • Hinweise zum erweiterten Zeichengerätetreiber des Linux-Kernel-Gerätetreibers
  • Linux-Kernel-Gerätetreiber. Zusammenfassung der Hinweise zum Lademechanismus von Linux-Kernelmodulen.
  • Hinweise zur Adresszuordnung von Gerätetreibern im Linux-Kernel
  • Gerätetreiber des Linux-Kernels – Zusammenfassung der grundlegenden Hinweise zum Linux-Kernel
  • Implementierung und Analyse des Linux-Kernel- und Benutzerbereichs
  • Detaillierte Erläuterung des Auslöse- und Ausführungszeitpunkts der Linux-Kernel-Prozessplanungsfunktion schedule()
  • Hinweise zur Verwendung der verknüpften Liste des Linux-Kernel-Gerätetreibers

<<:  my.cnf-Parameterkonfiguration zur Optimierung der InnoDB-Engine-Leistung

>>:  WeChat-Miniprogramme ermöglichen nahtloses Scrollen

Artikel empfehlen

Detaillierte Erläuterung des Shared-Memory-Mechanismus von Nginx

Der gemeinsam genutzte Speicher von Nginx ist ein...

Lösen Sie das MySQL 5.7.9 Version sql_mode=only_full_group_by Problem

MySQL 5.7.9 Version sql_mode=only_full_group_by P...

So verwenden Sie die Typerweiterung ohne Typingscript

Vorwort Aufgrund der schwachen Typisierung von JS...

Beispielcode zur Implementierung des Seiten-Cachings im Vue-Mobilprojekt

Hintergrund Auf Mobilgeräten ist das Caching zwis...

Eine kurze Diskussion zur Verwendung von React.FC und React.Component

Inhaltsverzeichnis 1. Reagieren.FC<> 2. Kla...

So konfigurieren Sie mehrere Tomcats mit Nginx-Lastausgleich unter Linux

Die Methoden zur Installation von Nginx und mehre...

Tutorial zu HTML-Tabellen-Tags (8): Hintergrundbild-Attribut BACKGROUND

Legen Sie ein Hintergrundbild für die Tabelle fes...

Wie wirkt sich der zusammengesetzte Index von MySQL aus?

Inhaltsverzeichnis Hintergrund Zusammengesetzte I...

Die HTML-Tag-ID kann eine Variable sein

<table id=" <%=var1%>">, der...

HTML Tutorial: Sammlung häufig verwendeter HTML-Tags (4)

Verwandte Artikel: Anfänger lernen einige HTML-Ta...