(未完待续……)
1. Java的命令执行方式
JDK中提供的可执行系统命令的API有:
java.lang.Runtime
java.lang.ProcessBuilder
java.lang.UNIXProcess
(Linux) /java.lang.ProcessImpl
(Windows)
2. java.lang.Runtime
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class Test1 {
public static void main(String[] args) throws IOException {
try {
String RuntimeExecRes = RuntimeTest();
System.out.println(RuntimeExecRes);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String RuntimeTest() throws IOException {
//执行whoami命令
InputStream ins = Runtime.getRuntime().exec("whoami").getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int size;
while ((size = ins.read(bytes)) > 0) {
bos.write(bytes, 0, size);
}
return bos.toString();
}
}
exec()
有多种重载,传入的命令参数可以是字符串或字符串数组,实际是调用ProcessBuilder
的start()
方法。
3. java.lang.ProcessBuilder
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class Test2 {
public static void main(String[] args) {
String ProcessExecRes = null;
try {
ProcessExecRes = ProcessTest();
System.out.println(ProcessExecRes);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String ProcessTest() throws IOException {
String[] cmds = {"cmd", "/c", "whoami"};
InputStream ins = new ProcessBuilder(cmds).start().getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int size;
while ((size = ins.read(bytes)) > 0) {
bos.write(bytes, 0, size);
}
return bos.toString();
}
}
ProcessBuilder
的start()
方法实际调用的是ProcessImpl.start()
方法(在Windows下)。
4. java.lang.ProcessImpl
ProcessImpl
是最终调用native方法执行系统命令的类。
public class Test3 {
public static void main(String[] args) {
try {
String ProcessImplRes = ProcessImplTest();
System.out.println(ProcessImplRes);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String ProcessImplTest() throws Exception {
String[] cmds = {"whoami"};
Class clazz = Class.forName("java.lang.ProcessImpl");
Method method = clazz.getDeclaredMethod("start", new String[]{}.getClass(),
Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
method.setAccessible(true);
InputStream ins = ((Process) method.invoke(null, cmds, null, ".", null, true)).getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int size;
while ((size = ins.read(bytes)) > 0) {
bos.write(bytes, 0, size);
}
return bos.toString();
}
}
注:在JDK17会报错:
java.lang.reflect.InaccessibleObjectException: Unable to make static java.lang.Process java.lang.ProcessImpl.start(java.lang.String[],java.util.Map,java.lang.String,java.lang.ProcessBuilder$Redirect[],boolean) throws java.io.IOException accessible: module java.base does not "opens java.lang" to unnamed module @41629346
需要添加JVM参数:--add-opens java.base/java.lang=ALL-UNNAMED
。
5. 防御
- 配置
SecurityManager
规则。 - RASP