Skip to content

Over The Wire – Natas 5 – Manipulation des cookies

On continue notre série sur la suite de challenges Natas avec le niveau 5 qui porte sur la manipulation des cookies

Pour rappel, histoire de donner un peu plus de consistance à ces articles , je compte, pour chaque challenge, rédiger un script Python qui permettra d’automatiser la détection ou l’exploitation des vulnérabilités présentées. Également, nous étudierons les recommandations associées à chaque vulnérabilité exploitée (lorsque cela sera pertinent). L’objectif étant de voir comment exploiter une vulnérabilité pour mieux s’en protéger, cela en étudiant aussi bien l’attaque que la défense.

Natas 5 – Exploitation

L’URL du challenge : http://natas5.natas.labs.overthewire.org/index.php

Lors de l’accès à la page web, je regarde comme d’habitude la réponse dans mon navigateur ainsi que les échanges (requêtes et réponses) dans mon proxy BurpSuite positionné entre les deux :

Réponse du serveur lors d’une requête à la page index.php

Ici, le serveur m’indique que l’accès est désactivé car je ne suis pas authentifié. Si je regarde la réponse du serveur sur ce premier accès, je vois qu’il m’assigne un cookie nommé loggedin :

Première réponse du serveur et assignation du cookie « loggedin »

Un cookie HTTP (cookie web, cookie de navigateur) est un petit ensemble de données qu’un serveur envoie au navigateur web de l’utilisateur. Le navigateur peut alors le stocker localement, puis le renvoyer à la prochaine requête vers le même serveur. Typiquement, cette méthode est utilisée par le serveur pour déterminer si deux requêtes proviennent du même navigateur — pour exemple pour garder un utilisateur connecté. Les cookies permettent de conserver de l’information en passant par le procotole HTTP qui est lui « sans état ».

Source : https://developer.mozilla.org/fr/docs/Web/HTTP/Cookies

Le nom du cookie est ici assez explicite. Dans des contextes réels, les cookies portent des noms comme « utm_x », « PHPSESSID », ou « dkamxonruapwmap2 ». Bref, ce n’est pas toujours aussi parlant. Il faut aussi savoir que certains cookies sont dit « de session » et d’autres non. Les cookies de sessions sont ceux qui permettent, côté serveur, d’identifier un utilisateur déjà authentifié. Le processus classique étant le suivant :

  1. L’utilisateur X arrive sur le formulaire d’authentification
  2. Le serveur lui demande ses identifiants, l’utilisateur lui envoi
  3. Si les identifiants sont correctes, le serveur assigne et envoie à l’utilisateur un cookie (par exemple « PHPSESSID = d3s5d4az8da32d51q8dg4784yh64za »)
  4. Pour l’accès aux autres pages de l’environnement web authentifié (typiquement une page de profil utilisateur). Le navigateur de l’utilisateur n’aura qu’à envoyer ce cookie PHPSESSID et le serveur se souviendra alors qu’il s’agit de l’utilisateur X, authentifié quelques minutes plus tôt.

Cela évite donc à l’utilisateur de saisir ses identifiants à chaque changement de page. Mis à part ce « rôle » de l’identification et de maintien d’une connexion (plus précisément d’une session) rien ne permet de distinguer un cookie « de session » d’un autre. Les autres cookies (ceux qui ne sont pas « de session ») permettent de garder des informations moins importantes telles que la langues de l’utilisateur ou son historique de recherche. Les cookies sont notamment beaucoup utilisés pour le tracking à des fins publicitaires).

Attention à ne pas confondre le cookie dit « de session » et l’expiration d’un cookie à la session, comme cela peut être le cas sur l’image suivante :

Visualisation des cookies du navigateur sur un site WordPress

L’expiration d’un cookie à la session veut simplement dire que le cookie sera détruit du navigateur lorsque celui-ci sera fermé. Rien a voir avec le « rôle » de maintien d’une session coté serveur comme indiqué précédemment 🙂

Bref, le cookie en question se nomme ici loggedin et contient la valeur « 0 ». On peut se douter qu’en le positionnant à « 1 », il se passera quelque chose d’intéressant. La modification d’un cookie peut être faite de façon permanente via le navigateur ou lors de son envoi au serveur via un proxy. J’opte pour la seconde méthode. Et oui, les cookies étant des éléments fournis par le serveur mais stockés côté utilisateur (dans son navigateur, plus précisément dans une base de données gérée par le navigateur), l’utilisateur est libre de pouvoir les modifier ou les supprimer.

Lors de l’envoi d’un requête avec le cookie loggedin ayant une valeur « 1 », j’obtiens la réponse suivante du serveur:

Envoi d’une requête avec une valeur du cookie loggedin changée manuellement

Le serveur pense donc que je suis un utilisateur authentifié car j’ai modifié la valeur du cookie qu’il m’avait précédemment assigné, et me donne le mot de passe du challenge suivant.

Exploitation via un script Python

Le script Python à créer ici va être assez basique, comme le précédent. Il va néanmoins nous permettre de voir comment manipuler les cookies avec la bibliothèque request en Python. En effet, comme les en-têtes, les cookies peuvent être créés, lus et modifiés d’une requête lorsque nous travaillons avec cette bibliothèque Python.

Dans un premier temps, nous pouvons effectuer une première requête basique, et lister les cookies qui nous sont assignés par le serveur. Il faut pour cela passer par une session, elle aussi gérée par la bibliothèque requests, qui permet de garder certains « éléments de contexte dans une suite de requêtes :

Je montre dans la capture ci-dessus la création d’un objet session qui va donc être « rempli » par notre première requête au serveur. Celui-ci nous envoyant un cookie, il sera contenu dans l’objet session (ainsi que les autres informations de la session, comme les en-têtes de la dernière requête par exemple). J’utilise ici la fonction native dir() pour lister les attributs de mon objet session, ce qui est très utile.

Une fonction native est une fonction proposée par l’interpréteur Python, ici, dir() permet « d’explorer » un objet et ses attributs, mais il en existe beaucoup d’autres très utiles : https://docs.python.org/fr/3.5/library/functions.html

Sans arguments, la fonction native dir() donne la liste des noms dans l’espace de noms local. Avec un argument, elle essaye de donner une liste d’attributs valides pour cet objet : https://docs.python.org/fr/3.6/library/functions.html#dir

Suite à cela, on voit que les cookies sont contenus dans un dictionnaire. On peut donc librement créer notre propre dictionnaire et assigner les cookies que l’on souhaite ainsi que leur valeur :

cookies = dict(session.cookies.get_dict())
cookies["loggedin"] = "1"

Je ne suis pas parvenu à directement modifier le dictionnaire de l’objet session, j’ai donc simplement copié celui-ci dans un autre dictionnaire que j’ai modifié, puis j’envoie celui-ci dans ma requête, comme pour les en-têtes du challenge précédent. On envoie ensuite notre seconde requête avec une valeur modifiée, et on analyse la réponse à la recherche du flag dans le code source :

Dans la capture ci-dessus, je récupère donc mon dictionnaire de cookies, je modifie celui qui m’intéresse et je renvoie ma seconde requête, le serveur pense alors que je suis authentifié et me donne le mot de passe du niveau suivant. Le script final peut être trouvé ici : https://github.com/ogma-sec/natas_scripts/blob/master/natas-05.py

Bonnes pratiques de sécurité

La principale faiblesse ici est à nouveau que le serveur fait confiance à une donnée qui est à 100% modifiable par l’utilisateur. Ce qui est par définition possible sur des cookies, qui sont stockés et transitent sur le poste/navigateur de l’utilisateur.

Dans le cas d’un vrai cookie de session, la valeur qu’il contient doit avoir une entropie (un taux d’aléatoire) très élevé.

Le terme entropie a été introduit en 1865 par Rudolf Clausius à partir d’un mot grec signifiant « transformation ». Il caractérise le degré de désorganisation, ou d’imprédictibilité du contenu en information d’un système.

Source : https://fr.wikipedia.org/wiki/Entropie

Cela afin que son contenu ne soit, d’une, incompréhensible par l’utilisateur, qui ne pourra donc pas deviner une valeur correcte.

Envoi d’un cookie de session contenant des informations intelligibles et donc modifiables par l’utilisateur

Et deux, qu’aucune combinaison correcte ne puisse être trouvée via une énumération de toutes les possibilités (un bruteforce).

Envoi d’un cookie de session contenant une chaine incrémentale pouvant être devinée par l’utilisateur

Un cookie de session rassemblant ces éléments ressemble par exemple à cela :

PHPSESSID=5a3ecbeef2d4e29e4acf783d142f9ebf953ecbee2d4f9ebf95 

Ici, on ne peut comprendre ce qui est stocké dans ce message, on ne peut donc pas le modifié pour en faire quelques chose de compréhensible pour le serveur. Également, l’entropie de cette chaine de caractères est élevée, on ne peut donc pas raisonnablement tomber sur une autre valeur que le serveur aurait pu générer et assigner à une autre session.

Envoi d’un cookie de session contenant une chaine aléatoire correspondant à un enregistrement côté serveur

L’ANSSI propose la page web suivante pour calculer l’entropie d’une chaine de caractère (plus précisément ici, d’un mot de passe) : Calculer la « force » d’un mot de passe.

L’OWASP parle notamment de ce sujet, et des recommandations associées, dans les articles suivants :

Également, voici quelques cas réels d’exploitation de faiblesse sur la modification des cookies :

Partager :
Published inChallenges et CTFs

2 Comments

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *