Skip to content
Faites fleurir votre intelligence Internet - Bénéficiez de 20% de réduction sur Censys Search Teams ou sur les plans annuels Solo avec le code Spring24 jusqu'au 31 mai.
Blogs

CVE-2021-41773 : Traversée de chemin Apache

Introduction

Le 22 juillet 2021, le célèbre serveur web Apache HTTPD a fusionné avec un autre serveur web. s'engager qui a remplacé la fonction ap_getparents() avec une nouvelle fonction appelée ap_normalize_path(). Cette nouvelle fonction a été présentée comme un moyen plus efficace et plus standard de traiter la normalisation des chemins bruts trouvés dans l'URI d'une requête HTTP. Le 15 septembre, ces changements ont été fusionné dans le tronc et étiqueté version 2.4.49.

Quel est le problème ?

Le 29 septembre, quatorze jours après la sortie de la version 2.4.49, un ingénieur de cPanel nommé Ash Daulton a signalé qu'un attaquant pouvait utiliser cette nouvelle logique pour exécuter un exploit de traversée de chemin, conduisant potentiellement à l'exécution de code à distance (RCE) lorsque certaines conditions de configuration sont remplies.

Une vue d'ensemble d'Apache v2.4.49

Censys a trouvé 75 085 hôtes uniques exécutant 136 469 services qui s'identifiaient comme Apache HTTPD version 2.4.49 à l'aide de cette simple requête. Alors que la plupart des installations de ce service ne sont pas vulnérables en l'état, Censys a identifié quelques vendeurs qui fournissent une configuration laxiste, permettant l'apparition du bogue. L'un de ces fournisseurs, Control Webpanel, qui compte plus de 22 000 hôtes sous CentOS, semble être une cible probable pour cette attaque en raison de la configuration qu'il fournit.

Certains chercheurs ont déclaré que cette vulnérabilité était activement recherchée dans la nature, mais aucune déclaration définitive n'a été faite quant à savoir si les recherches aboutissaient à une exploitation active.

Par défaut, la configuration de contrôle d'accès suivante est mise en place :

DocumentRoot "/usr/local/apache2/htdocs"
<Directory />
    AllowOverride none
    Require all denied
</Directory>
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted
</Directory>

Si un utilisateur demande "/index.html"le serveur prendrait le DocumentRoot (/usr/local/apache2/htdocs) et ajouter "/index.html" à celui-ci, ce qui donne le fichier "/usr/local/apache2/htdocs/index.html". Étant donné qu'une règle autorise l'accès à tous les fichiers de la zone "/usr/local/apache2/htdocs" la demande est acceptée.

D'autre part, si un utilisateur demandait "/../../../../foobar", le serveur découperait le "/../../../.." via ap_normalize_path() et ajouter le résultat au fichier DocumentRoot, ce qui donne "/usr/local/apache2/htdocs/foobar"; un fichier qui ne devrait pas exister sur le système de fichiers.

Mais comme ap_normalize_path() ne vérifie pas correctement les points doubles lorsqu'ils sont encodés en ascii-hex, par exemple, "%2e%2e"cette fonction de normalisation rendra les points et n'effacera pas le chemin. Mais même après le rendu des points doubles, les contrôles d'accès par défaut devraient empêcher l'exécution de la plupart des transitions de chemin. Ce n'est que lorsqu'un administrateur définit explicitement la configuration "Require all granted" sur un répertoire protégé que les choses peuvent devenir incontrôlables ; c'est-à-dire,

<Directory />
    AllowOverride none
    Require all granted
</Directory>

Cette configuration incorrecte, combinée à l'utilisation de la fonction mod_cgi a le potentiel de transformer cette simple vulnérabilité de traversée de chemin en une véritable attaque d'exécution de commande à distance (RCE).

Un regard plus approfondi

Bien que cette nouvelle ap_normalize_path() joue un rôle ici, mais ce n'est pas exactement la fonction cause de la vulnérabilité. Il s'agit plutôt d'une fonction qui a permis de mettre en lumière un problème déjà existant. Je veux dire, la preuve est dans le nom : c'est "normalize", pas "sanitize". Mais ce changement a eu des effets secondaires inattendus pour d'autres fonctions qui s'appuyaient sur cette itération précédente qui assainissait les données.

Le véritable problème est caché dans une fonction abstraite du système d'exploitation appelée apr_filepath_merge()et, plus précisément, comment cette fonction est appelée. Mais avant de parler de comment cette fonction est appeléNous devons comprendre ce que cette fonction fait. La documentation indique que cette fonction

"Fusionner le chemin d'accès au fichier supplémentaire avec le chemin d'accès à la racine précédemment traité".

L'objectif principal de cette fonction est de prendre un chemin de base (comme un chemin apache DocumentRoot), et un chemin transmis dans l'URI d'une requête, et les fusionner afin de faire correspondre l'URI d'entrée à un chemin entièrement qualifié sur le serveur.

Voici une traduction très basique de ce que fait cette fonction :

final_path = split("/usr/local/apache2/htdocs", "/") // result: ["usr", "local", "apache2", "htdocs"]
tokens     = split("usr/bin/..", "/")  // result: ["usr", "bin", ".."]
​
for each path_part in tokens:
    if path_part == "..":
        // remove the last segment in the final_path array
        final_path[length(final_path)] = null
    else:
        final_path.append(path_part)
​
return final_path.Join("/") // returns /usr/local/apache2/htdocs/usr 

Lorsque cette fonction est appelée à partir du gestionnaire Apache par défaut (ap_core_translate()), il prendra d'abord le chemin de la requête d'entrée, et sautera la première barre oblique pour indiquer qu'il s'agit d'un chemin relatif à partir de l'élément DocumentRoot. Par exemple, si le serveur est configuré avec DocumentRoot "/usr/local/apache2/htdocs"et la demande entrante est :

GET /foo/bar HTTP/1.0

Les apr_filename_merge() sera appelé comme suit :

char * path = "/foo/bar";
char * root = "/usr/local/apache2/htdocs";
char * output;
​
while (*path == '/') {
    ++path;
}
​
int rv = apr_filepath_merge(&output, root, path, APR_FILEPATH_SECUREROOT, r->pool));

Ce qui aboutirait à la output contenant un tampon : /usr/local/apache2/htdocs/foo/bar. La raison pour laquelle cet appel à apr_filepath_merge est non vulnérable à l'attaque de type "path-traversal" est principalement due au drapeau APR_FILEPATH_SECUREROOT étant défini. La fonction échouera si le chemin que nous essayons d'ajouter se trouve en dehors du répertoire "/usr/local/apache2/htdocs".

Mais toutes les demandes ne sont pas traitées par le gestionnaire par défaut, certaines sont traitées par des modules. Par exemple, le module mod_alias doit d'abord faire correspondre le chemin d'entrée à un chemin de base, et ce chemin de base doit être étendu à un chemin réel. Prenons la configuration suivante :

<IfModule alias_module>
    ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
</IfModule>

Cela signifie que tout chemin URI entrant qui commence par "/cgi-bin/" doit en fait aller dans le répertoire "/usr/local/apache2/cgi-bin/". La façon dont l'URI est traduit en fichier est donc différente de ce qu'elle est en ap_core_translate(). Premièrement, mod_alias va itérer sur une liste de alias_entryavec le format suivant :

alias_entry entry = {
    .real = "/usr/local/apache2/cgi-bin/",
    .fake = "/cgi-bin/",
};

Il examinera le préfixe du chemin de la demande entrante et, s'il correspond à l'attribut fake c'est-à-dire "/cgi-bin/", le contenu de l'URI entrant est ajouté à l'élément real qui, à son tour, est utilisé pour appeler l'élément apr_filepath_merge:

char * output_path = NULL;
​
prefix_len = alias_matches(request->uri, alias->fake);
​
if (prefix_len > 0) {
    // Append the contents of the request URI after the prefix to the end of the "real" element
    output_path = apr_pstrcat(request->pool, alias->real, request->uri + prefix_len, NULL);
    // if alias->real  IS "/usr/local/apache2/cgi-bin/"
    // AND
    // if request->uri IS "/cgi-bin/../../../etc"
    // THEN
    // output_path     IS "/usr/local/apache2/cgi-bin/../../../etc"
}
​
if (output_path != NULL) {
    char * newpath = NULL;
    int ret = apr_filepath_merge(&newpath, "/usr/local/apache2", output_path, 0, p);
    
    if (newpath != NULL && ret == APR_SUCCESS) {
        return newpath; // newpath IS /usr/etc
    }
}

Comme vous l'avez peut-être remarqué, le APR_FILEPATH_SECUREROOT n'a pas été transmis à l'option apr_filepath_merge comme c'était le cas dans ap_core_translate()ce qui signifie que même si l'URI contient des points doubles, l'appel n'aboutira pas à une erreur. mod_alias est vulnérable à l'attaque de type "path-traversal".

Mais pour être tout à fait clair, ce n'est pas parce que cette fonction a permis le rendu de la traversée que le serveur doit être configuré de manière à permettre l'accès au répertoire traversé. En reprenant l'exemple ci-dessus de "/usr/etc"un administrateur doit explicitement autoriser le serveur à accéder au répertoire "/usr/etc" avec un paramètre "Require all granted". Dans le cas contraire, la demande échouera lors d'une vérification secondaire dans la fonction ap_run_access_checker_ex().

Pourquoi est-ce important ?

Si les conditions sont réunies, un attaquant peut tirer parti de cette vulnérabilité de traversée de chemin pour exécuter n'importe quelle commande avec les mêmes autorisations que l'utilisateur qui exécute le service. Un attaquant peut alors utiliser cet accès pour obtenir des autorisations administratives élevées s'il existe des vulnérabilités locales non corrigées sur le système.

Étant donné que cette vulnérabilité est facile à exécuter et que des preuves d'exploitation sont facilement trouvées sur les médias sociaux et autres, nous devons supposer que des acteurs malveillants ont déjà commencé à exploiter des serveurs vulnérables dans la nature.

Que dois-je faire ?

Mise à jour 10-07-2021 (19h30 EST): Développeurs de la CentOS Webpanel avoir a fait le point sur la situation. Exécution manuelle /scripts/update_cwp rétrogradera le serveur Apache HTTPD à la version 2.4.48.

A propos de l'auteur

Mark Ellzey
Chercheur principal en sécurité Tous les postes de Mark Ellzey
Mark Ellzey est chercheur principal en sécurité à l'adresse Censys. Avant d'occuper son poste actuel, Mark a travaillé pendant plus de 22 ans en tant qu'ingénieur en sécurité des réseaux et développeur de logiciels pour plusieurs fournisseurs de services Internet et institutions financières.
Solutions de gestion de la surface d'attaque
En savoir plus