userland execve()
Was userland execve()
ist und was es
bezüglich System-Sicherheit bedeutet.
Userland execve()
Userland execve()
im PraxistestDie etwas vage gehaltene Ankündigung des Vortrages von Vincenzo Iozzo für die Black Hat 2009 sorgte für diverse fehlgeleitete Spekulationen in den Nachrichten. Durch Nicht-Wissen, worum es wirklich gehen könnte, und die angekündigte Präsentation auf einer Konferenz für Angriffstechniken, schlossen viele Artikel schlagzeilen-trächtig auf eine neu entdeckte Sicherheitslücke in OS X.
Wenn man jedoch auf Gespenster-Märchen verzichtet und erfährt, worum es geht, dann stellt sich das Ganze als eine sehr interessante Technik dar, die man sowohl für gute als auch für böse Zwecke nutzen kann; genauso wie man ein Küchenmesser einsetzen kann, um einen Apfel zu zerlegen oder um das Meerschweinchen umzubringen, so hat auch diese Technik unterschiedliche Anwendungsmöglichkeiten, aber sie ist nicht von Natur aus einem dunklen Zweck geweiht.
Im Endeffekt macht diese Technik einem Angreifer das "Eindringen in", "Befallen" oder "Erobern von" OS X nicht leichter. Sie stellt keinen neuen Angriffsweg (die coolen Kids sagen "attack vector") dar. Sie kann allerdings dazu verwendet werden, wenn man bereits erfolgreich eingebrochen ist, also den Rechner schon erobert oder dem Benutzer einen Virus oder Trojaner untergeschoben hat, zusätzliche Programme aufzurufen und dabei weniger Spuren zu hinterlassen als durch normale Programm-Aufrufe.
Das beste Mittel gegen FUD ist Wissen, daher sehen wir uns an, worum es hier geht:
Die Technik, die Vincenzo Iozzo entwickelt hat, ist eine Alternative
zum normalen Programm-Aufruf über den dafür verfügbaren System-Befehl execve()
.
Der execve()
-System-Aufruf ist der einzige Mechanismus des Betriebssystem-Kerns,
der Benutzer-Programmen zur Verfügung steht, um ein anderes Programm aufzurufen. Das Programm
liegt dabei als Datei vor.
Vergleiche dazu in dem Buch
Mac OS X Internals von
Amit Singh das Kapitel 7.5
The execve()
System Call.
Vergleiche dazu auch man execve
.
Dies ist der Quelltext im Kern von
OS X,
der
execve()
enthält.
Das auszuführende Programm ist eine Binärdatei in einem bestimmten Format. Bei Windows ist das PE, welches auf dem veraltetem COFF von UNIX basiert. Bei den meisten Unices und GNU/Linux ist es ELF und bei OS X ist es Mach-O.
Userland execve()
Userland bedeutet, daß man sich nicht im
Kern, sondern im Benutzerbereich bewegt.
Ein userland execve()
kann also ein anderes Programm
starten, ohne den sonst üblichen System-Aufruf.
Eine wichtige Besonderheit ist, daß userland execve()
ein
Programm starten kann, welches nur im Hauptspeicher liegt. Es kann aber
auch wie das normale execve()
Programme von der Platte starten.
Dadurch, daß man zum Starten des Programms keine Datei angeben muß, ist es möglich, ein Programm per Netzwerk in den Speicher zu laden und dann direkt dort auszuführen, ohne es auf der Platte speichern zu müssen.
Für Angreifer ist dies dann auch der interessanteste Aspekt, denn sie können, wenn sie bereits ein Programm auf dem System übernommen haben, weitere Programme nachladen, und dabei vermeiden, daß sie durch Speichern der nachgeladenen Programme auf Platte, viele Spuren hinterlassen.
Diese Technik, Programme ohne Systemaufruf direkt aus dem Hauptspeicher zu starten, gibt es in jeweils verschiedenen Varianten für UNIX, GNU/Linux und für Windows schon länger. Außer den beiden referenzierten Beispielen gibt es noch weitere.
Bei Windows ist der originale System-Aufruf natürlich
nicht execve()
, sondern CreateProcess()
.
Vincenzo Iozzo mußte nachbauen, wie ein ausführbares Programm nach dem Laden im Speicher abgelegt wird.
Ein Interview mit Vincenzo Iozzo über diese Technik.
Vincenzo Iozzo sagt zwar, daß es vorher nicht möglich war,
komplette ausführbare Dateien in einen Prozeß einzuschleusen, aber das stimmt
nicht ganz, denn mit Objective C
läßt sich etwas Vergleichbares
realisieren, etwa wie die System-Einstellungen als prominentestes
Beispiel in OS X
ihre Module dynamisch, also zur Laufzeit (allerdings von der Platte)
nachladen, wenn die entsprechende Einstellung angeklickt wird.
Mit userland execve()
kann also ein Programm A ein
anderes Programm B ausführen, ohne daß Programm B als Datei auf der Festplatte
(oder sonstwo im Dateisystem) vorliegen muß. Das ist technisch sehr interessant
und wir dürfen auf seine Beispiel-Implementierung gespannt sein.
Nachdem nun bekannt ist, was man mit Implementierungen von userland execve()
,
beispielsweise der von Vincenzo Iozzo,
erreichen kann, bleibt die Frage, was man damit nicht tun kann.
Wie Vincenzo Iozzo selbst sagt, wird seine Technik von den Medien verzerrt dargestellt als etwas, was sie nicht ist: Sie stellt kein Schadprogramm dar, aber sie kann von Schadprogrammen benutzt werden, um einige Aktivitäten auf dem Opfer-System zu verschleiern. Sie erleichtert keinen Einbruch, aber sie macht die Programm-Ausführung auf einem erfolgreich angegriffenen System einfacher.
Vincenzo Iozzo macht auf mich daher einen sehr sympathischen
Eindruck, weil er im Gegensatz zu vielen selbsternannten Hackern erstens offenbar
eine eigene gute innovative Programmier-Leistung erbracht hat, indem er die erste
Version von userland execve()
für
OS X implementiert hat,
und diese zweitens realistisch
einschätzt und nicht den "Hack", wie das üblicherweise bei anderen Hackern passiert,
als den nächsten Weltuntergang für
OS X verkauft. Insofern
Lob, Respekt und Anerkennung für Vincenzo von meiner Seite. Es
gibt zu wenige Leute mit Charakter wie ihn auf diesem Gebiet.
Ein Betriebssystem stellt Programmen diverse komfortable Routinen zur Verfügung, um
alltägliche Aufgaben zu lösen. Es wird aber kein Programm gezwungen, diese auch zu
benutzen. Um ein anderes Programm aufzurufen, muß man daher nicht zwingend den
bereitliegeneden execve()
nutzen. Man kann sich auch mehr Mühe machen
und dies und einiges mehr selbst programmieren, wie es bei den Implementierungen von
userland execve()
auf den verschiedenen Betriebssystemen der Fall ist.
Diese Eigenkreation kann natürlich Extras bieten, wie beispielsweise das Schmankerl,
daß man nicht auf eine Datei der Festplatte angewiesen ist, die das ausführbare
Programme enthält, sondern auch von irgendwo anders her das Programm direkt in den
Speicher laden kann. Das stellt übrigens keine Verletzung der UNIX-Regeln dar, denn
ein Programm darf ja in seinem Adreßraum machen, was es mag. Und es darf auch weiteren
Speicher anfordern. Daß es den nun für einen selbstgeschaffenen Prozeß benötigt,
interessiert das System nicht.
Vincenzo Iozzo behauptet auch nicht, daß seine Technik völlige Unsichtbarkeit garantiert. Wenn damit ein Programm wie beispielsweise Safari gestartet würde, dann hinterläßt dieses natürlich die üblichen Spuren auf der Platte, wie es das sonst auch tut. Führt man jedoch ein speziell angefertigtes Schadprogramm aus, dann wird dieses darauf achten, nichts auf der Platte zu verändern.
Auf jeden Fall kann man aber durch Überwachung des Netzverkehrs feststellen, was passiert, denn das typische Schad-Szenario sieht hier so aus, daß nach einer Programm-Übernahme diese Technik verwendet werden kann, um aus dem Netz weitere Schadprogramme in den Speicher zu laden und zu starten. Auch durch Überwachung des Adreßraums, also beim Nachsehen, welche Prozesse auf dem System laufen und wie sich ihre Speicherbelegung verändert, kann ein Angriff auffliegen, der sich dieser Technik bedient.
Es ist momentan noch nicht klar, ob man einen so gestarteten Prozeß im Aktivitätsmonitor-Programm
oder mit top
oder mit ps
als Prozeß in der Liste sehen kann oder ob
sich nur der Speicherplatz des Mutter-Programms vergrößert. Diese Information werde ich erst
nach seinem Vortrag haben können.
Eine weitere Möglichkeit zur Verhinderung so eines Szenarios ist, daß man Programme in einem Sandkasten laufen läßt, den man so einstellt, daß Netzverkehr nicht erlaubt ist. Das ist allerdings nur hilfreich bei Programmen, die kein Netz benötigen.
Ein Angriff, der nötig ist, um diese Technik anwenden zu können, muß wenigstens ein Programm unter
seine Kontrolle bringen. Und dieser Angriff hinterläßt natürlich wie bisher
alle seine Spuren. Nur die anschließend mittels userland execve()
nachgeladenen Programme können von dem
festplattenlosen Programme-Laden profitieren. Schlagzeilen wie "Angriff ohne Spuren" sind
daher falsch, denn der eigentliche Angriff hat weiterhin Spuren und nur der nachgeladene Teil
hat weniger Spuren.
ASLR, die möglichst zufällige Belegung von Speicherplatz durch Daten, Programme und Bibliotheken, soll es einem Angreifer erschweren, die Adresse seines Angriffsziels herauszufinden. Im Grunde ist es Verstecken-Spielen: Der Angreifer muß sein Opfer erst im Speicher finden, bevor er es tatsächlich angreifen kann.
Die Implementierungen von ASLR unterscheiden sich darin, was alles "versteckt" wird. In Leopard wurde beispielsweise Library Randomization eingeführt. Für spätere Versionen rechne ich damit, daß weitere Bereiche von OS X "zufällig" werden, da bei dieser Version hauptsächlich bezüglich Sicherheitsaspekten weiterentwickelt wird.
In der Praxis hilft ASLR
hier jedoch nicht besonders viel. Das sieht man daran, daß es userland execve()
oder vergleichbare Techniken nicht nur bereits für diverse andere
Unices und
GNU/Linux gibt, sondern auch
für Windows. Für Windows gibt es sogar
Meterpreter, ein Modul für das
Metasploit Framework.
Offenbar stellt ASLR in der Praxis auf keinem der Betriebssysteme ein ernstes Hindernis für Techniken dieser Art dar. Und wenn man in Betracht zieht, daß ASLR eigentlich nur eine Form von "security by obscurity" ist, dann ist klar, warum es sich dabei nur um eine Unebenheit auf der Straße handelt. Man sollte sich davon nicht zuviel versprechen.
Eine Untersuchung der Universität Stanford kommt zu dem Schluß, daß ASLR mit 32 Bit Programmen ziemlich unnütz ist. Sie haben sogar ein Programm entwickelt, welches aus einem Exploit, der ohne ASLR einen Pufferüberlauf ausnutzen kann, einen Exploit macht, der mit ASLR funktionioniert.
Sie kommen zu dem Schluß, daß Randomisation zur Übersetzungszeit und Randomisation zur Laufzeit, selbst wenn man beide kombiniert, den Angriff nur für wenige Sekunden aufhalten kann. Die einzig vielversprechende Lösung sehen sie darin 64 Bit zu nutzen, weil damit ein brutales Durchprobieren nicht mehr praktikabel ist, um ASLR zu überwinden.
Dies bedeutet, daß ASLR auf 32 Bit Systemen mit 32 Bit Programmen nur der Werbung für das System dient, es jedoch keineswegs sicherer macht. Eine weitere Schlußfolgerung ist, daß Schnee-Leopard als System, welches die meisten Programme in 64 Bit mitbringt ein sichereres ASLR bietet als die immer noch am weitesten verbreiteten 32 Bit Versionen von Windows.
Userland execve()
im Praxistest
Ich habe userland execve()
, nachdem Vincenzo Iozzo es
veröffentlicht hatte, ausprobiert: Unter 10.5 funktioniert es. Es ist zur Laufzeit zu erkennen,
wenn man die Nutzung des virtuellen Speichers des Prozesses überprüft, beipsielsweise mit
vmmap
. In den üblichen Werkzeugen (top, ps
)
taucht kein weiterer Prozeß auf. Der Grund ist,
daß das nachgeladene Programm im Speicher des Opfers läuft und auch von diesem gestartet wurde,
ohne das System zu nutzen. Die Anzeige ist also korrekt. Das eingeschleuste Programm taucht jedoch
in der Benutzungsoberfläche auf, sofern es selbst eine hat. Wurde es bereits vom Benutzer vorher selbst
gestartet, dann erscheint es nach dem Einschleusen ein zweites Mal in der Obefläche, auch im Dock und
im Programm-Umschalter.
Wenn man weiß, worauf zu achten ist, erkennt man die Aktivität auch
mit einem Netzwerk-Analyse-Werkezeug wie Wireshark
.
Es gibt jedoch ein paar Praxisprobleme: userland execve()
kommt anscheinend mit Universal-Binaries
nicht klar: Man kann zum Beispiel reine 32-Bit-Intel-Binaries in den Opfer-Prozeß nachladen, muß die Version
also gegebenenfalls aus dem Universal-Binary vorher erst herauskopieren.
Außerdem funktioniert es nicht mehr mit 10.6: Der Angriff scheitert mit einem Segmentation fault
des
Opferprogramms, sobald das nachzuladende Programm dort eingeschleust und gestartet werden soll.
Vincenzo sagte mir, als ich ihn darauf ansprach, daß er
es selbst noch nicht auf 10.6 probiert habe und sein Programm möglicherweise nochmal dafür angepaßt werden müßte.
Ein anderes Problem ist, daß das nachgeladene und im Opferprogramm dann gestartete Programm eventuell bestimmte Ressourcen auf der Platte sucht und zwar relativ zu der Stelle, an der das Opferprogramm läuft. Programme auf OS X sind nicht das was man im Finder sieht: Es sind tatsächlich Verzeichnisse, die die ausführbare Datei und zugehörige private Ressourcen enthalten. Die ausführbare Datei findet diese ihre privaten Ressourcen jedoch nicht mehr, wenn sie in ein fremdes Programm eingeschleust wurde. Daher sind nur sehr einfache Programme, die keine privaten Ressourcen verwenden, einschleusbar. In seiner Vorführung umging Vincenzo das Problem, indem er das Opferprogramm von einem anderen Pfad aus aufrief. Darauf hat man jedoch im realen Fall keinen Einfluß.