
CVE-2023-29478 - Exploit di manipolazione dei file/esecuzione remota di codice che colpisce BiblioCraft versioni precedenti alla v2.4.6
Questo metodo richiede solo BiblioCraft! Non troppi trucchi e nessun'altra mod è necessaria per ottenere l'esecuzione di codice!
La descrizione originale incentrata su CoreTweaks si trova qui. Consiglio di leggerla prima perché potrei saltare alcuni dettagli importanti per quanto segue.
L'esecuzione di codice è possibile attraverso molteplici metodi. Questo riguarda BiblioCraft 1.7.10 v1.11.7 e BiblioCraft 1.12.2 v2.4.5 (confermato) e probabilmente tutte le versioni di BiblioCraft precedenti alla v2.4.6 (non confermato, non testato)
Le patch esistenti che correggono il bug di path traversal impediranno comunque questo nuovo percorso di esecuzione del codice.
Questa volta ci concentriamo sul salvataggio dei libri scritti di Vanilla.
I libri scritti vengono salvati in world/books/<author-name>, <book-title>.
Il formato di questi libri è, per riga:
Continuando dall'inizio, le pagine del libro iniziano con un marcatore #pgx<page-number> e dalla riga successiva fino al marcatore successivo fanno parte di quella pagina. BiblioCraft aggiungerà sempre un newline alla fine delle righe, anche se non segue nulla.
Come prima, possiamo controllare sia il titolo del libro che il nome dell'autore tramite il tag NBT del libro, oltre a eseguire il path traversal.
Il posto in cui scriveremo è la cartella mods/. Le mod in questa directory sono solitamente archiviate come file JAR. Una proprietà interessante dei file JAR è che sono in realtà solo file ZIP mascherati.
Allora, perché ci aiuta?
I file ZIP hanno una proprietà interessante: possono ancora essere validi anche se hanno dati spazzatura preposti o apposti.
Il record EOCD (End of central directory) all'interno di un file ZIP è posto alla fine.
Esso consiste (approssimativamente) di una magia/firma 0x06054b50, oltre al numero, alla dimensione e all'offset dei record della directory centrale all'interno dell'archivio.
L'ultimo campo in un record EOCD è un commento con prefisso di lunghezza, che può essere quasi qualsiasi sequenza di byte che non sia la magia (non testato).
(Penso che i parser di file ZIP partano dalla fine e cerchino la firma EOCD per analizzare l'archivio, ma non ne sono del tutto sicuro.)
Se tutto ciò di cui il parser ZIP ha bisogno per trovare i file all'interno dell'archivio è il record EOCD, e se qualsiasi dato (con limitazioni) può seguire il record EOCD, allora possiamo creare un ZIP valido anche se ci sono altri dati all'interno del file.
(Grande credito a https://github.com/c0ny1/ascii-jar! Ho usato pesantemente questi strumenti!)
Per prima cosa, dobbiamo strutturare il nostro payload. Forge caricherà le classi come mod se hanno l'annotazione speciale @Mod, quindi lo useremo qui.
Il nostro payload approssimativo:
@Mod(modid = "payload-mod")
public class Payload {
// ...
static {
System.out.println("Hello World!");
}
// ...
}
Per evitare problemi di codifica, usiamo uno script per creare un file JAR solo ASCII contenente solo la nostra classe payload.
Riempiamo l'inizio con PK\3\4.jar\n../../mods/\nprivate\n#pgx0\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Il motivo per cui prependiamo PK\3\4 (la firma dell'intestazione del file locale di ZIP) al riempimento è dovuto a una strana stranezza in Forge che non sono riuscito a riprodurre all'esterno, dove i file JAR vengono controllati per iniziare con questa firma, nonostante non sia un requisito di ZIP? Potrebbe essere un bug di Forge.
Usiamo la lunga stringa di 'A' e 'a' per assicurarci che l'offset sia maggiore di 255, garantendo che l'intero a 2 byte che codifica l'offset non abbia byte al di fuori dell'intervallo ASCII.
Riempiamo la fine con \n per evitare problemi con BiblioCraft che aggiunge newline.
Con il nostro nuovo jar imbottito, prendiamo tutti i byte successivi all'ultimo newline dei dati con cui abbiamo riempito l'inizio e creiamo una nuova stringa (la chiameremo <jar-data>) e creiamo un nuovo libro scritto con dati NBT:
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>
}
}
Quando salviamo questo libro su disco tramite BiblioCraft, tutti i dati di riempimento verranno "rigenerati" dal suo formato, rendendo di nuovo valido il nostro JAR.
E ora, al successivo riavvio del server (anche un normale riavvio, o se causi un crash) il nostro JAR mod verrà caricato e il nostro codice verrà eseguito.
In BiblioPOC/tools/ c'è uno script Python3 per aiutare a generare un payload valido.
Gli argomenti sono:
python3 create_payload.py [java_file] [forge_jar_location] [class_name] [output_dir]
Poi, in gioco mentre si tiene un atlante di BiblioCraft, esegui /jarpoccommand [completed_jar_path] dove [completed_jar_path] è il percorso di [output_dir]/completed.jar.