0. Technische Aspekte von Mac OS X Inhaltsverzeichnis

0.3 Einordnung von mach_override und mach_inject

Dynamic Mach overriding, dynamisches Überschreiben und Code Injection: Warum ein Angreifer davon nichts hat.

Stichwort-Liste

0.3.1 Entstehung

Im Jahr 2001 dachten einige Mac-Entwickler-Veteranen darüber nach, wie man wohl bei Mac OS X etwas den Systemerweiterungen von System 7-9 vergleichbares schaffen könnte, nämlich ausgewählte Verhaltensweisen von System- und Anwendungsprogrammen dynamisch zu überschreiben. Dynamisch bedeutet dabei, daß die Programme nur im Hauptspeicher und nicht auf der Festplatte verändert werden sollen.

Im Juni 2003 stellte der Mac-Entwickler mit dem schönen Namen Jonathan Wolfgang von Rentzsch, Rufname Wolf, eine Lösung dazu auf der MacHack 2003 vor, die dort zum besten Konferenz-Beitrag gewählt wurde.

0.3.2 Verbreiteter Einsatz

Seine Lösung ist auch als Programmquelltext frei verfügbar und inzwischen weit verbreitet in vielen Mac-Programmen enthalten, beispielsweise in:

0.3.3 Definitionen

Um festzulegen, was gemeint ist, einige Definitionen, auf die ich mich weiter unten beziehe.

Ein Programm ist eine ausführbare Datei auf der Festplatte.

Ein Prozeß ist ein Programm, was gestartet wurde, sich im Hauptspeicher befindet und vom Prozessor ausgeführt wird. Also ein laufendes Programm.

Ein Thread ist ein Teil eines Prozesses. Ein Thread besteht aus einer Reihe von Anweisungen, die eine bestimmte Teilaufgabe erledigen. Ein Thread kann zeitgleich mit anderen Threads ausgeführt werden. Ein Prozeß besteht aus mindestens einem Thread.

0.3.4 Funktionsweise

Das dynamische Überschreiben besteht aus zwei Teilen.

Der mach_override-Teil ermöglicht es, eine vorhandene Funktion mit einer neuen Funktion zu ersetzen oder zu ergänzen. Das klappt sowohl mit Funktionen, die das System anbietet, als auch mit solchen, die man in eigenen Programmen definiert.

Der mach_inject-Teil erlaubt es einem Prozeß, Programm-Code zu laden und in einem anderen Prozeß auszuführen. Das passiert zur Laufzeit.

Man kann also Code schreiben und mit Hilfe von mach_inject in ein anderes Programm einschleusen, das gerade läuft. Und mit mach_override kann man dann eine Funktion des Wirtsprogrammes verändern, indem der eingeschleuste Code diese Funktion überschreibt oder ergänzt.

0.3.5 Sicherheit

Das klingt zuerst alles ziemlich erschreckend, aber das ist es nicht, denn mach_inject benutzt eine Programmbibliothek, eine API, die die Grenzen zwischen den Benutzern des Betriebssystems respektiert; insbesondere Grenzen zwischen den Programmen und Prozessen, die unter den jeweiligen Benutzern ausgeführt werden. Ein Prozeß, der kein System-Prozeß ist (System bedeutet root), kann zum Beispiel nicht mit Hilfe von mach_inject Code in einen System-Prozeß einschleusen. Und ein Benutzer kann auch nicht die Prozesse eines anderen Benutzers beeinflussen hiermit. Allein seine eigenen Prozesse sind als Ziel für jeden Benutzer möglich. Nur root hat Zugriff auf alle Prozesse. In Leopard werden explizit POSIX-Rechte überprüft. Trotz Unterschieden im Detail bei den verschiedenen Versionen des Betriebssystems galt immer: Man kann maximal das tun, was die UNIX-Grenzen erlauben.

Es ist vollkommen legitim, daß ein Benutzer Zugriff auf seine eigenen Prozesse und deren Speicher hat. In diesem Fall verändert er innherhalb eines seiner Prozesse den Sprung zu einer Funktion so, daß der Sprung zu einer anderen Stelle im Programm erfolgt. An dieser anderen Stelle hat er zuvor zusätzliche Programmbefehle eingefügt, die nun ausgeführt werden. Um dies zu tun, bedient man sich der für auf dem jeweiligen Betriebssystem für Speicherzugriffe zuständigen API.

0.3.6 Umsetzung

Wie dynamisches Überschreiben vorgeht.

An verschiedenen Stellen dieses Vorgehens müssen Speicherbereiche angefordert werden, beschreibbar und ausführbar gemacht werden. Das wird mit Hilfe der Mach-Programmbibliothek (API) möglich, die für die Verwaltung von Speicher und Prozessen zuständig ist als Teil des XNU -Kernels von Mac OS X.

0.3.6.1 mach_override

0.3.6.2 mach_inject

Einfach formuliert funktioniert dies mit Hilfe der Mach-Programmbibliothek (siehe oben) so:

Durch die kombinierte Nutzung von mach_override und mach_inject kann man neue Möglichkeiten (Features) in vorhandene Programme einführen. Dazu nutzt man die beiden Pakete, um Code mit den gewünschten Features einzuschleusen. Der eingefügte neue Thread führt das Überschreiben durch.

0.3.6.3 Weitere technische Details

Grundsätzlich verwendet das dynamische Überschreiben schlichtes C und Maschinensprache. Im Grunde könnte man auch damit zufrieden sein, alle neuen Anweisungen an der Stelle abzulegen, wohin man den Programmfluß aufgrund des ausgewechselten Sprungbefehls umgeleitet hat. Diese Umleitung ist meist zweistufig, denn man fügt an der Zielstelle des ersten Sprungs noch einen weiteren Sprung ein, um nicht bezüglich der Sprungadresse eingeschränkt zu sein auf das, was die Ersetzung des originalen absoluten Sprunges ermöglicht.

Einzige Bedingung beim Umleiten (Überschreiben) ist, daß hierfür ein absoluter Sprung in Maschinensprache ersetzt wird. Alles Weitere baut darauf auf. Ob die Funktion oder Methode, die zu dem ersetzten Sprung gehört, C oder Objective C ist, ist für die Machbarkeit egal. Handelt es sich jedoch um Objective C, dann macht es das Leben einfacher, weil das eingeschleuste Programmfragment (Ziel der Umleitung) dann zusätzlich noch Objective C Categories zum Ersetzen von weiteren Methoden zur Laufzeit oder Objective C Posing zum Ersetzen von Klassen zur Laufzeit verwenden kann, indem es ein entsprechendes Objective C-Programm-Bündel lädt.

Handelt es sich jedoch um normales C, dann kann man zwar auch noch weitere Programmteile nachladen, was aber die gleiche Wirkung hat, als hätte man gleich mehr an die Umleitungs-Stelle eingeschleust. C ermöglicht nicht Funktionen zur Laufzeit umzudefinieren. Hier beschränkt sich die Veränderung dann auf den einen auf Maschinen-Befehl-Ebene im Speicher umgeleiteten Funktions-Aufruf.

Der Unterschied bei den Sprachen liegt also darin, daß man im Falle von C nur die eine Funktion überschreiben (oder ergänzen) kann und der eingeschleuste Code per CFBundle weiteren Code laden kann, und daß im Falle von Objective C der per NSBundle nachgeladene Code zusätzliche eigene Überschreibungen vornehmen kann, weil Objective C das grundsätzlich ermöglicht.

Es ist nicht unbedingt notwendig, eine Objective C-Methode als Ziel zu nutzen, aber dies erleichtert die Arbeit, weil man zusätzliche Programmteile einfacher laden und mehr Methoden ersetzen kann. Wenn man ein Cocoa-Programm als Ziel hat, ermöglicht dies mehr als nur die eine Funktion (mit dem absoluten Sprung) dynamisch zu überschreiben. Der Unterschied zur normalen Cocoa-Programmierung besteht dann hier darin, daß das Nachladen nicht schon bei Programmerstellung vorgesehen war. Dynamisches Laden und Ersetzen zur Laufzeit ist jedoch normal bei Objective C. Es eröffnet dem eingeschleusten Code halt komfortablere Möglichkeiten für zusätzliche dynamische Änderungen. Die System-Einstellungen als prominentestes Beispiel in Mac OS X laden ihre Module dynamisch, also zur Laufzeit, wenn die entsprechende Einstellung angeklickt wird, nach.

0.3.6.4 Quellen

Vergleiche dazu folgende Quellen:

Scott Knaster: Hacking Mac OS X Tiger, Kapitel 22 (geschrieben von Jonathan Rentzsch)

Behind the Red Shed, with Jonathan 'The Wolf' Rentzsch

Dynamically Overriding Mac OS X

0.3.7 Panikmache und Geltungsdrang

Ende 2004 bis Anfang 2005 (also 1,5 Jahre nach Veröffentlichung dieser Technik) gab es Vorträge, Folien, Interviews und Hofhaltung in Foren durch Angelo Laub und andere selbsternannte "Hacker", die an dieser Technik und ihrer Entwicklung vollkommen unbeteiligt waren. Sie verbreiteten diverse Unrichtigkeiten über diese Technik und wollten mit Hilfe der dadurch erzeugten Besorgnis Aufmerksamkeit auf sich lenken.

Deren sachlich falsche Aussagen bedürfen einer Richtigstellung.

0.3.7.1 Richtigstellungen

Angeblich sei mach_inject eine Schwachstelle für Mac OS X. Angelo glaubte, daß Schadprogramme damit das System negativ beeinflussen können, was allerdings nicht so ist, wegen der oben genannten Grenzen zwischen den Benutzerprozessen und im speziellen den Systemprozessen. Er glaubte, es seien keine Privilegien notwendig. Da irrte er sich im selben Punkt nochmals, da man nur jeweils seine eigenen Prozesse (sprich: die unter dem eigenen Benutzer laufenden Programme) beeinflussen kann. Und auch in diesem Fall prüft Mac OS X selbstverständlich, ob die Privilegien ausreichen für den Zugriff, wie ich weiter unten anhand des Betriebssystem-Quelltextes erläutere.

Er behauptete weiterhin, damit sei ein Rootkit ohne root machbar. Das würde bedeuten, man könnte Systemprozesse, ohne selbst root zu sein, beeinflussen. Wie oben beschrieben ist das mit mach_inject aufgrund der zugrundeliegenden Programmbibliothek und der Benutzergrenzen unter Unix nicht möglich.

Auch ein sogenanntes "Userland"-rootkit ist damit nicht machbar, weil User- oder Admin-Rechte zum Nutzen der benötigten Funktion nicht ausreichen. Auf die Einzelheiten gehe ich weiter unten ein.

Ebensowenig bietet mach_inject eine freundliche Umgebung für Viren. Ein Virus hängt sich immer an eine Wirtsdatei. Mit mach_inject kann man jedoch keine Dateien ändern. Denkbar ist allenfalls ein Trojaner, der das Verhalten eines unter demselben Benutzer laufenden Programms ändert. Dazu müssen beide Programme zeitgleich unter demselben Benutzer laufen. Der Spuk ist jedoch in dem Moment vorbei, wo das Opferprogramm neu gestartet wird.

Genauso falsch ist seine Behauptung, ein Angreifer könne damit Systemprogramme zur Laufzeit negativ beeinflussen. Dazu müßte der Angreifer bereits root sein, denn sonst kann er auch mit mach_inject keinen Systemprozeß ändern.

Er behauptete, man könne beliebige C-Funktionen zur Laufzeit damit überschreiben. Das ist vollkommen falsch. Man kann damit nur Funktionen eigener Prozesse (nicht beliebiger Prozesse) überschreiben, die im besten Fall als Objective C-Funktion verfügbar sind. Siehe oben.

Nach seinem Vortrag wurde er gefragt, ob man so auch Code in eine Shared Library einschleusen könnte. Er wußte darauf keine Antwort und sagte, man solle es halt ausprobieren. Mich hat sein Unwissen über Mac OS X dann doch erstaunt. Es ist nämlich so, daß jeder Prozeß eine private Kopie der Shared Library bekommt, sobald der Prozeß eine Speicherseite der in seinen Adreßraum eingeblendeten Bibliothek beschreiben will. Die anderen Prozesse arbeiten hingegen weiter mit der originalen schreib-geschützten Version der gemeinsamen Bibliothek im Speicher. Dieser Mechanismus nennt sich copy-on-write und ist ein bekanntes übliches Verhalten.

Woher kamen seine Fehleinschätzungen und Wissenslücken? Es mag daran liegen, daß Angelo Laub nach eigenen Angaben zu der Zeit Student der Physik, Mathematik und Astronomie war. Also fachfremd. Man sollte keine reißerischen Vorträge über Themen halten, die man nicht ausreichend beherrscht. Damit erspart man sich und anderen grundlose Panik. Allerdings muß man dann auch auf die öffentliche Aufmerksamkeit verzichten. Für mich fallen selbsternannte "Hacker", die Techniken von anderen Leuten ohne tieferes Verständnis verwenden, eher unter den Begriff "script kiddy".

In seinem Papier schrieb Angelo ferner, daß die Sicherheitsauswirkungen von mach_inject noch erforscht werden müßten. Korrekt ist, daß sie von ihm noch nicht umfassend erforscht waren zu dem Zeitpunkt, was ihm Raum gab zu haltlosen Spekulationen, die er sich und uns hätte ersparen können, wenn er das Thema genauer betrachtet und durchdacht hätte.

0.3.7.2 Nebennotiz: Weitere Fehlmeldungen

Es war nicht das erste Mal, daß Angelo Fehlmeldungen veröffentlichte. In einem Vortrag (und zugehöriger Dokumentation) über die "Unsicherheiten in Mac OS X" behauptete er, es wäre möglich, die graphische Oberfläche eines am Rechner arbeitenden Benutzers zu steuern, indem man sich als Normalbenutzer per SSH verbindet und dann ein entsprechendes Skript zur Oberflächensteuerung aufruft. Das funktioniert definitiv nicht; man bekommt bei dem Versuch die Fehlermeldung kCGErrorRangeCheck : Window Server communications from outside of session allowed for root and console user only
INIT_Processeses(), could not establish the default connection to the WindowServer.Abort trap
.

Zum Zeitpunkt seiner Behauptung war 10.3.6 aktuell. Der unerlaubte Übergriff wird jedoch auch von 10.3.0 unterbunden ebenso wie von 10.4.x und späteren Versionen.

Das Skript sollte die Benutzungs-Oberfläche eines angemeldeten Administrators steuern und automatisch einen weiteren Administrator anlegen, dessen Paßwort definieren und sich dann als dieser anmelden. Das Skript funktioniert nur, wenn es der Administrator selbst ausführt. Heutzutage würde selbst das nicht mehr klappen, weil die wichtigsten Systemeinstellungen nur nach erneuter Bestätigung durch einen Administrator überhaupt zugänglich werden.

Im der gleichen Dokumentation behauptet er, Firewire-Geräte hätten per DMA Zugriff auf den gesamten virtuellen Speicher. Tatsächlich haben sie Zugriff auf den physikalischen Speicher, aber nicht auf den virtuellen. DMA-Controller kennen nämlich keine virtuellen Adressen, sondern nur physikalische. Sie lesen und schreiben direkt das RAM.

0.3.7.3 Schlecht informiert bei "uninformed"

Der "Nemo" (lateinisch: niemand) von "uninformed" machten diesbezüglich ihrem Seiten-Namen ebenfalls alle Ehre: Er bemerkt, daß man zwar ein Programm hinsichtlich seiner Dateizugriffe mit chroot einschränken kann, diese Schranke jedoch nicht für andere Prozesse des Benutzers gilt. Kommuniziert nun das "eingeschränkte" Programm mit einem nicht-eingeschränkten, dann kann die Schranke umfahren werden, indem das andere Programm das tut, was das eingeschränkte nicht tuen könnte. Diese Kommunikation kann beliebig erfolgen, auch (umständlich) über mach_inject. Es wird hierdurch keine neue Möglichkeit eröffnet, sondern nur ein weiterer Weg dorthin.

In seinem Beispiel startet er in irgendeinem anderen Prozeß des gleichen Benutzers mit Hilfe von mach_inject eine shell, die auf Kommandos per Netzwerk wartet. Die shell ist eine Bind Shell aus dem Metasploit-Framework. Er kombiniert also zwei vorhandene Techniken.

Der mit chroot eingeschränkte Prozeß läßt dann per Netzwerk-Kommunikation diese shell die gewünschten Arbeiten verrichten, die er selbst nicht erledigen kann.

Der andere Diskussions-Punkt bei "uninformed" war, daß man mit mach_inject natürlich auch Variablen im eigenen Speicher ändern kann. Ihre Bedenken bestehen darin, daß root seine System-Variablen nun nicht nur über die üblichen Funktionsaufrufe verändern kann, sondern auch (umständlich) über mach_inject. Sie übersehen dabei, daß der einzig wichtige Punkt darin besteht, daß eben nur root diese Werte ändern kann und darf. Ob er das nun mit der einen oder anderen Funktion durchführt ist ziemlich belanglos hinsichtlich Sicherheit. Es ist wieder nur ein weiterer Weg um legitime Dinge zu tun. Wie sie anmerken, wäre auch eine Kernel-Extension ein möglicher weiterer Weg.

Das entspricht ungefähr dem Folgendem: Man hat das Recht, auf eine Datei oder eine Variable zuzugreifen. Das kann jedoch auf verschiedenen Wegen geschehen. Drei Wege sind offensichtlich und gut bekannt. Ein vierter Weg ist nur echten Kennern des Systems geläufig. Sie ereifern sich nun, daß dieser vierte Weg die anderen Wege "umgehen" kann, geben jedoch zu, daß die anderen drei sich auch gegenseitig umgehen. Keiner der Wege ermöglicht jedoch Unberechtigten den Zugriff auf die Datei oder die Variable. Wer hier trotzdem Sicherheitsrisiken reindeutet, sollte als Sicherheits-Experte bitte nicht mehr hausieren gehen.

Die "Uniformierten" liessen noch einen weiteren zentralen Punkt unbeachtet liegen, weil er ihren Phantasien im Wege wäre: Der task_for_pid()-Befehl, der einem die Kontrolle über einen Prozeß geben kann, tut dies nur, wenn es den UNIX-Rechten gemäßt erlaubt wäre, denn er prüft diese (und mehr), siehe unten. Die Jungs von "uninformed" verkürzen das jedoch unzulässiger Weise auf "man könne damit die UNIX-Rechte umgehen". Sie lassen weg, daß man das nur kann, wenn man die Prüfung des Befehls besteht. Und der prüft eben auch die UNIX-Rechte. Man kann damit also die UNIX-Rechte nur "umgehen", wenn man aufgrund eben dieser UNIX-Rechte die Erlaubnis dazu bekommt. Oder anders ausgedrückt: Man kann die UNIX-Rechte damit eben doch nicht umgehen. Aber solche Feinheiten würde ihre sarkastischen Bemerkungen über das System ja als puren Unfug entlarven.

0.3.7.4 Schlagzeile oder Wahrheit?

Anhand der Beispiele "Angelo Laub" und "Nemo" ("uninformed") kann man leider feststellen, daß eine Unsitte auch vor selbsternannten "Hackern" nicht Halt macht: Schlagzeilen und Publicity über gründliche Recherche und die volle Wahrheit zu stellen. Die wahren Hacker sind jedoch Leute wie "Wolf", der diese hier diskutierte Technik entwickelt hat.

0.3.8 Mit mach_inject verwandte Techniken

Der Application Enhancer von Unsanity geht praktisch den gleichen technischen Weg.

Der Application Enhancer lädt Module, die ausführbaren Code enthalten, in laufende Programme, um deren Verhalten anzupassen. Auch dieser arbeitet auf Prozeßebene: Er greift wie mach_inject auf den Speicherbereich zu, der für einen bestimmten Prozeß (ein laufendes Programm) bereitgestellt wurde.

Beide, der Application Enhancer und mach_inject, stellen eine Programmbibliothek bereit, mit der man neue Programmfunktionalitäten in bestehende laufende Programme einschleusen kann. Das, was eingeschleust wird, hat bei mach_inject keinen besonderen Namen und beim Application Enhancer wird es ein Modul genannt. In beiden Varianten kann schlechter eingeschleuster Code zum Absturz des Zielprogrammes führen.

Es gibt wie bei mach_inject sehr viele Programme und Module, die den Application Enhancer verwenden, um ihr Ziel zu erreichen. Diese Techniken, um laufende Programme zu beeinflussen, sind also alles andere als neu, sondern seit Jahren weit verbreitet im Einsatz.

0.3.9 Zwischenzeitlich deaktiviert

Mit der Einführung der Intel-basierten Macintosh-Rechner war das Betriebssystem für diese Prozessoren anders und mit ihm war mach_inject unter Standard-Bedingungen nicht mehr möglich.

Der Grund liegt darin, daß mach_inject den Befehl task_for_pid() verwenden muß, um den gewünschten Prozeß verändern zu können. Diesen Befehl kann nur root oder ein Mitglied der procmod-Gruppe, in der standardmäßig nur root ist, verwenden. Verwenden bedeutet, daß anderenfalls der Aufruf dieser Funktion nicht die gewünschte task (den Zielprozeß) zurückgibt, sondern einen Fehler.

Vergleiche dazu folgende Quellen:

Amit Singh: Mac OS X Internals - A Systems Approach, Seite 899.

Apple Developer Connection: Mach Processes: The Task for PID Function

Für den Befehl task_for_pid() gibt es eine Sicherheitseinstellung, deren Zustand mit der Variable kern.tfp.policy des Betriebssystemkerns festgelegt wird. Man kann deren aktuellen Zustandswert ansehen mit sysctl kern.tfp.policy und setzen mit beispielsweise sudo sysctl -w kern.tfp.policy=2.

Es gibt drei verschiedene Zustände für kern.tfp.policy, die bestimmen, wer task_for_pid() benutzen kann:

Vergleiche dazu folgende Quellen:

Quelltext von Mac OS X 10.4.9 Tiger für Intel sysctl.h

Quelltext von Mac OS X 10.4.9 Tiger für Intel vm_unix.c

0.3.10 Leopard, Version 10.5

Die PowerPC-Version von Mac OS X hat diese Einschränkungsmöglichkeit erst mit der Version 10.5 bekommen. Seit 10.5 gibt es keine zwei prozessorspezifischen Varianten des Betriebssystems mehr sondern eine gemeinsame.

Generell ist es nicht mehr möglich, daß der kernel-Prozeß diese Funktion verwendet. Das führt umgehend zu einer Fehlerrückgabe.

Bei 10.5 wurden die möglichen Zustände für kern.tfp.policy geändert. Den ersten gibt es noch, die anderen beiden sind beseitigt worden. Neu hinzugekommen ist KERN_TFP_POLICY_DEFAULT (2), welches der Standardwert ist.

Mit 10.5 "Leopard" wurde der zusätzliche Befehl task_for_pid_posix_check eingeführt. Diese Funktion wird von task_for_pid aufgerufen, um einige Extraprüfungen für POSIX zu machen:

  1. Wenn der aufrufende Prozeß unter root läuft, dann ist die Überprüfung bestanden.
  2. Wenn der aufrufende Prozeß und der Zielprozeß identisch sind, dann ist die Überprüfung bestanden.
  3. Falls KERN_TFP_POLICY_DENY (0) aktiv ist und die obigen beiden Bedingungen nicht zutrafen, dann ist die Überprüfung jetzt fehlgeschlagen, ansonsten wird weiter geprüft:
  4. Wenn die (effektive) Benutzer-Identität euid des aufrufenden Prozesses die gleiche ist wie die (reale, effektive und gesicherte) Benutzer-Identität des Zielprozesses und die Prozeßgruppe des Zielprozesses eine Untermenge der Prozeßgruppe des aufrufenden Prozesses ist und die Berechtigungsnachweise (credentials) des Zielprozesses nicht ausgetauscht wurden, dann ist die Überprüfung bestanden, ansonsten ist sie fehlgeschlagen.

Wenn die obige Prüfung fehlgeschlagen ist, dann gibt die Funktion einen Fehler zurück. Ansonsten wird weitergeprüft:

Anscheinend wird danach noch wie in 10.4 auch auf Mach-Ebene die Zugriffsberechtigung auf den Zielprozeß überprüft und gegebenenfalls auch ein Fehler zurückgegeben. Dafür werden die Funktionen check_task_access und mac_proc_check_get_task aufgerufen, die diese Aufgabe nun wohl übernommen haben. Erstere befragt den task access server. Die letztere befragt das Mandatory Access Control Framework (Code Signing, Sandboxing), das neu in Leopard hinzukam.

Vergleiche dazu folgende Quellen:

Quelltext von Mac OS X 10.5 Leopard sysctl.h

Quelltext von Mac OS X 10.5 Leopard vm_unix.c

Die Zugehörigkeit zur procmod- oder procview-Gruppe per setgid als hinreichendes Kriterium dafür, task_for_pid verwenden zu dürfen, hat in Versionen ab Leopard ausgedient. Wenn alle vorherigen Bedingungen erfüllt waren, ruft der Kernel ab 10.5 also noch den taskgated-Dämon auf, um die Entscheidung zu fällen. Die verwendeten Sicherheitseinstellungen für den Dämon sind konfigurierbar per Parameter und in /etc/authorization.

Code Signing Release Notes for Mac OS X 10.5 Leopard

Zusatz: Eine lokale Kopie des Artikels, da das Original nicht mehr in ursprünglicher Form existiert.

Für nicht als root laufende Programme ist es dann beispielsweise eines der notwendigen Kriterien zum erfolgreichen Zugriff auf diese Funktion, daß das Programm die Verwendung dieser Funktion deklariert und passend signiert ist und das Betriebssystem dem Signierendem vertraut. Dies wird von Entwicklungswerkzeugen zur Leistungs-Analyse und Fehlersuche in Programmen verwendet. Weitere Voraussetzung ist hier ebenfalls, daß beide Programme unter demselben Benutzer laufen.

0.3.11 Lion, Version 10.7.1

Zwischen 10.5 und 10.7 hat sich nicht mehr viel verändert.

Ich habe den Zugriff auf die task_for_pid Funktion unter Lion getestet. Man kann die Kernel-Task-For-Pid-Policy einstellen wie man möchte und man kann sich auch zur procmod-Gruppe hinzufügen, aber es nützt alles nichts: Man kann nicht mal auf seine eigenen Prozesse damit zugreifen. Einzig root ist es generell möglich, task_for_pid zu verwenden.

Xcode kann beispielsweise keine Programme mehr debuggen, wenn man die KERN_TFP_POLICY_DENY verwendet:

23.08.11 21:15:52,200 com.apple.debugserver-141: 19 +0.011133 sec [451c/0303]: error: ::task_for_pid ( target_tport = 0x0103, pid = 17693, &task ) => err = 0x00000005 ((os/kern) failure) err = ::task_for_pid ( target_tport = 0x0103, pid = 17693, &task ) => err = 0x00000005 ((os/kern) failure) (0x00000005)

23.08.11 21:15:52,222 com.apple.debugserver-141: 21 +0.011210 sec [451c/0303]: RNBRunLoopLaunchInferior DNBProcessLaunch() returned error: 'failed to get the task for process 17693'

Verwendet man hingegen die normale Policy, dann kann sich Xcode per task_for_pid dranhängen. Die oben besprochenen Kernel-Funktionen check_task_access und mac_proc_check_get_task, die in der task_for_pid-Funktion befragt werden, entscheiden also offenbar, daß Xcode task_for_pid nutzen darf, obwohl er nicht unter root läuft. Andere Prozesse unter dem gleichen User können hingegen task_for_pid nicht nutzen. Der Kernel bestimmt also, welche Programme task_for_pid benutzen dürfen, die damit jedoch wiederum nur auf Programme unter dem gleichen Nutzer zugreifen dürfen, solange sie nicht root sind.

Die task_for_pid-Funktion ist also nur für die Task auf sich selbst erlaubt und für ausgewählte (Apple-) Applikationen unter dem gleichen User und für root. Aber nicht für allgemeine Applikationen, die unter irgendetwas anderem laufen als root.

Oder kurzgefaßt: Ein typisches Schadprogramm, das nicht unter root läuft, kann all dies nicht nutzen.

Zugriff für Developer Tools

Nach bestandener POSIX-Prüfung und so weiter, siehe oben, bestimmt das Developer Tools Access (DTA) Subsystem, welche Programme Zugriff auf die DTA APIs (beispielsweise task_for_pid) haben. Dazu gehört wie oben beschrieben unter anderem Xcode. Das DTA-Subsystem prüft, ob es der vorliegenden Signatur trauen möchte.

Man kann eine weitere App auf diese API zugreifen lassen, wenn man in der Info.plist der App den Key "SecTaskAccess" mit dem Value "allowed" hinzufügt. Außerdem muß die App durch einen von Apple vergebenen Developer-Account signiert werden. Von mir wollten sie dazu unter anderem eine Kopie meines Personalausweises. So sieht das aktuelle Zertifikat für dieses Jahr aus:

take control developer

Benutzt die so erstellte App dann task_for_pid, so bekommt man einmal pro Login-Session diesen Dialog zu sehen, falls man Mitglied in der "Developer Tools"-Gruppe ist:

take control developer

Und wenn man nicht in der "Developer Tools"-Gruppe ist, diesen:

take control normal

Der oben beschriebene taskgated-Dämon entscheidet dann, ob Apps mit geeigneter Signatur und ensprechendem Eintrag in ihrer Info.plist während dieser Login-Session Zugriff bekommen:

Aug 27 20:21:19 KeyWest com.apple.SecurityServer[34]: UID 0 authenticated as user macmark (UID 502) for right 'system.privilege.taskport'
Aug 27 20:21:19 KeyWest com.apple.SecurityServer[34]: Succeeded authorizing right 'system.privilege.taskport' by client '/usr/libexec/taskgated' [27022] for authorization created by '/usr/libexec/taskgated' [27087]

Hier hat das DTA-Subsystem entschieden, daß es meiner Signatur traut, mit der dieses Programm versehen ist, das task_for_pid verwendet. Ein anderes ebenfalls von mir signiertes Programm, das das ebenfalls nutzt, erzeugte keine weitere Abfrage in dieser Login-Session. Offenbar merkt sich das System, welchen Signaturen in der aktuellen Sitzung schon vertraut wurde. Von jemand anderem signierte Programme erfordern jedoch eine separate Erlaubnis. Das kann man leicht probieren, indem man versucht, mit Xcode als Normal-User zu debuggen.

take control normal

Um task_for_pid also in böser Absicht nutzen zu können, müßte man einen Trojaner erstellen, der task_for_pid verwendet und mit einem offiziellem Apple-Developer-Zertifikat signiert ist inklusive der erforderlichen Deklaration in seiner Info.plist. Das ist technisch kein Problem. Allerdings kommt man damit nicht in den App Store, weil der Einsatz untersucht wird und begründet werden muß. Und bei freier Verteilung wäre der Schädling leicht zu erkennen, dank seiner eindeutigen Entwickler-Signatur. Apple könnte diese Signatur ungültig machen und so die weitere Verwendung der Funktion durch den Schädling abstellen. Außerdem könnte eine Aktualisierung der Quarantäne-Liste sämtliche Apps des Entwicklers abfangen. Zudem ist fraglich, wer seinen Entwickler-Account, Geld oder seine Freiheit verlieren möchte, indem er seine Schädlinge signiert. Und zuletzt sind die Abfrage-Dialoge, die ein solcher Zugriff auf die DTA APIs auslöst, recht ungewöhnlich und auffällig.

Warum als Trojaner? Unix Shell-Scripte, Command-Line Tools sowie statische und dynamische shared Libraries verwenden nicht die Bundle-Struktur wie normale Mac-Apps und können daher keine Info.plist wie diese Apps nutzen, um Zugriff auf task_for_pid zu beantragen. Ein "trojanisches Pferd" kann jedoch solch eine normale App sein.

0.3.12 Windows-Injection ist Standard

Wie wir oben gesehen haben, ist Code-Injection mit Mac OS X nur unter großem Aufwand und mit noch größeren Einschränkungen möglich.

Bei Windows sieht das jedoch ganz anders aus. Dort gibt es eine offizielle und komfortable API, die das Einschleusen von Code anbietet. Mit WriteProcessMemory und CreateRemoteThread injiziert und startet man Code in einem anderen seiner Prozesse. Und mit aktivem SeDebugPrivilege kann man das mit jedem beliebigen Prozeß tun.

So mancher Mac-Hacker würde für so eine API in Mac OS X seine rechte Hand geben. Unter Windows gibt es nämlich erstens diese starken Einschränkungen des vollen Zugriffs auf einen anderen Prozeß nicht und zweitens muß man nicht dermaßen viel auf niedriger Ebene programmieren, um einen Thread einzuschleusen, sondern nutzt die von Windows angebotene API.

Valid XHTML 1.0!

Besucherzähler


Latest Update: 11. September 2015 at 19:51h (german time)
Link: cnc.realmacmark.de/osx_dynamic-overriding.php