Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2019-11043 — PHP-FPM Exploit d'exécution de commande à distance | Kitploit
Outils/GitHubGitHub/lindemer/cve-2019-11043
Analyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionApprentissage et ÉducationDéveloppement de Charges Utiles
GitHublindemer/cve-2019-11043

CVE-2019-11043

PHP-FPM Exploit d'exécution de commande à distance

Voir le dépôt
42il y a 5 ansPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2019-11043

Exécution de code à distance PHP-FPM

Screencast : https://youtu.be/d6benC5FVZM

Aperçu

Cet exploit zero-day dans les configurations PHP-FPM courantes a été découvert lors du concours Realworld CTF en 2019. Une expression régulière est utilisée pour analyser l'URI demandée, mais les caractères de nouvelle ligne %0a ne correspondent pas. Cela déclenche un bug dans FastCGI qui calcule incorrectement la longueur de la chaîne de requête et écrit un octet nul à un emplacement avant le début du tampon prévu. En sélectionnant soigneusement la longueur de la chaîne de requête, un attaquant peut utiliser ce bug pour écraser des variables PHP internes sur le serveur et exécuter du code shell arbitraire.

L'implémentation originale de cet exploit en Go se trouve ici. J'ai utilisé ceci, un article et le rapport de bug original comme ressources d'apprentissage afin d'implémenter l'exploit en Python.

Instructions

Docker sur Linux Exécutez sudo docker run --rm -ti -p 8080:80 reproduce-cve-2019-11043 pour instancier un serveur NGINX/PHP-FPM minimal avec un script vide à /script.php. Le Dockerfile de cette image est disponible ici, bien qu'il ne soit pas nécessaire pour exécuter la commande susmentionnée.

Docker sur Mac Exécutez sudo docker-compuse up -d depuis le répertoire /php/CVE-2019-11043 du dépôt vulhub. (Compose est inclus avec Docker pour Mac.)

Exécutez le script d'exploitation avec la commande python3 exploit.py http://localhost:8080/script.php (ou /index.php si la seconde option a été utilisée). Après une exécution réussie, un shell Web sera accessible en ajoutant des commandes à l'URL après ?a= (p. ex., http://localhost:8080/script.php?a=uname -a).

N.B. J'ai bien essayé de créer un playbook Ansible pour ce travail, mais je me suis heurté à un bug bloquant documenté ici. Il n'est pas possible de démarrer des services systemd sur les noyaux Linux récents (par exemple, toute version LTS d'Ubuntu) avec un playbook Ansible.

Théorie

Vulnérabilité

Les fichiers de configuration PHP-FPM contiennent une règle pour faire correspondre les requêtes URI entrantes aux scripts PHP, qui ressemble souvent à ceci :

root@kitploit:~
location ~ [^/]\.php(/|$) {
  ...
  fastcgi_split_path_info       ^(.+?\.php)(/.*)$;
  fastcgi_param PATH_INFO       $fastcgi_path_info;
  fastcgi_pass                  php:9000;
  ...
}

Cela devrait correspondre à toute URI de la forme /script.php/pathinfo, mais . ne correspond en réalité pas aux caractères de nouvelle ligne %0a. Si l'URI contient une nouvelle ligne, cela déclenche le bug suivant dans l'implémentation PHP :

root@kitploit:~
1141    int ptlen = strlen(pt);
1142    int slen = len - ptlen;
1143    int pilen = env_path_info ? strlen(env_path_info) : 0;
1144    int tflag = 0;
1145    char *path_info;
1146    if (apache_was_here) {
1147        /* recall that PATH_INFO won't exist */
1148        path_info = script_path_translated + ptlen;
1149        tflag = (slen != 0 && (!orig_path_info || strcmp(orig_path_info, path_info) != 0));
1150    } else {
1151        path_info = env_path_info ? env_path_info + pilen - slen : NULL;
1152        tflag = (orig_path_info != path_info);
1153    }

Le problème ici est que slen est correctement calculé comme la longueur de l'URI moins la longueur du chemin de la ressource, mais pilen est par erreur mis à 0. Cela définit path_info à une valeur négative à la ligne 1151, ce qui entraîne un sous-dépassement de tampon. Immédiatement après cette erreur de calcul dans le même fichier, on a :

root@kitploit:~
1159    FCGI_PUTENV(request, "ORIG_PATH_INFO", orig_path_info);
1160    old = path_info[0];
1161    path_info[0] = 0;
1162    if (!orig_script_name ||
1163        strcmp(orig_script_name, env_path_info) != 0) {
1164        if (orig_script_name) {
1165            FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name);
1166        }
1167        SG(request_info).request_uri = FCGI_PUTENV(request, "SCRIPT_NAME", env_path_info);
1168    } else {
1169        SG(request_info).request_uri = orig_script_name;
1170    }
1171    path_info[0] = old;

À la ligne 1161, un octet nul est écrit à l'emplacement mémoire mal calculé de l'étape précédente. Cela peut être exploité pour tirer parti d'une vulnérabilité à la ligne 1165, où FastCGI écrit une variable d'environnement. En écrivant l'octet nul dans le pointeur qui contrôle l'opération d'écriture de la variable d'environnement, nous pouvons insérer des variables PHP arbitraires dans l'environnement avec nos requêtes HTTP.

Exploitation

Structures de données internes de FastCGI

Les variables d'environnement dans FastCGI sont stockées en mémoire dans une séquence compacte de paires clé-valeur sous forme de chaînes. Le début et la fin du tampon contenant ces chaînes s'appellent _fcgi_data_seg. Le membre pos pointe vers le prochain emplacement disponible pour l'écriture. Si le tampon se remplit (pos > end), un nouveau tampon est alloué et le membre next pointe vers l'ancien.

root@kitploit:~
118    typedef struct _fcgi_data_seg {
119        char                  *pos;
120        char                  *end;
121    	   struct _fcgi_data_seg *next;
122    	   char                   data[1];
123    } fcgi_data_seg;

FastCGI accède aux variables d'environnement individuelles via une table de hachage appelée _fcgi_hash.

root@kitploit:~
125    typedef struct _fcgi_hash {
126    	   fcgi_hash_bucket  *hash_table[FCGI_HASH_TABLE_SIZE];
127    	   fcgi_hash_bucket  *list;
128        fcgi_hash_buckets *buckets;
129        fcgi_data_seg     *data;
130    } fcgi_hash;

L'idée ici est d'écraser l'octet de poids faible de pos afin de tromper FastCGI pour qu'il écrase une variable existante. Le code est censé prendre la chaîne ajoutée à notre chemin d'URI et la placer à l'emplacement de PATH_INFO. Cependant, nous voulons écraser PHP_VALUE, car cette valeur est immédiatement récupérée et chargée dans les paramètres PHP après le segment de code vulnérable.

Alignement des données

Comme vous pouvez le voir dans exploit.py, le principe général de cet exploit est de trouver une requête URI très longue qui alignera le tampon mémoire interne de FastCGI d'une manière que nous pouvons exploiter. L'idée est de trouver le nombre exact de caractères nécessaire pour que FastCGI alloue un nouveau tampon _fcgi_data_seg. Lorsque cela se produit, FastCGI écrira de manière prévisible notre PATH_INFO dans le nouveau tampon, suivi immédiatement par chacun de nos en-têtes HTTP comme nouvelles valeurs d'environnement. L'étape suivante consiste donc à trouver de combien de caractères nous devons compléter un en-tête HTTP arbitraire pour aligner la mémoire selon nos besoins. Étant limités à l'écriture d'un octet nul à un emplacement arbitraire, nous devons faire pointer pos vers un décalage prévisible par rapport à PHP_VALUE afin que la modification de l'octet de poids faible l'y déplace.

Contournement de la table de hachage

Le défi est que nous voulons écraser PHP_VALUE, mais nous ne savons pas où elle se trouve en mémoire. Lorsque FastCGI charge cette variable, il hache la chaîne PHP_VALUE pour obtenir l'adresse mémoire réelle selon un algorithme simple :

root@kitploit:~
31    #define FCGI_HASH_FUNC(var, var_len) \
32        (UNEXPECTED(var_len < 3) ? (unsigned int)var_len : \
33        (((unsigned int)var[3]) << 2) + \
34        (((unsigned int)var[var_len-2]) << 4) + \
35        (((unsigned int)var[var_len-1]) << 2) + \
36        var_len)

Au lieu de modifier réellement la table de hachage d'une manière ou d'une autre, tout ce que nous avons à faire est de créer une autre variable d'environnement ayant la même longueur de chaîne et le même hachage que PHP_VALUE selon cette fonction. Cela trompera la recherche de hachage pour qu'elle lise notre en-tête HTTP au lieu de la variable prévue. L'auteur de cet exploit a intelligemment remarqué qu'un en-tête appelé EBUT sera enregistré sous la forme HTTP_EBUT dans l'environnement FastCGI, ce qui satisfait cette exigence.

Injection de code

Pour l'attaque elle-même, nous envoyons des requêtes GET contenant notre en-tête EBUT et utilisons le bug d'écrasement par octet nul pour écraser sa valeur. Nous essayons de définir les variables d'environnement PHP une par une avec des requêtes répétées :

root@kitploit:~
short_open_tag=1
html_errors=0
include_path=/tmp
auto_prepend_file=a
log_errors=1
error_reporting=2
error_log=/tmp/a
extension_dir=\"<?=`\"
extension=\"$_GET[a]`?>\"

La modification réussie de toutes ces variables permet une nouvelle requête ?a= sur le serveur pour l'exécution arbitraire de code shell. La boucle d'attaque vérifie le succès à chaque itération en essayant d'exécuter which which. L'attaquant peut facilement détecter si cela a réussi en lisant le résultat de la réponse HTTP (p. ex., /bin/which).

Télécharger l’outil