Loading

ysoserial payloads/JRMPListener

payloads/JRMPListener

JRMPListener的攻击流程:

1.生成payloads/JRMPListener发送给服务器,当服务器反序列化payloads/JRMPListener后,即会开启一个端口进行监听。

2.使用exploit/JRMPClient模块发送payload,服务器就会把payload进行反序列化,从而完成进行攻击。

直接通过ysoserial源码调试JRMPListener类来理解payloads/JRMP模块的利用原理

生成Payload

打开idea,设置启动参数,主类为ysoserial.GeneratePayload,参数为JRMPListener 1199

image-20210722212414521

断点启动,GeneratePayload类中调用了JRMPListener的getObject方法

image-20210722212529435

跟进JRMPListener#getObject

image-20210722213249142

第一行jrmpPort获取了端口号1199

重点讲一下第二部分

image-20210722213440945

46行new了一个UnicastServerRef对象并且把jrmpPort端口号传入了进去

然后调用了一个Reflections工具类构造了UnicastRemoteObject对象

Reflections构造对象

在前面调用了Reflections#createWithConstructor方法,利用ActivationGroupImpl.class、RemoteObject.class、RemoteRef.class、UnicastServerRef构造了一个对象

image-20210722212046426

现在转到工具类Reflections.java

image-20210722213926944

debug来解释下这个工具类的作用

1.获取有参构造方法

在这里获取了RemoteObject类的有参构造,参数为RemoteRef.class

image-20210722152606420

2.暴力反射

setAccessible(objCons);

3.获得Constructor

在这里用了ReflectionFactory.newConstructorForSerialization来创建无参构造方法

下面用一个简单的demo来解释下这段代码的作用

ReflectionFactory.getReflectionFactory().newConstructorForSerialization()

新建一个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
        );
        //constructor.setAccessible(true);
        //实例化对象,给person构造方法传入xxx,并转型成User
        User user = (User) constructor.newInstance("xxx");
        user.eat();
    }
}

运行结果:

调用有参构造xxx
eat...

可以看到,虽然User类没有构造方法,但是依然能通过父类创建出来,并且调用其方法。

demo解读

解释下在这里newConstructorForSerialization()传入的参数

第一个参数是需要创建对象的Class,比如User.class
第二个参数是对象的父类的构造方法,比如Person.class.getDeclaredConstructor()或者Person.class.getDeclaredConstructor(String.class)

因为不能确定一个类中是否有无构造函数,所以在这里yososeroal中利用ReflectionFactory.newConstructorForSerialization创建对象的话,比直接反射创建对象适用性要高,并不会产生报错。

继续回到ysoserial源码中,就看的很明白了

ReflectionFactory.getReflectionFactory()获取了ReflectionFactory对象的实例

image-20210722154026454

之后进行了newConstructorForSerialization()

传入的第一个值是ActivationGroupImpl.class,第二个则是它的父类RemoteObject的构造方法。以下是ActivationGroupImpl的继承关系图

image-20210722210927942

4.创建出ActivationGroupImpl对象

最后一行把获取到的Constructor进行了newInstance,并且传入了consArgs,即UnicastServerRef对象

image-20210722211428983

UnicastServerRef对象通过父类有参构造函数,赋值给了ref

image-20210722211626480

然后对其向下转型成了ActivationGroupImpl类(T即为ActivationGroupImpl)

image-20210722211225244

最后返回值就是一个ActivationGroupImpl对象了

回到JRMPListener.java,此时的uro为ActivationGroupImpl

image-20210722214441626

跟进49行,这里又调用了Reflections类得getField方法,把UnicastRemoteObject中的port值设置成了jrmpPort,也就是1199。

最后返回的payload对象就是ActivationGroupImpl

然后在GeneratePayload中把ActivationGroupImpl进行了序列化操作

image-20210724154835914

gadget链分析

以下是ysoserial给出给出得gadget链

* Gadget chain:
* UnicastRemoteObject.readObject(ObjectInputStream) line: 235
* UnicastRemoteObject.reexport() line: 266
* UnicastRemoteObject.exportObject(Remote, int) line: 320
* UnicastRemoteObject.exportObject(Remote, UnicastServerRef) line: 383
* UnicastServerRef.exportObject(Remote, Object, boolean) line: 208
* LiveRef.exportObject(Target) line: 147
* TCPEndpoint.exportObject(Target) line: 411
* TCPTransport.exportObject(Target) line: 249
* TCPTransport.listen() line: 319

在前面生成payload得时候返回的是ActivationGroupImpl对象,但是其对象本身是没有readObject方法的,所以从父类找readObject最终找到UnicastRemoteObject

攻击流程:

payload发送给服务器,服务器会对其进行反序列化,触发UnicastRemoteObject#readObject

image-20210724155711013

进入reexport

image-20210724155755910

这里执行了exportObject,传入了ActivationGroupImpl对象和port

继续跟进到UnicastServerRef#exportObject,这里后面就是之前分析RMI时候操作了。

image-20210724160035813

在this.ref.exportObject执行后会最终到TCPTransport类的exportObject方法

image-20210724160238725

跟进listen

image-20210724160656221

调用TCPEndpoint#newServerSocket方法,会开启端口监听

image-20210724161613236

如果服务器反序列化了该payload,就会开启一个监听。

这里就做了一个很简单的调试,更具体的可以看我之前写的文章,RMI源码调试

参考链接:

https://www.cnblogs.com/nice0e3/p/14333695.html

https://blog.csdn.net/whatday/article/details/106971531

posted @ 2021-07-28 10:53  Atomovo  阅读(340)  评论(0编辑  收藏  举报