PostScript-Interpreter mit Fuzzing auf Schwachstellen prüfen
Die Sicherheitsanalysten von Zscaler haben zwei PostScript-Interpreter mit einem eigens entwickelten syntaxbasierten Fuzzer untersucht und mehrere Schwachstellen gefunden. Unser Autor zeigt, wie man einen solchen Fuzzer mit ANTLRv4 und dem Python-Paket treelib für die PostScript-Sprache implementiert.
PostScript ist eine stackbasierte Programmiersprache, bei der Werte auf einem Stack abgelegt und durch nachfolgende Operationen wieder hervorgeholt werden. Es handelt sich um eine höhere Programmiersprache mit einer Vielzahl eingebauter Operatoren zur Ausführung ebenso vielfältiger Aufgaben, einschließlich der Manipulation von Text, dem Zeichnen von Formen und der Umwandlung von Grafiken.
Ein PostScript-Interpreter führt eine PostScript-Sprache nach festgelegten Regeln aus. Der Interpreter manipuliert Entitäten, die als PostScript-Objekte bezeichnet werden. Dazu gehören zum Beispiel Daten wie Zahlen, boolesche Werte, Zeichenketten und Arrays. Weitere Objekte sind Elemente von auszuführenden Programmen, wie Namen, Operatoren und Prozeduren. Es gibt jedoch keine Unterscheidung zwischen Daten und Programmen. Jedes PostScript-Objekt kann als Datei behandelt oder als Teil eines Programms ausgeführt werden. Ein Zeichen-Stream kann nach den Syntaxregeln der PostScript-Sprache gescannt werden, wodurch eine Folge neuer Objekte entsteht. Der Interpreter erfüllt seine Aufgabe durch die Ausführung einer Folge von Objekten. Er kann fünf Stacks verwalten, die den Ausführungsstatus eines PostScript-Programms darstellen: den Operand-Stack, den Dictionary-Stack, den Execution-Stack, den Grafik Status-Stack und den Clipping-Pfad-Stack:
- Der Operand-Stack wird verwendet, um beliebige PostScript-Objekten vorzuhalten, die Operanden sind und die Ergebnisse von PostScript-Operatoren ausführen. Der Interpreter legt Objekte auf den Operand-Stack ab, wenn er sie als literale Daten in einem ausgeführten Programm vorfindet.
- Der Dictionary-Stack wird verwendet, um die Wörterbuchobjekte zu speichern, die den aktuellen Kontext für PostScript-Operationen definieren.
- Der Execution-Stack dient dazu, die ausführbaren Objekte vorzuhalten, bei denen es sich hauptsächlich um Prozeduren und Dateien handelt. Zu jedem Zeitpunkt der Ausführung eines PostScript-Programms stellt dieser Stack den Call-Stack des Programms dar.