LANGSEC (language-theoretic security)

From
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

LANGSEC

LANGSEC – language-theoretic security – ist ein relativ neuer Ansatz zur Betrachtung von Sicherheitsproblemen und ihren Ursachen. Im Kern steht die Erkenntnis, dass Eingaben (Dateien, Pakete, …) als Code aufgefasst werden können, der auf den durch das verarbeitende System zur Verfügung gestellten Berechnungsprimitiven ausgeführt wird.<ref> Sergey Bratus, Michael E. Locasto, Meredith L. Patterson, Len Sassaman, Anna Shubina. Exploit Programming: From Buffer Overflows to "Weird Machines" and Theory of Computation ;login: December 2011, Volume 36, Number 6 </ref> Die Struktur der Eingabedaten bestimmt, welche Codepfade des interpretierenden Programms durchlaufen und welche Aktionen dadurch ausgeführt werden.

Als sehr vereinfachtes Beispiel lässt sich eine PNG-Datei als ein "Programm" betrachten, welches, "ausgeführt" von einem Bildbetrachter, ein Bild auf den Bildschirm malt. Enthalten im Bildbetrachter wiederum ist üblicherweise libpng, auf welcher eine PNG-Datei als ein Programm zur Befüllung eines Speicherbereichs mit einer bestimmten Folge von Bytes verstanden werden kann – oberflächlich betrachtet relativ langweilig, jedoch hängt die Größe des Speicherbereichs dabei von Informationen in bestimmten Bereichen der Datei ab und Teile des Datenstroms sind "Unterprogramme" die von libz interpretiert werden.
Existiert nun z. B. in libz ein Bug (z. B. ein Heap Buffer Overflow), so können durch diesen andere Programmzustände und -pfade erreicht werden. Verwendet der Bildbetrachter die GPU (z. B. ein Browser), so kann es u. U. dazu kommen, dass diese aus einem seltsamen Programmzustand heraus mit invaliden Daten beschickt und dadurch ebenfalls in einen seltsamen Zustand versetzt wird, was dann z. B. dazu führen könnte, dass der X-Server stirbt. Bei "richtiger" Konfiguration der Umgebung (passender Daten- und Kontrollfluss, nützliche Bugs, …) ist es also möglich, ein PNG-"Programm" zu konstruieren, welches beim "Ausführen" den X-Server beendet.

Auch wenn dies nur ein hypothetisches Beispiel ist, existieren ähnliche reale Beispiele. Eines davon ist der "Ping of Death", ein überlanger ICMP ECHO Request, der per Pufferüberlauf viele verschiedene Systeme zum Absturz bringen konnte. Neben Pufferüberläufen existieren aber noch viele andere Wege, ein System negativ zu beeinflussen. Diese werden üblicherweise auf Basis des verwendeten Mechanismus kategorisiert (Stack-/Heap-Pufferüberlauf, SQL/…-Injection, Cross-Site-Scripting, Return-Oriented Programming, …). Während z. B. SQL-Injection und Cross-Site-Scripting als verwandte Mechanismen erkennbar sind, fällt es schwer, einen Zusammenhang zwischen SQL-Injection und Return-Oriented Programming oder auch einem Heap-Pufferüberlauf zu finden. Auch wenn diese Auflistung gut genug für die Beschreibung von beobachteten Problemen und Bugs funktioniert, kann sie deshalb nicht helfen, ein System robuster gegen neue (noch unbekannte) Angriffswege zu machen.

Die zweite simple Erkenntnis, die den Kern der LANGSEC-Perspektive prägt, ist, dass Eingaben in ein System bzw. Einflussnahme auf die Kommunikation zwischen seinen Komponenten (also die Eingaben seiner Subsysteme) notwendiger Bestandteil eines Angriffs sind. Kombiniert man diese und die andere Kernerkenntnis ("Daten sind Code"), ergibt sich eine Sichtweise, die sowohl zur systematischen Absicherung eines Systems als auch zur Suche nach Schwachstellen und neuen Angriffswegen geeignet ist.

Zuallererst ist hiernach klar, dass der kritische Bereich eines Programms die Verarbeitung von Eingaben ist. Damit ein Fehler in einem beliebigen Teil des Programms tatsächlich eine ausnutzbare Schwachstelle darstellt, muss ein Pfad (durch Daten oder Kontrollfluss) von Eingaben ausgehend zu diesem Punkt existieren. Hieraus folgt, dass Eingaben so früh wie möglich so vollständig wie möglich überprüft werden sollten und insbesondere auch alle möglichen Eingaben behandelt werden sollten, um so die durch invalide Eingaben erreichbaren Pfade zu beschränken.

Zur Suche von Schwachstellen ist die von der Eingabe ausgehend erreichbare Funktionalität interessant. Ziel ist es, ein "Programm" zu schreiben, welches mit der zur Verfügung gestellten Berechnungskapazität eine unerwartete (die erwarteten oder zumindest erhofften Grenzen überschreitende) Berechnung ausführt. Interessant hierfür sind z. B. Programmstellen, an denen die Eingaben verzögert oder gar nicht geprüft werden und so interessante Funktionalität erreichbar ist. Aber auch Mechanismen, die die Programmierung vereinfachen, sind wertvolle Werkzeuge. (Erwähnt seien hier die Funde, dass ELF-Metadaten<ref> Rebecca Shapiro, Sergey Bratus, Sean W. Smith. "Weird Machines" in ELF: A Spotlight on the Underappreciated Metadata USENIX WOOT (Workshop on Offensive Technologies) '13 </ref>, DWARF-Debug-Informationen<ref> James Oakley, Sergey Bratus. Exploiting the hard-working DWARF: Trojan and Exploit Techniques With No Native Executable Code USENIX WOOT (Workshop on Offensive Technologies) '11 </ref> und sogar die X86-MMU<ref> Julian Bangert, Sergey Bratus, Rebecca Shapiro, Sean W. Smith. The Page-Fault Weird Machine: Lessons in Instruction-less Computation USENIX WOOT (Workshop on Offensive Technologies) '13 </ref> beliebige Berechnungen ausführen können.)

Die logische Folge zur Defensive ist, dass sämtliche potentiell unvertrauenswürdigen Eingaben zur Verfügung gestellte Berechnungskapazität minimiert werden sollte. Empfehlenswert ist die Beschränkung auf reguläre oder deterministisch kontextfreie Eingabeformate, da sich diese mit der Berechnungskapazität eines endlichen- oder Kellerautomaten parsen und validieren lassen. Für Sprachen höherer Komplexität ist die Frage, ob zwei Grammatiken die selbe Sprache beschreiben, unentscheidbar. Das heißt, dass nicht oder nur mit viel Aufwand für einen konkreten Fall sichergestellt werden kann, dass zwei Implementierungen die selbe Eingabe gleich interpretieren.

Dies ist eine weitere häufige Ursache von sicherheitsrelevanten Fehlern. Als vielseitiges Beispiel seien hier SSL-Zertifikate erwähnt. Im Zertifikat bezeichnet auf ASN.1-Ebene die OID 2.5.4.3 den "common name", den Hostnamen für den das Zertifikat ausgestellt wurde. Einige Programme interpretieren aber z. B. 2.5.4.18446744073709551619 (also 2.5.4.(2^64+3)) ebenfalls als diese OID (durch Verwendung von 64-bit Integern anstelle von Bignums). Auch ist es möglich, als common name Zeichenketten wie "bank.com\0.evil.com" oder mehrere common names zu verwenden. Certificate Authorities (CAs), Browser, … verwenden unterschiedliche Parser und interpretieren das selbe Zertifikat unterschiedlich. So kann also ein vom Besitzer von evil.com erzeugtes Zertifikat von einer CA geprüft, von dieser als valide eingestuft und signiert werden, dann aber von Browsern anders interpretiert und auch oder sogar nur für bank.com akzeptiert werden.<ref> Dan Kaminsky, Meredith L. Patterson, Len Sassaman. PKI Layer Cake: New Collision Attacks Against the Global X.509 Infrastructure Financial Cryptography and Data Security '10 </ref><ref> Len Sassaman, Meredith L. Patterson, Sergey Bratus, Anna Shubina. The Halting Problems of Network Stack Insecurity ;login: December 2011, Volume 36, Number 6 </ref> (Hierbei sei angemerkt, dass die gleiche Interpretation durch unterschiedliche Systeme wichtiger ist als die korrekte Interpretation. Wenn z. B. alle Systeme Strings am ersten Null-Byte abschneiden oder nur 64-bit-Zahlen verwenden, ist die tatsächlich implementierte Sprache zwar eine andere als die im Standard beschriebene, es ist aber genau eine Sprache und nicht eine Vielzahl verschiedener semi-kompatibler Dialekte.)


Polyglots

Kippbild "My wife and my mother in law"

Ein Polyglot ist eine Datei, die verschiedene Dateitypen hat. <ref name="Polyglots"> Ange Albertini. Funky Files, the Novella! in: 7. Ausgabe des International Journal of PoC||GTFO, März 2015</ref> Das heißt, ein Polyglot wird von zwei verschiedenen Interpretern akzeptiert und die Ausgabe ist u.U. unterschiedlich. Durch die Erzeugung von Polyglots lässt sich zeigen, dass eine Datei keinen eindeutigen Dateityp hat. Die Existenz einer solchen Datei genügt als Beweis. Dadurch lässt sich schließen, dass in diesem Bereich in der Praxis ein Kompromiss gemacht wird. Die potentielle Mehrdeutigkeit von Dateien wird in Kauf genommen, da Abwärtskompatibilität erwünscht ist.

Warum gibt es Polyglots?

Polyglots sind aus der Informatik nicht wegzudenken. Die "historischen Fehler", die beim Design von Dateitypen gemacht wurden, machen das Verhindern von polyglotten Effekten de-facto unmöglich. Theoretisch kann ein Interpreter so realisiert werden, dass dieser nur strikte Dateiformate annimmt. Ein Dateiformat ist strikt, wenn es am offset zero (Dateianfang) eine eindeutige magic number hat. Dieser strikte Interpreter dürfte dann allerdings keine Formate wie z.B. PDF oder ZIP akzeptieren und wäre somit in seinem Nutzen stark eingeschränkt. Ein Beispiel für ein striktes (Bild-)Dateiformat ist PNG.<ref name= "PNG"> https://www.w3.org/TR/PNG/ </ref>

Minimalbeispiel eines Polyglots

Eine einfache Möglichkeit ein Polyglot zu erstellen wird im Folgenden erläutert. Eine GIF-Datei benötigt, um als solche erkannt zu werden, am Anfang der Datei die Signatur "GIF". Das Ende der GIF-Datei wird mit "3B" markiert. Alle weiteren Bytes werden einfach ignoriert. <ref name= "corkami gif"> https://github.com/corkami/pics/blob/master/GIF.png</ref>

Der Aufbau einer GIF-Datei

Dies ermöglicht das Anhängen einer weiteren Datei, zum Beispiel eine ZIP-Datei. Eine ZIP-Datei benötigt lediglich an einer Stelle in der Nähe vom Dateiende den Marker "PK56", der Hinweis darauf, dass sie eine Datei diesen Typs ist. Nach diesem Marker gibt es einen Verweis auf eine Central Directory, in dem alle Verweise auf Datenblöcke der Datei gelistet sind. Es muss allerdings nicht jedes Byte der Datei gelesen werden. <ref name= "corkami zip"> https://github.com/corkami/pics/blob/master/ZIP.png </ref>

Der Aufbau einer ZIP-Datei

Die Eigenschaften der GIF- und ZIP-Dateien ermöglichen das Konkatenieren zweier solcher Dateien miteinander, ohne dass die Funktionalität der ursprünglichen Dateien beeinträchtigt wird.

Sind also x.gif und y.zip eine GIF- bzw. eine ZIP-Datei, so lassen sich diese in der Linux-Kommandozeil mittels cat x.gif y.zip > merge.new zu einer neuen Datei merge.new konkatenieren. Diesen neue Datei kann mit eog merge.new als GIF-Datei ausgeführt und mit unzip merge.new wie eine ZIP-Datei entpackt werden.

Dasselbe Prinzip funktioniert auch mit einer JAR-Datei. Eine JAR-Datei ähnelt im Aufbau einer ZIP-Datei. Der Kernunterschied ist die Datei META-INF/MANIFEST.MF, die Informationen für die JAR-Datei enthält. Sie lässt sich jedoch einfach erstellen. Ist eine JAR-Datei und die zugehörige META-INF/MANIFEST.MF gegeben, so lässt sich analog wie im letzten Abschnitt ein Polyglot erstellen mit y.jar statt y.zip. Bei der Ausführung der JAR-Datei mit java -jar merge.new muss lediglich darauf geachtet werden, dass sich die Datei MÈTA-INF/MANIFEST.MF im selben Ordner befindet.

resultierende Probleme

Wie gesehen können Dateien teilweise Interpretation unter mehr als einem existenten Dateiformat zulassen. So könnten potentiell gefährliche Daten durch ein System durchgeleitet werden, ohne dass dieses der Vorgang sicher verhindern oder auch nur bemerken kann. Es ist unmöglich, die Eindeutigkeit der Interpretation am Zielsystem zu garantieren. (Zum einen gibt es zu viele Programme und mögliche Dateiformate, zum anderen führen Bugs in der Eingabeverarbeitung dieser Programme oft dazu, dass auch invalide Daten akzeptiert oder valide Daten anders interpretiert werden.) Trotzdem versuchen viele Programme dies zu erreichen, oft durch Suche nach magic numbers von Dateiformaten. Da in hinreichend großen Datenmengen rein zufällig verschiedene Bytefolgen vorkommen werden, werden gelegentlich auch valide Daten abgelehnt.

Wir haben nun eine große Sammlung von validen PNGs nach magic numbers verschiedener Dateiformate durchsucht und viele Kollisionen bemerkt. Das unten zu betrachtene Bild einer Approximation der Mathematikerin Grete Hermann mit ~100 Dreiecken ist eines von etwa 10000 im Verlauf der Approximation entstandenen. Ungefähr ein Dutzend von diesen enthält im Bytestream zufällig die magic number von ZIP (PK\05\06) und kann dadurch in einem Mediawiki nicht hochgeladen werden. (Deswegen ist hier ein Screenshot zu sehen). Mediawiki glaubt, dass die Datei ein ZIP-Archiv sein könnte, kann aber die (nicht vorhandenen) Daten nicht lesen, und verbietet deshalb "sicherheitshalber" den Upload. Demgegenüber gestellt kann das Vortrags-PDF, welches gleichzeitig eine tatsächliche ZIP-Datei ist, welche neben JARs und anderen Dateien ebenjenes Bild enthält, ohne Probleme hochgeladen werden (sofern nicht PDF-Upload generell verboten wurde).

Hundertste Dreiecksiteration der Mathematikerin Grete Hermann
Im Byte-Dump der Datei ist die ZIP-Signatur

Vortragsfolien

Die Vortragsfolien können mit einem PDF-Viewer betrachtet und ebenso mit unzip entpackt werden. Link zum Vortrag

Literatur- und Quellenverzeichnis

<references/>

Weiterführende Literatur

  • Halvar Flake. Understanding the fundamentals of attacks: What is happening when someone writes an exploit? (Slides, November 2016) - Eine detaillierte Einführung am Beispiel eines kleinen key-value-Stores, mit Fokus auf die Definition von Exploitbarkeit, über die ursprünglichen Ideen von LANGSEC hinausgehend.
  • langsec.org - Sammlung von Essays, Papers und Talks
  • alchemistowl.org/pocorgtfo/ - Alle Ausgaben von PoC||GTFO - Fast alle sind Polyglots, zusätzliche Informationen sind als ZIP-Archiv eingebettet. (Oft haben die PDFs aber noch mehr Interpretationen, sind z. B. bootbar, ein Dateisystem, als Android-Paket installierbar, oder sogar gleichzeitig PDF, ZIP, Audio, und Bild.)