Exploitation de faille include avec les sessions PHP
16 septembre 2009 – 21:05Il y a quelques jours, je suis tombé sur un challenge de hack PHP. J’arrive sur une page avec une URL du type www.site.com/index.php?page=main.php
, qui ressemble étrangement à la célèbre faille include. En effet, quelques tests le démontrent. Autre part sur le site, on trouve une page protégée par un .htaccess. Pour le bypasser, on utilise naturellement cette faille include (ou bien cette autre méthode). Mais ce n’est pas tout. Outre le fait qu’on ne puisse pas inclure de page distante (directive allow_url_include
activée), l’administrateur n’a pas du tout protégé cette faille include. On peut donc inclure tous les fichiers locaux accessibles…
Il était une fois une include non protégée…
Je me lance donc dans l’exploration de l’arborescence en récupérant quelques fichiers intéressants. Commepar exemple /proc/version, qui indique que le serveur tourne sous une Ubuntu basée sur un kernel 2.6.25. Je regarde également une partie de la configuration Apache dans /etc/apache2/apache2.conf, la liste des utilisateurs dans /etc/passwd. /etc/shadow est bien évidemment non accessible car Apache n’est pas lancé par le root. Mais sachant qu’un exploit touchant les Linux non patchés a récemment été publié, je me dis qu’il doit certainement être possible de rooter le serveur à partir de là. Si j’arrive à exécuter des commandes sur le serveur avec les droits d’Apache, devenir root ne devrait pas être dur en utilisant cet exploit…
Je cherche donc un moyen d’utiliser la faille include pour pouvoir exécuter du code. Je pense en particulier à l’injection de commandes dans les logs Apache. Seul problème : ils ne sont lisibles que par le root, donc pas par Apache (il suffit de tester pour s’en rendre compte rapidement). Où vais-je donc pouvoir injecter mes commandes… ? Quels sont les fichiers manipulés par Apache et PHP dans lesquels on peut écrire indirectement et lire ensuite ? Sur le coup, je sèche…
…un site utilisant les sessions…
Là, Heurs et Virtualabs me donnent la réponse : les sessions PHP. En effet, PHP stocke les variables de session dans des fichiers (elles sont sérialisées) qui se situent dans /var/lib/php5/. Leur nom suit le format sess_$PHPSESSID
où $PHPSESSID
est l’identifiant de session qui est envoyé dans le cookie, donc facilement récupérable. Et, coup de chance, il se trouve que le challenge en question utilise les sessions pour stocker l’utilisateur courant et son mot de passe. Champs qui ne sont sont bien pas vérifiés lors de la soumission du formulaire
C’est parti ! Je crée un compte bidon avec quelque chose comme <?php system($_GET['c']); ?>
pour le login. Je soumet, récupère le cookie contenant l’identifiant de session, et inclus le fichier de session correspondant. Et là, j’obtiens une jolie backdoor PHP sur le serveur. Rudimentaire, certes, mais fonctionnelle ! A partir de là, j’en conçois une légèrement plus élaborée, que je place sur un de mes serveurs, et que je fais télécharger au serveur victime avec un wget
(suivi d’un mv
). Je peux maintenant exécuter des commandes PHP à volonté, lire des fichiers sources, etc. Je récupère une autre backdoor permettant d’obtenir un remote shell sur ma machine. J’ouvre un port avec Netcat sur ma machine, j’upload et lance la backdoor, et le tour est joué.
… et un kernel non patché.
Je peux maintenant exécuter des commandes de façon interactive, mais toujours avec les droits d’Apache. Je télécharge un des exploits sock_sendpage de Milw0rm, le compile sur le serveur (gcc y était installé, cool), je lance l’exécutable généré, et bing ! Root sur le serveur . Comme mon but n’est pas de planter le serveur, je m’arrête ici. Enfin je prends quand même soin de supprimer toute trace de l’intrusion dans les logs. Et je garde un petit screenshot, pour envoyer au propriétaire du site, l’histoire de le prévenir. Bien entendu, pour envoyer le mail j’ai pris soin de créer un mail anonyme et de passer par un proxy, au cas où le propriétaire serait furax et menacerait de porter plainte… Heureusement, celui-ci est sympa ; il me répond rapidement et me remercie de l’avoir prévenu. Et vous savez le comble de l’histoire ? Ce serveur ne lui appartenait pas, il ne disposait pas lui-même des droits root
Conclusion
Je crois que cette histoire (vraie, pour ceux qui douteraient) illustre plutôt bien et de façon concrète la marche à suivre employée par un attaquant afin de prendre la main sur un serveur : exploitation d’une faille distante pour récupérer des informations, exécution de commandes dans le contexte du processus vulnérable, élévation de privilèges en utilisant un local root, et nettoyage des logs. De plus, cet exemple démontre que ce n’est pas parce que l’on a interdit l’inclusion de fichiers distant que l’on a un appel à include() sécurisé.
J’insiste enfin sur le fait que ce que j’ai réalisé ici était à la portée d’un script kiddie pour peu qu’il connaisse la faille, sache utiliser une backdoor PHP et compiler un exploit. Administrateurs de challenges de hack PHP, méfiez-vous : reproduire des applications vulnérables c’est bien, mais pensez à vous protéger un minimum pour éviter le pire…
7 réponses à “Exploitation de faille include avec les sessions PHP”
Pas mal de souçis de sécu il est vrai, mais il est vrai aussi que de sécuriser 100% est très compliqué.
Enfin rapidement l’openbasedir c’est pas du luxe et tu n’aurais pas réussi aussi facilement (même si il y a toujours moyens). Après il y a toujours moyen de mettre dans une jail le tout mais ça commence à devenir un peu plus compliqué.
Pour finir la sécurité uniquement du point de vue système n’est pas la bonne solution, trop de dev le pensent, beaucoup de solution éxiste dans le code pour parer à un bon nombre d’attaque (mysql_real_escape; vérif du contenu des variables; etc …)
Par Yann le 16 septembre 2009
Ce type d’exécution de code PHP est moins connu que l’exécution de code par les logs (qui fonctionne même sur des systèmes autres que AMP, type Coldfusion), mais les conditions requises pour l’exploitation sont plus draconniennes: emploi des sessions par le site, stockage dans les sessions de contenu non filtré (un htmlentities empêche cette exploitation), sans oublier la modification de la configuration par défaut. Cette dernière n’est pas un problème si on a accès à la configuration PHP.
Tout ca pour dire que p0wner des serveurs de challenge, bah saimal. Même si la méthode pour y parvenir est quelque peu exotique.
Par virtualabs le 17 septembre 2009
Sur les serveurs LAMP ca passe pas pour l’exploitation par les logs, mais sur la majorité des WAMP ca marche par contre
Par Heurs le 18 septembre 2009
Lool t’as challengé le challenge lui même : ça le fou mal…
En Angleterre par contre je tombes sur un problème de sécu récurant : Robustesse des mots de passes négligée, plus récupération simple des accès PPPoA, y’a de quoi ce faire plaisir + surveillance mail (j’ai de jolis screenshots).
Au final => Plus rien ne me choque!
Par FlUxIuS le 22 septembre 2009
Salut,
pourrais-tu m’en dire plus sur l’inclusion dans le phpsessid? J’ai un peu de mal à imaginer une interception des requêtes pour lui balancer l’adresse d’une backdoor.
Apparemment, dans ton article, le site cible était vulnérable à une LFI, mais aux travers de la variable, tu lui lances une RFI non?
Un peu plus de précision sur la manière dont tu as procéder serait le bienvenue Ainsi que la sécu, tu peux, car sinon dans un fichier secu.php (admettons en mvc, donc appliqué directement à toutes les pages) tu devrait sécurisé phpsessid sur xss, lfi, rfi.
Meilleures salutations,
P.S: Félicitation pour l’insomni’hack
Par cr0t4lux le 30 janvier 2010
Le PHPSESSID est un identifiant de session généré par le serveur et stocké par le client le plus souvent sous forme de cookie. Il permet de retrouver les données de la session de l’utilisateur qui sont stockées sur le serveur, donc non accessibles directement. Ce sont les données du tableau $_SESSION en PHP. Sur le serveur, ces données sont stockées dans des fichiers, généralement dans /var/lib/php5/. Leur nom est devinable en connaissant le PHPSESSID (cf ce que je dis dans l’article). Et ce qu’il y a de bien, c’est que ces fichiers sont toujours accessibles par Apache (sinon les sessions ne peuvent pas fonctionner).
Dans cette exploitation, on n’injecte donc pas dans le PHPSESSID, mais dans les données qui vont être sauvegardées en session. Dans mon cas, il se trouve que le concepteur du site stockait le login tel quel dans la session. Il était possible d’y insérer du code PHP. Ce sont donc les fichiers de session que l’on va chercher à inclure en utilisant la LFI (qui reste bien une LFI, pas une RFI). Ainsi on peut exécuter le code que l’on veut.
Pour sécuriser la LFI, il faut simplement éviter les appels à include() (et autres du même genre) avec des variables directement contrôlables, à moins de faire une vérification poussée sur le fichier. Vérifier si le fichier existe sur le serveur ne suffit pas, comme on vient de le voir (c’est le principe même de la LFI). Une bonne idée peut être de vérifier son extension, ou bien de s’assurer qu’il n’est pas plus haut que souhaité dans l’arborescence (filtrer les caractères / par exemple).
Par Emilien Girault le 31 janvier 2010