写一篇水文,没有什么技术,记录一次失败的探索
拒绝服务漏洞本身是鸡肋洞,但研究如何通过DoS到RCE是有意义的,我进行了一些尝试,虽然最终失败,但我认为有必要记录一下过程,算是学习的笔记
Java理论上不存在通过拒绝服务到RCE的可能性,因为无法操作底层,不能通过栈溢出写shellcode
但我找到了鸡肋的办法,确实能够从DoS到RCE,虽说鸡肋,但也可以拿来写一下
在hotspot
中os
模块的C++
代码,找到system()
函数的调用
xxxxxxxxxx
int 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
找到一处有意思的地方
xxxxxxxxxx
void 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
的时候,执行用户自定义的某些脚本或命令
xxxxxxxxxx
product(ccstrlist, OnOutOfMemoryError, "",
"Run user-defined commands on first java.lang.OutOfMemoryError")
阅读Oracle
相关文档后发现了触发的方式
xxxxxxxxxx
java -XX:OnOutOfMemoryError=calc.exe com.test.Start
简单写一个OOME
的类(模拟实际中可能出现的DoS
漏洞)
xxxxxxxxxx
public 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
参数的方法
xxxxxxxxxx
public 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);
}
使用该接口的实现类修改参数
xxxxxxxxxx
new HotSpotDiagnostic().setVMOption("OnOutOfMemoryError","calc.exe");
报错如下
xxxxxxxxxx
Exception 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
xxxxxxxxxx
if (!(var4 instanceof String)) {
throw new IllegalArgumentException("VM Option \"" + var1 + "\" is of an unsupported type: " + var4.getClass().getName());
}
Flag.setStringValue(var1, var2);
通过反射直接操作Flag
类(该类的私有的)
xxxxxxxxxx
Class<?> 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.exe
System.out.println(value);
尝试通过反射修改
xxxxxxxxxx
Class<?> clazz = Class.forName("sun.management.Flag");
Method setMethod = clazz.getDeclaredMethod("setStringValue", String.class, String.class);
setMethod.setAccessible(true);
setMethod.invoke(null,"OnOutOfMemoryError","notepad.exe");
报错如下
xxxxxxxxxx
Caused 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
相关的代码
xxxxxxxxxx
JNIEXPORT void JNICALL
Java_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
层面无论如何也是无法修改的
xxxxxxxxxx
JVM_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