macOS Process Abuse
Last updated
Last updated
Lerne & übe AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Lerne & übe GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ein Prozess ist eine Instanz eines laufenden ausführbaren Programms, jedoch führen Prozesse keinen Code aus, das sind Threads. Daher sind Prozesse nur Container für laufende Threads, die den Speicher, Deskriptoren, Ports, Berechtigungen... bereitstellen.
Traditionell wurden Prozesse innerhalb anderer Prozesse (außer PID 1) durch den Aufruf von fork
gestartet, was eine exakte Kopie des aktuellen Prozesses erstellt, und dann würde der Kindprozess normalerweise execve
aufrufen, um die neue ausführbare Datei zu laden und auszuführen. Dann wurde vfork
eingeführt, um diesen Prozess schneller zu machen, ohne Speicher zu kopieren.
Dann wurde posix_spawn
eingeführt, das vfork
und execve
in einem Aufruf kombiniert und Flags akzeptiert:
POSIX_SPAWN_RESETIDS
: Setze effektive IDs auf reale IDs zurück
POSIX_SPAWN_SETPGROUP
: Setze die Prozessgruppen-Zugehörigkeit
POSUX_SPAWN_SETSIGDEF
: Setze das Standardverhalten von Signalen
POSIX_SPAWN_SETSIGMASK
: Setze die Signalmaske
POSIX_SPAWN_SETEXEC
: Exec im selben Prozess (wie execve
mit mehr Optionen)
POSIX_SPAWN_START_SUSPENDED
: Starten im angehaltenen Zustand
_POSIX_SPAWN_DISABLE_ASLR
: Starten ohne ASLR
_POSIX_SPAWN_NANO_ALLOCATOR:
Verwende den Nano-Allocator von libmalloc
_POSIX_SPAWN_ALLOW_DATA_EXEC:
Erlaube rwx
auf Datensegmenten
POSIX_SPAWN_CLOEXEC_DEFAULT
: Schließe alle Dateibeschreibungen bei exec(2) standardmäßig
_POSIX_SPAWN_HIGH_BITS_ASLR:
Randomisiere die hohen Bits des ASLR-Slides
Darüber hinaus erlaubt posix_spawn
, ein Array von posix_spawnattr
anzugeben, das einige Aspekte des gestarteten Prozesses steuert, und posix_spawn_file_actions
, um den Zustand der Deskriptoren zu ändern.
Wenn ein Prozess stirbt, sendet er den Rückgabewert an den übergeordneten Prozess (wenn der übergeordnete Prozess gestorben ist, ist der neue übergeordnete Prozess PID 1) mit dem Signal SIGCHLD
. Der übergeordnete Prozess muss diesen Wert abrufen, indem er wait4()
oder waitid()
aufruft, und bis das geschieht, bleibt das Kind in einem Zombie-Zustand, in dem es weiterhin aufgeführt ist, aber keine Ressourcen verbraucht.
PIDs, Prozessidentifikatoren, identifizieren einen einzigartigen Prozess. In XNU sind die PIDs 64 Bit und steigen monoton an und wickeln sich niemals (um Missbrauch zu vermeiden).
Prozesse können in Gruppen eingefügt werden, um die Handhabung zu erleichtern. Zum Beispiel werden Befehle in einem Shell-Skript in derselben Prozessgruppe sein, sodass es möglich ist, sie zusammen zu signalisieren, indem man beispielsweise kill verwendet.
Es ist auch möglich, Prozesse in Sitzungen zu gruppieren. Wenn ein Prozess eine Sitzung startet (setsid(2)
), werden die Kindprozesse in die Sitzung gesetzt, es sei denn, sie starten ihre eigene Sitzung.
Koalition ist eine weitere Möglichkeit, Prozesse in Darwin zu gruppieren. Ein Prozess, der einer Koalition beitritt, ermöglicht den Zugriff auf Poolressourcen, teilt ein Hauptbuch oder steht Jetsam gegenüber. Koalitionen haben unterschiedliche Rollen: Führer, XPC-Dienst, Erweiterung.
Jeder Prozess hält Berechtigungen, die seine Privilegien im System identifizieren. Jeder Prozess hat eine primäre uid
und eine primäre gid
(obwohl er mehreren Gruppen angehören kann).
Es ist auch möglich, die Benutzer- und Gruppen-ID zu ändern, wenn die Binärdatei das setuid/setgid
-Bit hat.
Es gibt mehrere Funktionen, um neue uids/gids festzulegen.
Der Systemaufruf persona
bietet ein alternatives Set von Berechtigungen. Die Annahme einer Persona übernimmt ihre uid, gid und Gruppenmitgliedschaften auf einmal. Im Quellcode ist es möglich, die Struktur zu finden:
POSIX-Threads (pthreads): macOS unterstützt POSIX-Threads (pthreads
), die Teil einer standardisierten Thread-API für C/C++ sind. Die Implementierung von pthreads in macOS befindet sich in /usr/lib/system/libsystem_pthread.dylib
, die aus dem öffentlich verfügbaren libpthread
-Projekt stammt. Diese Bibliothek bietet die notwendigen Funktionen zur Erstellung und Verwaltung von Threads.
Threads erstellen: Die Funktion pthread_create()
wird verwendet, um neue Threads zu erstellen. Intern ruft diese Funktion bsdthread_create()
auf, einen niedrigeren Systemaufruf, der spezifisch für den XNU-Kernel (den Kernel, auf dem macOS basiert) ist. Dieser Systemaufruf nimmt verschiedene Flags entgegen, die aus pthread_attr
(Attributen) abgeleitet sind und das Verhalten des Threads spezifizieren, einschließlich der Planungsrichtlinien und der Stackgröße.
Standard-Stackgröße: Die Standard-Stackgröße für neue Threads beträgt 512 KB, was für typische Operationen ausreichend ist, aber über Thread-Attribute angepasst werden kann, wenn mehr oder weniger Platz benötigt wird.
Thread-Initialisierung: Die Funktion __pthread_init()
ist während der Thread-Einrichtung entscheidend und nutzt das Argument env[]
, um Umgebungsvariablen zu parsen, die Details über den Standort und die Größe des Stacks enthalten können.
Threads beenden: Threads werden typischerweise durch den Aufruf von pthread_exit()
beendet. Diese Funktion ermöglicht es einem Thread, sauber zu beenden, notwendige Aufräumarbeiten durchzuführen und dem Thread zu erlauben, einen Rückgabewert an alle Joiner zu senden.
Thread-Aufräumung: Nach dem Aufruf von pthread_exit()
wird die Funktion pthread_terminate()
aufgerufen, die die Entfernung aller zugehörigen Thread-Strukturen behandelt. Sie gibt Mach-Thread-Ports frei (Mach ist das Kommunikationssubsystem im XNU-Kernel) und ruft bsdthread_terminate
auf, einen Systemaufruf, der die kernelseitigen Strukturen, die mit dem Thread verbunden sind, entfernt.
Um den Zugriff auf gemeinsame Ressourcen zu verwalten und Rennbedingungen zu vermeiden, bietet macOS mehrere Synchronisationsprimitive. Diese sind entscheidend in Multi-Threading-Umgebungen, um die Datenintegrität und Systemstabilität zu gewährleisten:
Mutexe:
Regulärer Mutex (Signatur: 0x4D555458): Standard-Mutex mit einem Speicherbedarf von 60 Bytes (56 Bytes für den Mutex und 4 Bytes für die Signatur).
Schneller Mutex (Signatur: 0x4d55545A): Ähnlich wie ein regulärer Mutex, aber für schnellere Operationen optimiert, ebenfalls 60 Bytes groß.
Bedingungsvariablen:
Wird verwendet, um auf das Eintreten bestimmter Bedingungen zu warten, mit einer Größe von 44 Bytes (40 Bytes plus eine 4-Byte-Signatur).
Bedingungsvariablenattribute (Signatur: 0x434e4441): Konfigurationsattribute für Bedingungsvariablen, mit einer Größe von 12 Bytes.
Once-Variable (Signatur: 0x4f4e4345):
Stellt sicher, dass ein Stück Initialisierungscode nur einmal ausgeführt wird. Ihre Größe beträgt 12 Bytes.
Lese-Schreib-Sperren:
Ermöglicht mehreren Lesern oder einem Schreiber gleichzeitig den Zugriff und erleichtert den effizienten Zugriff auf gemeinsame Daten.
Lese-Schreib-Sperre (Signatur: 0x52574c4b): Größe von 196 Bytes.
Lese-Schreib-Sperrenattribute (Signatur: 0x52574c41): Attribute für Lese-Schreib-Sperren, 20 Bytes groß.
Die letzten 4 Bytes dieser Objekte werden verwendet, um Überläufe zu erkennen.
Thread-lokale Variablen (TLV) im Kontext von Mach-O-Dateien (dem Format für ausführbare Dateien in macOS) werden verwendet, um Variablen zu deklarieren, die spezifisch für jeden Thread in einer Multi-Thread-Anwendung sind. Dies stellt sicher, dass jeder Thread seine eigene separate Instanz einer Variablen hat, was eine Möglichkeit bietet, Konflikte zu vermeiden und die Datenintegrität aufrechtzuerhalten, ohne explizite Synchronisationsmechanismen wie Mutexe zu benötigen.
In C und verwandten Sprachen können Sie eine thread-lokale Variable mit dem __thread
-Schlüsselwort deklarieren. So funktioniert es in Ihrem Beispiel:
Dieses Snippet definiert tlv_var
als eine thread-lokale Variable. Jeder Thread, der diesen Code ausführt, hat sein eigenes tlv_var
, und Änderungen, die ein Thread an tlv_var
vornimmt, wirken sich nicht auf tlv_var
in einem anderen Thread aus.
Im Mach-O-Binary sind die Daten, die mit thread-lokalen Variablen verbunden sind, in spezifischen Abschnitten organisiert:
__DATA.__thread_vars
: Dieser Abschnitt enthält die Metadaten über die thread-lokalen Variablen, wie ihre Typen und den Initialisierungsstatus.
__DATA.__thread_bss
: Dieser Abschnitt wird für thread-lokale Variablen verwendet, die nicht explizit initialisiert sind. Es ist ein Teil des Speichers, der für null-initialisierte Daten reserviert ist.
Mach-O bietet auch eine spezifische API namens tlv_atexit
, um thread-lokale Variablen zu verwalten, wenn ein Thread beendet wird. Diese API ermöglicht es Ihnen, Destruktoren zu registrieren – spezielle Funktionen, die thread-lokale Daten bereinigen, wenn ein Thread endet.
Das Verständnis von Thread-Prioritäten beinhaltet, wie das Betriebssystem entscheidet, welche Threads ausgeführt werden und wann. Diese Entscheidung wird durch das Prioritätsniveau beeinflusst, das jedem Thread zugewiesen ist. In macOS und Unix-ähnlichen Systemen wird dies mit Konzepten wie nice
, renice
und Quality of Service (QoS) Klassen gehandhabt.
Nice:
Der nice
-Wert eines Prozesses ist eine Zahl, die seine Priorität beeinflusst. Jeder Prozess hat einen nice-Wert, der von -20 (höchste Priorität) bis 19 (niedrigste Priorität) reicht. Der Standard-nice-Wert, wenn ein Prozess erstellt wird, ist typischerweise 0.
Ein niedrigerer nice-Wert (näher an -20) macht einen Prozess "egoistischer" und gibt ihm mehr CPU-Zeit im Vergleich zu anderen Prozessen mit höheren nice-Werten.
Renice:
renice
ist ein Befehl, der verwendet wird, um den nice-Wert eines bereits laufenden Prozesses zu ändern. Dies kann verwendet werden, um die Priorität von Prozessen dynamisch anzupassen, indem entweder ihre CPU-Zeit-Zuweisung basierend auf neuen nice-Werten erhöht oder verringert wird.
Zum Beispiel, wenn ein Prozess vorübergehend mehr CPU-Ressourcen benötigt, könnten Sie seinen nice-Wert mit renice
senken.
QoS-Klassen sind ein modernerer Ansatz zur Handhabung von Thread-Prioritäten, insbesondere in Systemen wie macOS, die Grand Central Dispatch (GCD) unterstützen. QoS-Klassen ermöglichen es Entwicklern, Arbeiten in verschiedene Ebenen basierend auf ihrer Wichtigkeit oder Dringlichkeit zu kategorisieren. macOS verwaltet die Thread-Priorisierung automatisch basierend auf diesen QoS-Klassen:
Benutzerinteraktiv:
Diese Klasse ist für Aufgaben, die derzeit mit dem Benutzer interagieren oder sofortige Ergebnisse erfordern, um eine gute Benutzererfahrung zu bieten. Diese Aufgaben erhalten die höchste Priorität, um die Benutzeroberfläche reaktionsschnell zu halten (z. B. Animationen oder Ereignisbehandlung).
Benutzerinitiiert:
Aufgaben, die der Benutzer initiiert und sofortige Ergebnisse erwartet, wie das Öffnen eines Dokuments oder das Klicken auf eine Schaltfläche, die Berechnungen erfordert. Diese haben eine hohe Priorität, liegen jedoch unter benutzerinteraktiven Aufgaben.
Dienstprogramm:
Diese Aufgaben sind langlaufend und zeigen typischerweise einen Fortschrittsindikator an (z. B. Dateien herunterladen, Daten importieren). Sie haben eine niedrigere Priorität als benutzerinitiierte Aufgaben und müssen nicht sofort abgeschlossen werden.
Hintergrund:
Diese Klasse ist für Aufgaben, die im Hintergrund arbeiten und für den Benutzer nicht sichtbar sind. Dazu können Aufgaben wie Indizierung, Synchronisierung oder Backups gehören. Sie haben die niedrigste Priorität und einen minimalen Einfluss auf die Systemleistung.
Durch die Verwendung von QoS-Klassen müssen Entwickler die genauen Prioritätszahlen nicht verwalten, sondern sich auf die Art der Aufgabe konzentrieren, und das System optimiert die CPU-Ressourcen entsprechend.
Darüber hinaus gibt es verschiedene Thread-Planungspolitiken, die eine Reihe von Planungsparametern spezifizieren, die der Scheduler berücksichtigen wird. Dies kann mit thread_policy_[set/get]
durchgeführt werden. Dies könnte in Race-Condition-Angriffen nützlich sein.
MacOS bietet, wie jedes andere Betriebssystem, eine Vielzahl von Methoden und Mechanismen, damit Prozesse interagieren, kommunizieren und Daten teilen. Während diese Techniken für das effiziente Funktionieren des Systems unerlässlich sind, können sie auch von Bedrohungsakteuren missbraucht werden, um böswillige Aktivitäten durchzuführen.
Bibliotheksinjektion ist eine Technik, bei der ein Angreifer einen Prozess zwingt, eine bösartige Bibliothek zu laden. Nach der Injektion läuft die Bibliothek im Kontext des Zielprozesses und gibt dem Angreifer die gleichen Berechtigungen und den gleichen Zugriff wie der Prozess.
macOS Library InjectionFunktionshooking beinhaltet das Abfangen von Funktionsaufrufen oder Nachrichten innerhalb eines Softwarecodes. Durch das Hooken von Funktionen kann ein Angreifer das Verhalten eines Prozesses ändern, sensible Daten beobachten oder sogar die Ausführungsreihenfolge kontrollieren.
macOS Function HookingInterprozesskommunikation (IPC) bezieht sich auf verschiedene Methoden, durch die separate Prozesse Daten teilen und austauschen. Während IPC für viele legitime Anwendungen grundlegend ist, kann es auch missbraucht werden, um die Prozessisolierung zu untergraben, sensible Informationen zu leaken oder unbefugte Aktionen durchzuführen.
macOS IPC - Inter Process CommunicationElectron-Anwendungen, die mit bestimmten Umgebungsvariablen ausgeführt werden, könnten anfällig für Prozessinjektionen sein:
macOS Electron Applications InjectionEs ist möglich, die Flags --load-extension
und --use-fake-ui-for-media-stream
zu verwenden, um einen Man-in-the-Browser-Angriff durchzuführen, der es ermöglicht, Tastenanschläge, Datenverkehr, Cookies zu stehlen und Skripte in Seiten einzufügen...:
NIB-Dateien definieren Benutzeroberflächenelemente (UI) und deren Interaktionen innerhalb einer Anwendung. Sie können jedoch willkürliche Befehle ausführen, und Gatekeeper stoppt nicht, dass eine bereits ausgeführte Anwendung ausgeführt wird, wenn eine NIB-Datei geändert wird. Daher könnten sie verwendet werden, um beliebige Programme willkürliche Befehle ausführen zu lassen:
macOS Dirty NIBEs ist möglich, bestimmte Java-Funktionen (wie die _JAVA_OPTS
-Umgebungsvariable) zu missbrauchen, um eine Java-Anwendung willkürlichen Code/Befehle ausführen zu lassen.
Es ist möglich, Code in .Net-Anwendungen zu injizieren, indem man die .Net-Debugging-Funktionalität missbraucht (nicht durch macOS-Schutzmaßnahmen wie Runtime-Härtung geschützt).
macOS .Net Applications InjectionÜberprüfen Sie verschiedene Optionen, um ein Perl-Skript willkürlichen Code ausführen zu lassen in:
macOS Perl Applications InjectionEs ist auch möglich, Ruby-Umgebungsvariablen zu missbrauchen, um willkürliche Skripte willkürlichen Code ausführen zu lassen:
macOS Ruby Applications InjectionWenn die Umgebungsvariable PYTHONINSPECT
gesetzt ist, wird der Python-Prozess in eine Python-CLI wechseln, sobald er beendet ist. Es ist auch möglich, PYTHONSTARTUP
zu verwenden, um ein Python-Skript anzugeben, das zu Beginn einer interaktiven Sitzung ausgeführt werden soll.
Beachten Sie jedoch, dass das PYTHONSTARTUP
-Skript nicht ausgeführt wird, wenn PYTHONINSPECT
die interaktive Sitzung erstellt.
Andere Umgebungsvariablen wie PYTHONPATH
und PYTHONHOME
könnten ebenfalls nützlich sein, um einen Python-Befehl willkürlichen Code ausführen zu lassen.
Beachten Sie, dass ausführbare Dateien, die mit pyinstaller
kompiliert wurden, diese Umgebungsvariablen nicht verwenden, auch wenn sie mit einem eingebetteten Python ausgeführt werden.
Insgesamt konnte ich keinen Weg finden, um Python willkürlichen Code durch den Missbrauch von Umgebungsvariablen auszuführen. Die meisten Leute installieren jedoch Python mit Homebrew, was Python an einem beschreibbaren Ort für den Standard-Admin-Benutzer installiert. Sie können es mit etwas wie:
Even root wird diesen Code ausführen, wenn Python ausgeführt wird.
Shield (Github) ist eine Open-Source-Anwendung, die Prozessinjektions-Aktionen erkennen und blockieren kann:
Verwendung von Umgebungsvariablen: Es wird die Anwesenheit einer der folgenden Umgebungsvariablen überwachen: DYLD_INSERT_LIBRARIES
, CFNETWORK_LIBRARY_PATH
, RAWCAMERA_BUNDLE_PATH
und ELECTRON_RUN_AS_NODE
Verwendung von task_for_pid
-Aufrufen: Um zu finden, wann ein Prozess den Task-Port eines anderen abrufen möchte, was das Injizieren von Code in den Prozess ermöglicht.
Electron-App-Parameter: Jemand kann die Befehlszeilenargumente --inspect
, --inspect-brk
und --remote-debugging-port
verwenden, um eine Electron-App im Debugging-Modus zu starten und somit Code in sie einzufügen.
Verwendung von Symlinks oder Hardlinks: Typischerweise ist der häufigste Missbrauch, einen Link mit unseren Benutzerprivilegien zu platzieren und ihn auf einen Ort mit höheren Privilegien zu verweisen. Die Erkennung ist für sowohl Hardlinks als auch Symlinks sehr einfach. Wenn der Prozess, der den Link erstellt, ein anderes Privilegieniveau als die Zieldatei hat, erstellen wir einen Alarm. Leider ist im Fall von Symlinks eine Blockierung nicht möglich, da wir keine Informationen über das Ziel des Links vor der Erstellung haben. Dies ist eine Einschränkung des EndpointSecurity-Frameworks von Apple.
In diesem Blogbeitrag finden Sie, wie es möglich ist, die Funktion task_name_for_pid
zu verwenden, um Informationen über andere Prozesse, die Code in einen Prozess injizieren, zu erhalten und dann Informationen über diesen anderen Prozess zu erhalten.
Beachten Sie, dass Sie, um diese Funktion aufzurufen, die gleiche UID wie die des Prozesses haben müssen, der ausgeführt wird, oder root sein müssen (und es gibt Informationen über den Prozess zurück, nicht einen Weg, um Code zu injizieren).
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)