
Tauche in CFF-Fonts ein und lerne nebenbei etwas über einen bof im CFF-Parsing aus irgendeinem Jailbreak. Nur zum Spaß.
Tauche ein in CFF-Fonts und lerne nebenbei etwas über einen bof im CFF-Parsing aus einem Jailbreak. nur zum Spaß
Also.... was zur Hölle ist CFF? Nach meinem Wissen ist es ein Dateiformat, aber versuchen wir zu verstehen, was Wikipedia sagt: „CFF acts as a container to store multiple fonts together in a single unit known as a FontSet.“ (https://docs.fileformat.com/font/cff/) Im Grunde können wir also Schriften zusammen speichern. Cool, und jetzt? Nun, da es ein Dateiformat ist, muss es eine Spezifikation geben, und ja, es gibt eine, und nein, sie ist nicht schön, denn sie hat 60 Seiten und ich bin nicht bereit, ihr Format zu lernen. Aber wir können den Exploit aus dem star-master GitHub-Repo (Quellcode von Jailbreak 2.0, glaube ich) verwenden, um etwas über das Format zu lernen.
Also... Wir beginnen damit, das vom Autor bereitgestellte Skript cff.py zu verwenden, und öffnen außerdem die Datei out.cff (eine einfache Standard-.cff-Datei) im HxD-Editor.
Wir können sehen, dass wir beim Füttern der Datei an den Parser Folgendes erhalten:

Wir haben also bereits Fortschritte gemacht, denn wir können bereits eine Definition für CFF aufstellen und zu dem Schluss kommen, dass wir einige Metadaten über den Header haben, die vermutlich die TFF-Versionen angeben, die Headergröße und die Absoffsize, was auch immer das sein mag.
Bis jetzt also |Hauptversion (1 Byte)|Nebenversion (1 Byte)|Headergröße (1 Byte)|Header-Absoffsize (1 Byte)|
Dann gehen wir weiter und haben eine Funktion, die ermittelt, welche Schriften in dieser .cff-Datei verwendet werden. Wie zu sehen.

Woher wissen wir also, dass ihr Zweck darin besteht, zu lesen, welche Schriften verwendet werden? Nun, beim Ausführen des Tools erhielten wir das folgende Ergebnis in cmd:

von denen wir sehen, dass es die nächsten Bytes nach den Metadaten der Datei sind.

Wir werden später auf die Analyse dieser Funktion zurückkommen, aber vorerst können wir ableiten, dass das Dateiformat wie folgt aussieht: |Hauptversion (1 Byte)|Nebenversion (1 Byte)|Headergröße (1 Byte)|Header-Absoffsize (1 Byte)|ABCDEF+Fonts im Paket|
Außerdem sehen wir, dass wir im Skript nach etwas suchen, das als String bezeichnet wird:

Warum das? Weil ich vermute, dass sie Informationen über die im Paket verwendete Schrift sammeln wollen. Aus der Dokumentation: „Alle Strings, mit Ausnahme der FontName- und CIDFontName-Strings, die im Name INDEX erscheinen, die von verschiedenen Fonts innerhalb des FontSet verwendet werden, werden in einer INDEX-Struktur gesammelt und über eine 2-Byte-unsigned-Zahl referenziert, die als String-Identifier oder SID bezeichnet wird. Diese Strings, bekannt als die Standard-Strings, beschreiben alle Namen, die in den ISOAdobe- und Expert-Zeichensätzen verwendet werden.“ Wie auch immer, eine interessante Sache hier ist, dass wir etwa 41 Bytes übersprungen haben, um zum String zu gelangen.


Als Nächstes erhalten wir Informationen über die in der .cff-Datei enthaltenen Schriften.

Wie zur Hölle machen wir das? Nun, wir sammeln die sogenannten Top-Dict-Daten. Was zur Hölle ist das? Nun, soweit ich aus der Dokumentation verstehen konnte, ist es ein Python-Dict mit bestimmten Informationen, die auf eine bestimmte Weise codiert sind.

Zufälligerweise, wenn wir dem Dekodierungsalgorithmus folgen:

wir dereferenzieren den Datentyp Strings, um Informationen über die Schrift zu erhalten. Daraus schließen wir, dass die TopDicts einfach einige Indizes enthalten, die später im Datentyp Strings verwendet werden, um Informationen über die Schrift zu erhalten.
Bis jetzt gilt also weiterhin die Definition der Datei:
|Hauptversion (1 Byte)|Nebenversion (1 Byte)|Headergröße (1 Byte)|Header-Absoffsize (1 Byte)|ABCDEF+Fonts im Paket|41 Bytes bekannt|25 Bytes Informationen über die Schrift|
Cool, was passiert als Nächstes? Nun, wenn wir das Parser-Skript untersuchen, sehen wir, dass es charstring_off und private_off erhält und dann zur Position charstring_off geht und weitere Dinge liest.

Aber wie hilft es uns, das große Ganze zu verstehen? Also werde ich das abrupt abschließen. Im Grunde habe ich einen Diff zwischen zwei Dateien gemacht, einer normalen CFF und der beschädigten CFF.

Links ist die beschädigte .cff-Datei und rechts eine normale .cff-Datei. Wenn wir das Laufzeitergebnis untersuchen:

Beim ersten Ausführen des Parsers sehen wir, dass alles wie count, offsize, offbase einfach einige Offsets bis zu bestimmten Trennzeichen sind. Welche Trennzeichen? Genauer gesagt der Name der Schrift. Wie du also sehen kannst, ('offbase', 8L), also vom Anfang der Datei bis zum ersten Auftreten des Strings der in der CFF-Datei vorhandenen Schrift.

Wie auch im „zweiten Lauf des Parsers“ zu sehen ist:

wir sehen beim Offset 0x6c im Hex-Viewer das \x0e\0xe\0xe\x0e, den Anfang unserer bösartigen Daten.
Als Fazit also ein allgemeines Format der .cff-Datei:
|Hauptversion (1 Byte)|Nebenversion (1 Byte)|Headergröße (1 Byte)|Header-Absoffsize (1 Byte)|ABCDEF+Fonts im Paket|41 Bytes bekannt|25 Bytes Informationen über die Schrift|
Und benutzerdefiniertes Dateiformat (mit Benutzerinhalt)
|Hauptversion (1 Byte)|Nebenversion (1 Byte)|Headergröße (1 Byte)|Header-Absoffsize (1 Byte)|ABCDEF+Fonts im Paket|41 Bytes unbekannt|25 Bytes Informationen über die Schrift|4 Bytes (count)|4 Bytes (offsize)|9 Bytes, die noch zu dokumentieren sind|Benutzerinhalt|