LFI2RCE via Eternal waiting

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert de l'équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks:

Informations de base

Par défaut, lorsqu'un fichier est téléchargé sur PHP (même s'il ne s'y attend pas), il générera un fichier temporaire dans /tmp avec un nom tel que php[a-zA-Z0-9]{6}, bien que j'aie vu certaines images Docker où les fichiers générés ne contiennent pas de chiffres.

Dans une inclusion de fichier local, si vous parvenez à inclure ce fichier téléchargé, vous obtiendrez une RCE.

Notez que par défaut, PHP autorise uniquement le téléchargement de 20 fichiers dans une seule requête (défini dans /etc/php/<version>/apache2/php.ini):

; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20

Technique de l'attente éternelle

Dans cette technique, nous avons seulement besoin de contrôler un chemin relatif. Si nous parvenons à télécharger des fichiers et à faire en sorte que l'inclusion LFI ne se termine jamais, nous aurons "suffisamment de temps" pour forcer la brute-force des fichiers téléchargés et trouver n'importe lequel des fichiers téléchargés.

Avantages de cette technique:

  • Vous avez juste besoin de contrôler un chemin relatif à l'intérieur d'une inclusion

  • Ne nécessite pas nginx ou un niveau d'accès inattendu aux fichiers journaux

  • Ne nécessite pas un jour zéro pour provoquer une erreur de segmentation

  • Ne nécessite pas de divulgation de chemin

Les principaux problèmes de cette technique sont:

  • Besoin d'un ou de plusieurs fichiers spécifiques à être présents (il pourrait y en avoir plus)

  • La quantité folle de noms de fichiers potentiels: 56800235584

  • Si le serveur n'utilise pas de chiffres, le nombre total potentiel est de: 19770609664

  • Par défaut, seuls 20 fichiers peuvent être téléchargés dans une seule requête.

  • Le nombre maximal de travailleurs parallèles du serveur utilisé.

  • Cette limite avec les précédentes peut rendre cette attaque trop longue

  • Délai d'attente pour une requête PHP. Idéalement, cela devrait être éternel ou devrait arrêter le processus PHP sans supprimer les fichiers téléchargés temporaires, sinon, cela sera également une douleur

Alors, comment faire en sorte qu'une inclusion PHP ne se termine jamais? Simplement en incluant le fichier /sys/kernel/security/apparmor/revision (pas disponible dans les conteneurs Docker malheureusement...).

Essayez simplement en appelant:

php -a # open php cli
include("/sys/kernel/security/apparmor/revision");

Apache2

Par défaut, Apache prend en charge 150 connexions simultanées, suivant https://ubiq.co/tech-blog/increase-max-connections-apache/, il est possible d'augmenter ce nombre jusqu'à 8000. Suivez ceci pour utiliser PHP avec ce module : https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04.

Par défaut, (comme je peux le voir dans mes tests), un processus PHP peut durer éternellement.

Faisons quelques calculs :

  • Nous pouvons utiliser 149 connexions pour générer 149 * 20 = 2980 fichiers temporaires avec notre webshell.

  • Ensuite, utilisez la dernière connexion pour brute-force les fichiers potentiels.

  • À une vitesse de 10 requêtes/s, les temps sont :

  • 56800235584 / 2980 / 10 / 3600 ≈ 530 heures (50% de chances en 265h)

  • (sans chiffres) 19770609664 / 2980 / 10 / 3600 ≈ 185h (50% de chances en 93h)

Notez que dans l'exemple précédent, nous saturons complètement les autres clients !

Si le serveur Apache est amélioré et que nous pouvons abuser de 4000 connexions (à mi-chemin du nombre maximal), nous pourrions créer 3999*20 = 79980 fichiers et le nombre serait réduit à environ 19,7h ou 6,9h (10h, 3,5h 50% de chances).

PHP-FMP

Si au lieu d'utiliser le module php régulier pour apache pour exécuter des scripts PHP, la page web utilise PHP-FMP (ce qui améliore l'efficacité de la page web, il est donc courant de le trouver), il y a quelque chose d'autre qui peut être fait pour améliorer la technique.

PHP-FMP permet de configurer le paramètre request_terminate_timeout dans /etc/php/<version-php>/fpm/pool.d/www.conf. Ce paramètre indique le nombre maximum de secondes pendant lesquelles la demande à PHP doit se terminer (infini par défaut, mais 30s si le paramètre est décommenté). Lorsqu'une demande est en cours de traitement par PHP pendant le nombre de secondes indiqué, elle est interrompue. Cela signifie que si la demande téléchargeait des fichiers temporaires, car le traitement php a été arrêté, ces fichiers ne seront pas supprimés. Par conséquent, si vous pouvez faire durer une demande ce temps, vous pouvez générer des milliers de fichiers temporaires qui ne seront pas supprimés, ce qui accélérera le processus de recherche et réduira la probabilité d'un DoS sur la plateforme en consommant toutes les connexions.

Ainsi, pour éviter un DoS, supposons qu'un attaquant n'utilisera que 100 connexions en même temps et que le temps de traitement maximal par php-fmp (request_terminate_timeout) est de 30s. Par conséquent, le nombre de fichiers temporaires pouvant être générés par seconde est 100*20/30 = 66,67.

Ensuite, pour générer 10000 fichiers, un attaquant aurait besoin de : 10000/66,67 = 150s (pour générer 100000 fichiers, le temps serait de 25min).

Ensuite, l'attaquant pourrait utiliser ces 100 connexions pour effectuer une recherche brute-force. En supposant une vitesse de 300 req/s, le temps nécessaire pour exploiter cela est le suivant :

  • 56800235584 / 10000 / 300 / 3600 ≈ 5,25 heures (50% de chances en 2,63h)

  • (avec 100000 fichiers) 56800235584 / 100000 / 300 / 3600 ≈ 0,525 heures (50% de chances en 0,263h)

Oui, il est possible de générer 100000 fichiers temporaires dans une instance de taille moyenne EC2 :

Notez que pour déclencher le délai d'expiration, il suffirait d'inclure la page LFI vulnérable, afin qu'elle entre dans une boucle d'inclusion éternelle.

Nginx

Il semble que par défaut, Nginx prend en charge 512 connexions parallèles en même temps (et ce nombre peut être amélioré).

Dernière mise à jour