
CVE-2023-29478 - BiblioCraft Dateimanipulation/Remote Code Execution Exploit, der BiblioCraft-Versionen vor v2.4.6 betrifft
Diese Methode benötigt nur BiblioCraft! Nicht allzu viel Trickserei und keine anderen Mods sind erforderlich, um Codeausführung zu erreichen!
Der ursprüngliche Bericht über CoreTweaks ist hier zu finden. Ich empfehle, diesen zuerst zu lesen, da ich möglicherweise einige Details überspringe, die unten wichtig werden.
Codeausführung ist durch mehrere Methoden möglich. Dies betrifft BiblioCraft 1.7.10 v1.11.7 und BiblioCraft 1.12.2 v2.4.5 (bestätigt) und wahrscheinlich alle BiblioCraft-Versionen vor v2.4.6 (unbestätigt, ungetestet)
Vorhandene Patches, die den Path-Traversal-Fehler beheben, verhindern weiterhin diesen neuen Codeausführungspfad.
Dieses Mal konzentrieren wir uns auf das Speichern von Vanilla-Büchern.
Geschriebene Bücher werden unter world/books/<Author-Name>, <Buch-Titel> gespeichert.
Das Format dieser Bücher ist pro Zeile:
Weiter vom Anfang an: Die Seiten des Buches beginnen mit einer Markierung #pgx<Seitennummer> und ab der nächsten Zeile bis zur nächsten Markierung gehört der Inhalt zu dieser Seite. BiblioCraft fügt am Ende von Zeilen immer einen Zeilenumbruch hinzu, selbst wenn nichts folgt.
Wie zuvor können wir sowohl den Buchtitel als auch den Autornamen über das NBT-Tag des Buches kontrollieren und einen Path-Traversal durchführen.
Der Ort, an den wir schreiben werden, ist der Ordner mods/. Mods in diesem Verzeichnis werden meistens als JAR-Dateien gespeichert. Eine nette Eigenschaft von JAR-Dateien ist, dass sie eigentlich nur getarnte ZIP-Dateien sind.
Warum hilft uns das?
ZIP-Dateien haben die interessante Eigenschaft, dass sie auch dann noch gültig sein können, wenn ihnen Mülldaten vorangestellt oder angehängt werden.
Der EOCD-Eintrag (End of central directory) innerhalb einer ZIP-Datei befindet sich am Ende.
Er besteht (grob) aus einer Magie/Signatur 0x06054b50 sowie der Anzahl, Größe und dem Offset der Central-Directory-Einträge im Archiv.
Das letzte Feld in einem EOCD-Eintrag ist ein längenpräfixierter Kommentar, der fast jede Byte-Sequenz sein kann, die nicht die Magie ist (ungetestet).
(Ich denke, ZIP-Datei-Parser beginnen am Ende und suchen nach der EOCD-Signatur, um das Archiv zu parsen, aber ich bin mir nicht ganz sicher.)
Wenn der ZIP-Parser nur den EOCD-Eintrag benötigt, um die Dateien im Archiv zu finden, und wenn dem EOCD-Eintrag beliebige Daten (mit Einschränkungen) folgen können, dann können wir ein gültiges ZIP erstellen, selbst wenn andere Daten in der Datei vorhanden sind.
(Großer Dank an https://github.com/c0ny1/ascii-jar! Ich habe diese Tools intensiv genutzt!)
Zuerst müssen wir unsere Nutzlast strukturieren. Forge lädt Klassen als Mods, wenn sie die spezielle Annotation @Mod haben, also verwenden wir das hier.
Unsere grobe Nutzlast:
@Mod(modid = "payload-mod")
public class Payload {
// ...
static {
System.out.println("Hello World!");
}
// ...
}
Um Kodierungsprobleme zu vermeiden, verwenden wir ein Skript, um eine ASCII-only JAR-Datei zu erstellen, die nur unsere Nutzlastklasse enthält.
Wir polstern den Anfang mit PK\3\4.jar\n../../mods/\nprivate\n#pgx0\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Der Grund, warum wir PK\3\4 (die lokale Dateikopf-Signatur von ZIP) dem Padding voranstellen, liegt an einer seltsamen Eigenart in Forge, die ich außerhalb nicht reproduzieren konnte, wonach JAR-Dateien darauf überprüft werden, ob sie mit dieser Signatur beginnen, obwohl dies keine Anforderung von ZIP ist? Könnte ein Bug in Forge sein.
Wir verwenden die lange Zeichenfolge aus 'A's und 'a's, um sicherzustellen, dass der Offset größer als 255 ist, damit die 2-Byte-Ganzzahl, die den Offset kodiert, keine Bytes außerhalb des ASCII-Bereichs hat.
Wir polstern das Ende mit \n, um Probleme mit BiblioCraft zu vermeiden, das Zeilenumbrüche hinzufügt.
Mit unserer neuen gepolsterten JAR-Datei nehmen wir alle Bytes nach dem letzten Zeilenumbruch der Daten, die wir am Anfang hinzugefügt haben, und erstellen daraus einen neuen String (wir nennen dies <jar-data>) und erstellen ein neues geschriebenes Buch mit NBT-Daten:
TAG_Compound(''): 2 entries
{
TAG_String("author"): "../../mods/"
TAG_String("title"): "PK\3\4.jar"
TAG_List("pages"): 1 entry
{
TAG_String(None): "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + <jar-data>
}
}
Wenn wir dieses Buch über BiblioCraft auf der Festplatte speichern, werden alle Padding-Daten durch sein Format "regeneriert", was unsere JAR-Datei wieder gültig macht.
Und nun wird beim nächsten Neustart des Servers (selbst bei einem normalen Neustart oder wenn Sie einen Absturz verursachen) unsere Mod-JAR geladen und unser Code ausgeführt.
In BiblioPOC/tools/ gibt es ein Python3-Skript, das bei der Generierung einer gültigen Nutzlast hilft.
Die Argumente sind:
python3 create_payload.py [java_file] [forge_jar_location] [class_name] [output_dir]
Dann führen Sie im Spiel, während Sie einen BiblioCraft-Atlas halten, /jarpoccommand [completed_jar_path] aus, wobei [completed_jar_path] der Pfad zu [output_dir]/completed.jar ist.