Interpretation of Java security’s native readObject method

Interpretation of Java security's native readObject method

0x00 Preface

In the analysis of Shiro in the previous article, it was encountered that Shiro rewrites the resolveclass of objectinputstream, resulting in some utilization chains implemented based on invokertransformer, which cannot be used, because it needs to define an invokertransformation array, and an error will be reported when the array is passed into Shiro's rewritten resolveclass method. But before that, I didn't interpret and analyze the readObject method. So I don't know his specific realization. When analyzing the utilization chain, we only know that objectinputstream is called After the readObject method, if the readObject is rewritten, the rewritten readObject method will be called, but we don't know how to implement it internally. Now let's analyze the function implementation of readObject.

0x01 readObject method analysis

In front, first paste an execution flowchart of readObject, which is a deserialization execution flowchart of Weblogic. The first readObject is ignored directly and will be explained in the next article Weblogic.

Here, write a piece of test code to perform deserialization operation, and then perform dynamic tracking.

User entity class:

package com.nice0e3;

import java.io.Serializable;

public class User implements Serializable {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ",age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public User() {
    }

    public User(String name,int age) {
        this.name = name;
        this.age = age;
    }
}

Readtest class:

package com.nice0e3;

import java.io.*;

public class ReadTest {
    public static void main(String[] args) throws IOException,ClassNotFoundException {
        User user = new User();
        user.setName("nice0e3");
        user.setAge(20);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
        oos.writeObject(user);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.txt"));
        Object o = ois.readObject();
    }
}

Then drop the breakpoint in objectinputstream In the readObject method, perform dynamic tracking of the execution test class code.

Here, a judgment is made on enableoverride. If it is not false, the readobjectoverride method will be returned, and the value is defined as false in the constructor.

This is the next step

After calling the readobject0 method, select follow-up to check the internal implementation.

Here, the first byte of serialization information will be obtained. If it is TC_ Reset will call bin Readbyte() and handlereset(); method.

View TC_ Reset content.

After the value is converted to byte, it is 121, and the first byte of our serialized data is 151. Here, we skip and do not execute it.

Next, a switch is defined in the code to make a judgment, TC_ The value of object is just 115 after conversion. Then it will go to this step.

The readordinaryobject method will be called to follow up.

In this method, you will also call the readclassdesc method, and continue to follow up.

It's interesting to see the discovery here. Get the second byte of our serialized data, and then switch again. This time, go to the readnonproxydes method. Follow up!

Here, the resolveclass method is called again, and then the readdesc parameter is passed in. Or follow-up.

Back here

Class.forName(name,false,latestUserDefinedLoader());

The latestuserdefinedloader () method returns sun misc. VM. The latestuserdefinedloader() description specifies the loader.

Return to the readordinaryobject method to continue the analysis.

Directly locate to this step, and this method implements the deserialization operation.

Slotdesc Hasreadobjectmethod() gets the attribute readobjectmethod. If the deserialized class does not override readobject(), the attribute readobjectmethod is empty. If the class overrides readobject(), it will enter the if

slotDesc.invokeReadObject(obj,this);

If the readObject () method is overridden, you have reached this step

0x02 Shiro resolveclass method analysis

In Shiro, the resolveclass method has been rewritten, which makes most of the use chains unusable. Take a look at the implementation of this method.

Classutils. Is called here The forname method.

Here is the thread called_ CL_ ACCESSOR. Loadclass, check thread_ CL_ What is accessor.

Follow up and check this category.

The getclassloader method is called here to obtain the class loader, and the parallelwebappclassloader is obtained here, so the following call must be parallelwebappclassloader loadClass

Some supplements in the above contents:

TC_NULL描述符表示空对象引用

TC_REFERENCE描述符表示引用已写入流的对象

TC_PROXYCLASSDESC是新的代理类描述符

TC_CLASSDESC是新的类描述符

Reference articles

https://blog.csdn.net/niexinming/article/details/106665753

https://www.anquanke.com/post/id/192619#h2-2

0x03 end

In fact, under the foreshadowing of some CC chain debugging, you will be more skilled in debugging other vulnerabilities. This paper also makes a better groundwork for the following.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>