payloads_JRMPListener学习
前言
好吧,我建议你去跟一次RMI的操作你就懂的这个了。推荐文章:https://paper.seebug.org/1251/
简介
JRMP是一个Java远程方法协议,该协议基于TCP/IP之上,RMI协议之下。也就是说RMI该协议传递时底层使用的是JRMP协议,而JRMP底层则是基于TCP传递。
RMI默认使用的JRMP进行传递数据,并且JRMP协议只能作用于RMI协议。当然RMI支持的协议除了JRMP还有IIOP协议,而在Weblogic里面的T3协议其实也是基于RMI去进行实现的。
攻击流程:
1.生成payloads/JRMPListener发送给服务器,当服务器反序列化payloads/JRMPListener后,即会开启一个端口进行监听。
2.使用exploit/JRMPClient模块发送payload,服务器就会把payload进行反序列化,从而完成进行攻击。
可以直接在JRMPListener中设置参数断点启动也可以在GeneratePayload下设置都没问题
第一行就是获取端口号直接看第二行,我们直接转到工具类Reflections
而下面的setAccessible(objCons);
这个其实就是修改暴力反射的一个方法类
继续跟踪ReflectionFactory.getReflectionFactory().newConstructorForSerialization()
实际上就借助ReflectionFactory.getReflectionFactory()
工厂方法在这里就是返回了ReflectionFactory的实例对象,继续跟踪newConstructorForSerialization
传入的第一个值是ActivationGroupImpl.class
,第二个则是它的父类RemoteObject
的构造方法getDeclaringClass()
方法返回表示声明由此Method
对象表示的方法的类的Class
对象。然后通过字节码的方式生成constructorAccessor
后面涉及底层感觉自己有点说不清楚了,感兴趣师傅自行查看。
这里用@洋洋师傅的一个demo来说明一下
新建一个Person类,创建其有参和无参构造方法
public class Person{
private String name;
public Person() {
System.out.println("Person无参构造方法");
}
public Person(String name) {
this.name = name;
System.out.println("调用有参构造" + name.toString());
}
}
新建一个User类,继承Person
public class User extends Person{
public void eat(){
System.out.println("eat...");
}
}
新建Test类
public class Test1 {
public static void main(String[] args) throws Exception {
//获得Person的有参构造函数
Constructor<Person> personConst = Person.class.getDeclaredConstructor(String.class);
//获得Person的Constructor对象
Constructor constructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(
User.class,
personConst
);
//第一个参数是需要创建对象的Class,比如User.class
//第二个参数是对象的父类的构造方法,比如Person.class.getDeclaredConstructor()或者 //Person.class.getDeclaredConstructor(String.class)
//constructor.setAccessible(true);
//实例化对象,给person构造方法传入xxx,并转型成User
User user = (User) constructor.newInstance("xxx");
user.eat();
}
}
运行结果
调用有参构造xxx
eat...
可以看到,虽然User类没有构造方法,但是依然能通过父类创建出来,并且调用其方法。
newConstructorForSerialization
大致就是返回一个无参的constructor对象,但是绝对不会与原来的constructor冲突,被称为munged 构造函数
所以会创建出ActivationGroupImpl
对象,把获取到的Constructor进行了newInstance,并且传入了consArgs,即UnicastServerRef
对象,然后对其向下转型成了ActivationGroupImpl
类(T即为ActivationGroupImpl)所以返回值也是ActivationGroupImpl
对象
返回到getObject
类49行调用了Reflections
类得getField方法,把UnicastRemoteObject
中的port值设置成了jrmpPort
然后返回ActivationGroupImpl
对象然后进行序列化操作。
gadget链调试
因为ActivationGroupImpl
对象没有readObject方法所以它要从父类找readobject最终找到UnicastRemoteObject
所以它是入口点。
进入reexport
调用exportObject
方法并且传入this和port 这里的this,实际上是ActivationGroupImpl
,因为前面进行了向上转型。跟踪exportObject
继续跟进到UnicastServerRef#exportObject
然后LiveRef传入port里面会进行new ObjID的操作
继续跟进
new一个UID赋值给space成员变量,随机获取一个值赋值给objNum
介绍下ObjID
ObjID
用于标识导出到RMI运行时的远程对象。 导出远程对象时,将根据用于导出的API来隐式或明确地分配一个对象标识符。
执行完返回到LiveRef进行重载
里面再一次调用重载方法,并且在传递的第二个参数调用了TCPEndpoint.getLocalEndpoint
并且传入端口进行获取实例化对象。继续跟踪。
其实就是获取本地地址,如果端口为0就返回共享的默认值。
然后返回到exportObject。因为前面都是一些赋值没啥好说的了。
进入重载
调用ref.exportObject(target)
传入前面创建的实例对象
继续调用transport.exportObject(target);
到了这一步就调用了listen()
进行启动监听。
RMI是基于JRMP实现的,当然我们也是可以向目标机发送 exploit/RMIRegistryExploit ,然后自己服务器挂起 exploit/JRMPListener,意思就是我们去打别人可能别人反手打你一巴掌。
其他的分析可以查看orich1写的太好了
https://xz.aliyun.com/t/2650#toc-3
https://xz.aliyun.com/t/2651#toc-0
https://blog.csdn.net/whatday/article/details/106971531
参考
https://xz.aliyun.com/t/2649#toc-5
https://xz.aliyun.com/t/10036#toc-2