Java是基于C实现的,Java底层的很多API都是通过JNI(Java Native Interface)来实现的。通过JNI接口C/C++和Java可以互相调用。
加载动态链接库
JNI需要把c编写的动态链接库加载进来,然后进行调用,那么这里加载的时候可以在动态链接库里加上恶意代码
1 |
|
1 | gcc evil.c --shared -fPIC -o evil.so |
使用System.load加载,这里要使用绝对路径,运行完可以发现system命令成功执行
1 | System.load("/Users/xuemo/Desktop/java_project/untitled/src/main/java/evil.so"); |
使用ClassLoader.loadLibrary0加载,这里也要绝对路径,也可以发现命令成功执行
1 | File libPath = new File("/Users/xuemo/Desktop/java_project/untitled/src/main/java/evil.so"); |
那么如果实际场景里限制了命令执行的话,没有限制动态链接库加载,那么我们可以通过加载动态链接库的方式进行RCE
JNI demo
上面的例子只是简单的加载,还没有涉及到JNI这方面,我们简单来个demo
1 | public class demo { |
接下来就是生成对应的c文件头,进入到对应路径(这里我没有定义package,如果定义package了,就要相对应的修改一下参数)
1 | javac -cp . demo.java #生成class |
可以看到,我们java里的exec函数就对应这里的Java_demo_exec,Java_是固定前缀,然后是完整包名,然后是函数名
常用的话就注意字符串直接的转化
jstring转const char *:env->GetStringUTFChars(jstring str, jboolean isCopy)
const char转jstring: env->NewStringUTF(const char *utf)
如果用vscode编写时候,可以加一个include路径,对于mac来说,需要$JAVA_HOME/include和$JAVA_HOME/include/darwin两个目录下面的,这里配置**可以递归搜索
1 |
|
1 | g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" -shared -o demo.so demo.cpp |
直接调用会报错,因为毕竟我们还没有加载so
加载进来就可以执行啦
使用System.load
1 | System.load("/Users/xuemo/Desktop/java_project/untitled/src/main/java/demo.so"); |
使用ClassLoader.loadLibrary0
1 | File libPath = new File("/Users/xuemo/Desktop/java_project/untitled/src/main/java/demo.so"); |