写一篇水文,没有什么技术,记录一次失败的探索
拒绝服务漏洞本身是鸡肋洞,但研究如何通过DoS到RCE是有意义的,我进行了一些尝试,虽然最终失败,但我认为有必要记录一下过程,算是学习的笔记
Java理论上不存在通过拒绝服务到RCE的可能性,因为无法操作底层,不能通过栈溢出写shellcode
但我找到了鸡肋的办法,确实能够从DoS到RCE,虽说鸡肋,但也可以拿来写一下
在hotspot中os模块的C++代码,找到system()函数的调用
xxxxxxxxxxint os::fork_and_exec(char* cmd) { const char * argv[4] = {"sh", "-c", cmd, NULL}; // ... NOT_IA64(syscall(__NR_execve, "/bin/sh", argv, environ);) IA64_ONLY(execve("/bin/sh", (char* const*)argv, environ);) // ...}全局搜索fork_and_exec函数,在vmError.cpp找到一处有意思的地方
xxxxxxxxxxvoid VM_ReportJavaOutOfMemory::doit() { static char buffer[O_BUFLEN]; // ... char* cmd; const char* ptr = OnOutOfMemoryError; while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr)) != NULL){ // ... os::fork_and_exec(cmd); }}发现命令字符串来自于OnOutOfMemoryError变量,在globals.hpp中找到:
在程序出现OOM的时候,执行用户自定义的某些脚本或命令
xxxxxxxxxxproduct(ccstrlist, OnOutOfMemoryError, "", "Run user-defined commands on first java.lang.OutOfMemoryError")
阅读Oracle相关文档后发现了触发的方式
xxxxxxxxxxjava -XX:OnOutOfMemoryError=calc.exe com.test.Start简单写一个OOME的类(模拟实际中可能出现的DoS漏洞)
xxxxxxxxxxpublic class Start { public static void main(String[] args) throws Exception { int[] a = new int[Integer.MAX_VALUE]; }}成功通过DoS触发RCE

以上内容是启动JVM时修改参数,我想找到一种在运行时修改参数的方式
昨天橙子酱师傅分享了一篇文章:如何在运行时修改JVM参数
我尝试阅读JVM源码找到其中的说明(在globals.hpp中)
xxxxxxxxxx// manageable flags are writeable external product flags.// They are dynamically writeable through the JDK management interface// (com.sun.management.HotSpotDiagnosticMXBean API) and also through JConsole.// These flags are external exported interface (see CCC). The list of// manageable flags can be queried programmatically through the management// interface.其中提到com.sun.management.HotSpotDiagnosticMXBean类可以修改参数
发现这是一个接口,提供了dumpHeap和get/set参数的方法
xxxxxxxxxxpublic interface HotSpotDiagnosticMXBean extends PlatformManagedObject { void dumpHeap(String var1, boolean var2) throws IOException; List<VMOption> getDiagnosticOptions(); VMOption getVMOption(String var1); void setVMOption(String var1, String var2);}使用该接口的实现类修改参数
xxxxxxxxxxnew HotSpotDiagnostic().setVMOption("OnOutOfMemoryError","calc.exe");报错如下
xxxxxxxxxxException in thread "main" java.lang.IllegalArgumentException: VM Option "OnOutOfMemoryError" is not writeable at sun.management.HotSpotDiagnostic.setVMOption(HotSpotDiagnostic.java:94) at com.example.testenv.Start.main(Start.java:10)
跟踪上文的报错信息,发现某处判断了修改参数类型不能是String
xxxxxxxxxxif (!(var4 instanceof String)) { throw new IllegalArgumentException("VM Option \"" + var1 + "\" is of an unsupported type: " + var4.getClass().getName());}
Flag.setStringValue(var1, var2);通过反射直接操作Flag类(该类的私有的)
xxxxxxxxxxClass<?> clazz = Class.forName("sun.management.Flag");Method method = clazz.getDeclaredMethod("getFlag", String.class);method.setAccessible(true);Object flag = method.invoke(null,"OnOutOfMemoryError");Method getVal = clazz.getDeclaredMethod("getValue");getVal.setAccessible(true);String value = (String) getVal.invoke(flag);// 成功打印之前设置的calc.exeSystem.out.println(value);尝试通过反射修改
xxxxxxxxxxClass<?> clazz = Class.forName("sun.management.Flag");Method setMethod = clazz.getDeclaredMethod("setStringValue", String.class, String.class);setMethod.setAccessible(true);setMethod.invoke(null,"OnOutOfMemoryError","notepad.exe");报错如下
xxxxxxxxxxCaused by: java.lang.IllegalArgumentException: This flag is not writeable. at sun.management.Flag.setStringValue(Native Method) ... 5 more错误来自于native方法
xxxxxxxxxx static synchronized native void setStringValue(String var0, String var1);
找到JNI相关的代码
xxxxxxxxxxJNIEXPORT void JNICALLJava_sun_management_Flag_setStringValue (JNIEnv *env, jclass cls, jstring name, jstring value){ jvalue v; v.l = value;
jmm_interface->SetVMGlobal(env, name, v);}该flag在JVM中已经固定,无法修改,所以在Java层面无论如何也是无法修改的
xxxxxxxxxxJVM_ENTRY(void, jmm_SetVMGlobal(JNIEnv *env, jstring flag_name, jvalue new_value)) // ... // 如果flag不可写则抛出IllegalArgumentException if (!flag->is_writeable()) { THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "This flag is not writeable."); } // ...JVM_END