主に 3 つの部分で構成されます:
1. Java のリフレクション機構
2. Java のシリアル化処理
3. Java のリモート コード実行
Java のリフレクションとコード実行
まず、Java を使用して電卓プログラムを呼び出す簡単な例を見てみましょう:
import java.io.IOException; import java.lang.Runtime; public class Test { public static void main(String[] args) { Runtime env = Runtime.getRuntime(); String cmd = "calc.exe"; try { env.exec(cmd); } catch (IOException e) { e.printStackTrace(); } } }
java.lang パッケージから Runtime クラスをインポートし、その getRuntime メソッドを呼び出して Runtime オブジェクトを取得します。このオブジェクトは、JVM 仮想マシンの実行ステータスの処理に使用できます。次に、その exec メソッドを呼び出し、文字列をパラメータとして渡します。
この時点で、ローカル コンピュータ上の電卓プログラムが起動します。
Java のリフレクション メカニズムを使用して上記のコードを書き直してみましょう。 Java のイントロスペクション メカニズムを通じて、コードを動的に呼び出して、サーバー側のブラックリストの処理を回避できます。
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test { public static void main(String[] args) { try { Class<?> cls = Class.forName("java.lang.Runtime"); String cmd = "calc.exe"; try { Method getRuntime = cls.getMethod("getRuntime", new Class[] {}); Object runtime = getRuntime.invoke(null); Method exec = cls.getMethod("exec", String.class); exec.invoke(runtime, cmd); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } catch (ClassNotFoundException e1) { e1.printStackTrace(); } } }
上記のコードは非常に面倒に見えますが、実際には難しくありません。まず、Class.forName を通じて文字列をパラメータとして渡します。これにより、Class のインスタンスが返されます。その機能は、対応する名前に基づいて対応するクラスを見つけることです。
次に、Class インスタンスの getMethod メソッドを使用して、対応するクラスの getRuntime メソッドを取得します。クラスにはパラメータがないため、null に設定するか、匿名クラスを使用して処理します。
Method getRuntime = cls.getMethod("getRuntime", new Class[] {});
あとは取得したメソッドインスタンスのinvokeメソッドで対応するクラスメソッドを呼び出しますが、パラメータはないのでnullを渡すだけです。同様に、exec メソッドを取得します。
Java シリアル化処理
Java でのシリアル化処理の場合、対応するクラスは Serializable インターフェイスを実装する必要があります。例:
import java.io.Serializable; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class Reader implements Serializable { private static final long serialVersionUID = 10L; private void readObject(ObjectInputStream stream) { System.out.println("foo...bar..."); } public static byte[] serialize(Object obj) { //序列化对象 ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream output = null; try { output = new ObjectOutputStream(out); output.writeObject(obj); output.flush(); output.close(); } catch (IOException e) { e.printStackTrace(); } return out.toByteArray(); } public static Object deserialize(byte[] bytes) { //反序列化处理 ByteArrayInputStream in = new ByteArrayInputStream(bytes); ObjectInputStream input; Object obj = null; try { input = new ObjectInputStream(in); obj = input.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return obj; } public static void main(String[] args) { byte[] data = serialize(new Reader()); //对类自身进行序列化 Object response = deserialize(data); System.out.println(response); } }
ここでは、このクラスの readObject メソッドをオーバーライドして、テスト用のオブジェクトを読み取ります。 2 つの最も重要な関数は Serialize と Deserialize で、それぞれシリアル化と逆シリアル化に使用されます。
このうち、serialize メソッドはパラメータとしてオブジェクトを渡す必要があり、その出力結果はバイト配列になります。このクラスでは、オブジェクト出力ストリーム ObjectOutputStream は主に ByteArrayOutputStream のパッケージ化に使用され、次に writeObject メソッドを使用してオブジェクトをそれに書き込みます。最後に、ByteArrayOutputStream インスタンスの toByteArray メソッドを通じてバイト配列を取得します。
デシリアライズ メソッドでは、バイト配列を渡す必要があり、戻り値は Object オブジェクトです。前のシリアライズ関数と同様に、この時点では ByteArrayInputStream を使用してバイト配列を受け取り、次に ObjectInputStream を使用して ByteArrayInputStream をラップし、その readObject メソッドを呼び出して Object オブジェクトを取得して返します。
##このクラスを実行すると、次の結果が得られます:##Java リモート通信と送信 #Java コードのリモート送信とリモート コード実行を実現するには、RMI、RPC、その他の方法を使用できます。ここでは、サーバー側とクライアント側の処理に Socket を使用します。
最初はサーバー側で、ローカル ポート 8888 をリッスンします。コードは次のとおりです:import java.net.Socket; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; public class Server { public static void main(String[] args) throws ClassNotFoundException { int port = 8888; try { ServerSocket server = new ServerSocket(port); System.out.println("Server is waiting for connect"); Socket socket = server.accept(); InputStream input = socket.getInputStream(); byte[] bytes = new byte[1024]; int length = 0; while((length=input.read(bytes))!=-1) { String out = new String(bytes, 0, length, "UTF-8"); System.out.println(out); } input.close(); socket.close(); server.close(); } catch (IOException e) { e.printStackTrace(); } } }ポートを渡すことで ServerSocket クラスをインスタンス化し、この時点で次に、その accept メソッドを呼び出してクライアントのリクエストを受け取ります。このときソケットオブジェクトを取得し、ソケットオブジェクトのgetInputStreamメソッドで入力ストリームを取得し、長さ1024のバイト配列を指定します。 次に、ソケットの read メソッドを呼び出して指定された長さのバイト シーケンスを読み取り、そのバイト配列を String コンストラクターを通じて文字列に変換して出力します。このようにして、クライアントから送信されたコンテンツを取得します。 クライアントの場合、コードは次のようになります。
import java.io.IOException; import java.net.Socket; import java.io.OutputStream; public class Client { public static void main(String[] args) { String host = "192.168.1.108"; int port = 8888; try { Socket socket = new Socket(host, port); OutputStream output = socket.getOutputStream(); String message = "Hello,Java Socket Server"; output.write(message.getBytes("UTF-8")); output.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }クライアントでは、接続する IP アドレスとポートを Socket オブジェクト経由で渡し、それをソケット オブジェクトの getOutputStream メソッド 出力ストリーム。出力をサーバーに送信するために使用されます。これは単なるデモンストレーションであるため、ローカル ホスト IP が使用されます。実際のアプリケーションでは、特定の外部ネットワーク ホストの IP と開いているポートがわかっていて、現在のホストに対応する脆弱性がある場合、同様の方法を使用してこれを実現することもできます。 ここでは、UTF-8 でエンコードされた文字列に送信されるコンテンツを設定します。出力ストリームの write メソッドで、文字列の getBytes を通じてエンコードを指定し、対応するバイト配列に変換します。 . 送信します。 通常の状況では、サーバーを実行してからクライアントを実行すると、サーバー側で次の出力を取得できます。
Server is waiting for connect Hello,Java Socket Server
Java反序列化与远程代码执行
下面我们通过Java反序列化的问题来实现远程代码执行,为了实现远程代码执行,我们首先在Reader类中添加1个malicious方法,其代码为:
public Object malicious() throws IOException { Runtime.getRuntime().exec("calc.exe"); System.out.println("Hacked the Server..."); return this; }
在该方法中我们使用之前的介绍调用宿主机器上的计算器程序,然后输出1个相关信息,最后返回当前类。
之后是对服务器端的代码进行如下的修改:
while((length=input.read(bytes))!=-1) { Reader obj = (Reader) Reader.deserialize(bytes); obj.malicious(); }
我们在接收到客户端对应的字符串后对其进行反序列处理,之后调用某个指定的函数,从而实现远程代码的执行。而在客户端,我们需要对其进行序列化处理:
Reader reader = new Reader(); byte[] bytes = Reader.serialize(reader); String message = new String(bytes); output.write(message.getBytes());
下面我们在宿主机器上运行服务器端程序,之后在本地机器上运行客户端程序,当客户端程序执行时,可以看到类似如下的结果:
可以看到,我们成功的在宿主机器上执行了对应的命令执行。
总结
为了实现通过Java的反序列问题来实现远程代码执行的漏洞,我们需要编写1个有恶意代码注入的序列化类。之后在客户端将恶意代码序列化后发送给服务器端,而服务器端需要调用我们期望的方法,从而触发远程代码执行。
为了避免服务器端进行一些安全处理,我们可以采用反射的方式来逃逸其处理。
这里只是1个简化的过程,更加实用的过程可以参考Apache Common Collections的问题导致的Weblogic漏洞CVE-2015-4852及Jboss的漏洞CVE-2015-7501。
推荐相关文章教程:web安全教程
以上がJavaのデシリアライゼーションに起因するリモートコード実行脆弱性の原理の分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

MinGW - Minimalist GNU for Windows
このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

ドリームウィーバー CS6
ビジュアル Web 開発ツール

WebStorm Mac版
便利なJavaScript開発ツール

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

メモ帳++7.3.1
使いやすく無料のコードエディター
