java审计-rmi协议

RMI

RMI是Remote Method Invocation,远程方法调用。是让java虚拟机上的对象调用另一个java虚拟机中对象上的方法。

RMI Server:

package org.vulhub.RMI;

import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class RMIServer {
//继承了java.rmi.Remote接口,其中定义我们要远程调用的函数hello
public interface IRemoteHelloWorld extends Remote {
public String hello() throws RemoteException;
}
//实现此接口的类
public class RemoteHelloWorld extends UnicastRemoteObject implements IRemoteHelloWorld {
protected RemoteHelloWorld() throws RemoteException {
super();
}
public String hello() throws RemoteException{
System.out.println("call from");
return "hello world";
}
}
//主类,用来创建Registry,并将上面的类实例化后绑定到一个网址
private void start() throws Exception{
RemoteHelloWorld h=new RemoteHelloWorld();
LocateRegistry.createRegistry(1099);
Naming.rebind("rmi://127.0.0.1:1099/Hello",h);
}

public static void main(String[] args) throws Exception{
new RMIServer().start();

}
}

分析:

LocateRegistry.createRegistry(1099);
Naming.bind("rmi://127.0.0.1:1099/Hello",new RemoteHelloWorld());

第一行创建并运行RMI Registry,第二行将Rexxx对象绑定到Hello这个名字上。

Naming.bind的第一个参数是一个URL,形如:rmi://host:port/name。其中,host和port就是RMI Registry的地址和端口,name是远程对象的名字。

如果RMI Registry在本地运行,则host和port可以省略,默认为1099

RMI client:

package org.vulhub.Train;

import org.vulhub.RMI.RMIServer;

import java.rmi.Naming;

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

通信分析

整个通信过程进行了两次TCP握手,建立了两次TCP连接,首先客户端连接Registry,并在其中寻找Name是Hello的对象,这个对应数据流中的Call消息,然后Registry返回一个序列化的数据,这个就是找到的Name=Hello的对象,这个对应数据流中的ReturnData消息;客户端反序列化该对象,发现该对象是一个远程对象

RMI Registry就像⼀个⽹关,他⾃⼰是不会执⾏远程⽅法的,但RMI Server可以在上⾯注册⼀个Name到对象的绑定关系;RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMIServer;最后,远程⽅法实际上在RMI Server上调⽤。

安全问题

1.list+lookup远程调用

list方法可以列出目标上所有绑定的对象:

String[] s= Naming.list("rmi://xxx");

而lookup作用就是获得某个远程对象,若存在危险方法,则可进行调用

2.RMI利用codebase执行任意代码

codebase:是一个地址,告诉java虚拟机我们应该从哪个地方去搜索类,通常是远程URL、比如http、ftp等。

如果我们指定codebase=http://exampl.com/,然后加载org.vulhub.example.Example类,则java虚拟机会下载这个文件http://example.com/org/vulhub/example/Example.class ,并作为Example类的字节码。

RMI流程中,反序列化时发现一个对象,先会在CLASSPAATH下寻找,若没找到,则会远程加载codebase中的类。

攻击条件

PS:当java.rmi.server.useCodebaseOnly为true时,java虚拟机将只信任预先配置好的codebase。

classAnnotations

可以使用SerializationDumper查看数据包中的java反序列化内容

通过修改classAnnotations可以控制codebase。

Author

vague huang

Posted on

2022-03-30

Updated on

2022-04-04

Licensed under

Comments