一命通关RMI三端反序列化攻击及JRMP高版本绕过

2024-09-30 13 0

说实话我每次看到RMI的流程我都觉得脑袋疼。而且分析完了都不记得分析了个什么鸟。

或许这次会好一点。

RMI远程类调用

RMI的作用就是客户端调用服务端的远程类。

我们开两个项目,一个做客户端,一个做服务端。用jdk8u65

RMIServer

定义一个继承了Remote的接口RemoteInterface

importjava.rmi.Remote;
importjava.rmi.RemoteException;

publicinterfaceRemoteInterfaceextendsRemote{
publicStringsayHello(Stringname) throwsRemoteException;
}

一个实现该接口的类RemoteImpl,需要继承UnicastRemoteObject

importjava.rmi.RemoteException;
importjava.rmi.server.UnicastRemoteObject;

publicclassRemoteImplextendsUnicastRemoteObjectimplementsRemoteInterface{
protectedRemoteImpl() throwsRemoteException{
}

publicStringsayHello(Stringname) {
System.out.println("Hello "+name);
return"Hello "+name;
}
}

RemoteImpl必须加入一个调用了父类构造函数的构造函数。

这里的无参构造函数实际上会自动向第一行插入一条隐式的super(),如下。而不写构造函数是自动生成无参构造函数,不满足调用super(args...);的要求,所以报错。

protectedRemoteImpl() throwsRemoteException{
super();
}

向外开放服务的RMIServer。需要LocateRegistry.createRegistry(1099),开放注册中心到1099端口。并把类绑定到注册中心。

importjava.rmi.AlreadyBoundException;
importjava.rmi.RemoteException;
importjava.rmi.registry.LocateRegistry;
importjava.rmi.registry.Registry;

publicclassRMIServer{
publicstaticvoidmain(String[] args) throwsRemoteException, AlreadyBoundException{
RemoteInterfaceremoteImpl=newRemoteImpl();
Registryregistry=LocateRegistry.createRegistry(1099);
registry.bind("remoteImpl", remoteImpl);
}
}

RMIClient

需要一个相同的接口RemoteInterface

importjava.rmi.Remote;
importjava.rmi.RemoteException;

publicinterfaceRemoteInterfaceextendsRemote{
publicStringsayHello(Stringname) throwsRemoteException;
}

使用服务端远程类的,客户端RMIClient

importjava.rmi.NotBoundException;
importjava.rmi.RemoteException;
importjava.rmi.registry.LocateRegistry;
importjava.rmi.registry.Registry;

publicclassRMIClient{
publicstaticvoidmain(String[] args) throwsRemoteException, NotBoundException{
Registryregistry=LocateRegistry.getRegistry("127.0.0.1", 1099);
RemoteInterfaceremoteImpl=(RemoteInterface) registry.lookup("remoteImpl");
System.out.println(remoteImpl.sayHello("RMI"));
}
}

先开服务端,再开客户端。客户端就能调用到服务端上的方法sayHello

源码分析

移步视频:

https://www.bilibili.com/video/BV1L3411a7ax?p=1&vd_source=732f44595cd3e361ab78ff559f3c5ab5

此处概述+只给利用点和利用方式,因为分析了也记不住。我直接偷偷分析,挂上来我自己都不看。

偷个包浆RMI通信老图

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图

先从服务端开始

服务端

RemoteInterface remoteImpl = new RemoteImpl();

从UnicastServerRef一路跟到了TCPEndpoint.getLocalEndpoint,获取了IP

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图1

在UnicastServerRef.exportObject完成了创建远程对象的主要逻辑,前面都是层层封装,没什么好看的。

生成了一个stub并赋值,这是服务端stub

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图2

实际上,赋值调用的Util.creatProxy()内部,就是生成了一个代理类。等于说stub就是个代理类,如下

handler来自RemoteObjectInvocationHandler,看到该类满足代理handler的要求,继承了InvocationHandler

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图3

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图4

还记得动态代理吗,调用代理类的方法会自动走进handler的invoke方法(虽然这里没调用,但是为后面你使用sayHello的时候做了铺垫)

这里RemoteObjectInvocationHandler.invoke就是加了个判断的普通invoke执行方法

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图5

下面一堆调用,从ref.exportObject跟到TCPTransport.exportObject。listen()方法开始监听。跟进去看看

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图6

从ep中提取了Endpoint(翻译为终端)和端口。ep就是之前TCPEndpoint.getLocalEndpoint生成的。然后调用了newServerSocket

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图7

在newServerSocket中,createServerSocket新建了一个Websocket。并且监听端口为0,会调用setDefaultPort获取端口。从前面的调试信息也可以看到,一路过来端口都是默认的0

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图8

setDefaultPort内部,获取了所有的本地未开放端口,并异步循环取最后一个端口。就是随机取一个端口啦

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图9

从这里开始,port就有值了。

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图10

继续运行到UnicastServerRef.exportObject。在get不到值的时候会向map里put键值。

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图11

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图12

虽然是put进去了,但是只有个类名。方法名还需要跳转到UnicastServerRef$HashToMethod_Maps.computeValue中获取

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图13

从接口中循环获取方法名,设为可访问,put进map中

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图14

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图15

最后我们生成的remoteImpl对象,有LiveRef包含的host和一个随机的端口号,还有装填了方法的hashToMethod_Map。这些都封装在UnicastServerRef的一个对象中。创建了一个代理类stub,但是return时并没有存储,只是写了个逻辑在这。

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图16

但是服务端开随机的端口号,客户端怎么知道这个端口号来获取类呢?下一步就是解决这个问题

Registry registry = LocateRegistry.createRegistry(1099);

新建了一个指定端口为1099空的LiveRef对象

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图17

在随后的setup函数中,同样的来到UnicastServerRef.exportObject完成代理对象stub的创建。这是注册端stub

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图18

注意,这里创建stub时,跟进Util.creatProxy->stubClassExists()。

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图19

如果存在remoteClass_Stub类(在这里就是RegistryImpl_Stub类),返回true

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图20

可以看到该类是存在的

一命通关RMI三端反序列化攻击及JRMP高版本绕过插图21

在creatStub创建了RegistryImpl_Stub类


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

GShark:一款针对GitHub的敏感信息安全扫描与审计工具
BotKube:一款针对Kubernetes集群的安全监控与调试部署工具
Apache OFBiz SSRF漏洞CVE-2024-45507分析
Chainsaw:一款基于Windows事件日志的信息安全取证工具
SEMA:一款基于符号执行的恶意软件分析工具
简单易懂!Java应用中的不安全反序列化

发布评论