Skip to content

Over the Wire – Natas 4 – En-tête « Referer »

On continue notre série sur la suite de challenges Natas avec le niveau 4 qui va nous amener à parler de l’intérêt, mais aussi des potentiels dangers de l’en-tête Referer.

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 4 – Exploitation

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

Comme d’habitude, on arrive sur la page principale et l’on obtient le message suivant :

Accès à la page d’accueil du challenge

Ici, l’application web nous indique que seuls les utilisateurs qui viennent du site http://natas5.natas.labs.overthewire.org/ peuvent accéder au contenu de la page. La question à se poser ici est : comment un site web peut savoir d’où je viens ? Autrement dit, quelle page ou site web m’a amené ici ? Les sites web sont tous hébergés par des serveurs web différents, aucun échange d’information n’est effectué entre ces serveurs (cela serait bien trop complexe à mettre en place). L’information doit donc forcément passer par le navigateur de l’utilisateur.

Intéressons nous au contenu technique (en-tête) des échanges entre le serveur et mon navigateur. On voit dans l’image ci-dessous que si je suis sur Wikipedia et que je clique sur un lien menant vers le site de l’OWASP, la requête envoyée au service web de l’OWASP contient une information relative à la provenance de l’utilisateur, l’en-tête Referer :

Présence de l’attribut Referer

Voila comment un serveur web peut savoir d’où l’utilisateur provient, c’est mon navigateur qui lui indique en ajoutant cet en-tête automatiquement. Le serveur web n’a qu’à regarder le contenu de l’en-tête Referer de la requête faite par le navigateur.

Le lien « Refresh page » du challenge permet de comprendre cela. Au premier accès, j’obtiens cet affichage (je ne viens d’aucun site, j’ai juste saisie l’URL dans la barre d’URL de mon navigateur) :

Requête du premier accès à la page du challenge

Une fois que j’accède au lien « Refresh page« , qui me redirige simplement vers la même page, mais entraine la génération d’une nouvelle requête, j’obtiens cela :

Requête de l’accès à la page du challenge via le lien « Refresh page »

Effectivement, le lien « Refresh page » me fait changer de page, mon navigateur ajoute donc mon URL de provenance dans l’en-tête Referer de sa requête, ce que je peux voir facilement via un proxy.

Ce champ est aussi souvent utilisé pour s’assurer qu’un formulaire web envoyé par un utilisateur a bien été rempli sur le site web attendu, nous parlerons certainement des CSRF plus tard, sinon voici quelques-uns de mes articles sur le sujet :

Si je souhaite faire croire au service web de http://natas4.natas.labs.overthewire.org/ que je viens de http://natas5.natas.labs.overthewire.org/. Autrement dit que j’étais en train de naviguer sur http://natas5.natas.labs.overthewire.org/ et que j’ai cliqué sur un lien de cette page menant vers http://natas4.natas.labs.overthewire.org/. Il me suffit d’intercepter la requête générée par mon navigateur, et de modifier le contenu du champ Referer :

Modification de l’en-tête Referer via le proxy BurpSuite et obtention du flag

Le serveur se base ainsi seulement sur des données modifiables par l’utilisateur (elles ne peuvent donc être considérées comme étant de confiance) et accepte la requête.

Une autre méthode, plus alambiquée, aurait été que je dispose de mon propre serveur web et de mon propre serveur DNS (la modification de mon fichier /etc/hosts peut suffire), afin de me créer un site web http://natas5.natas.labs.overthewire.org/ local. Sur ce site web dont j’ai le contrôle, je crée une page web contenant un lien qui m’amène à l’endroit souhaité (http://natas4.natas.labs.overthewire.org/ ) et mon Referer sera le bon. Qu’il s’agisse d’un nom de domaine « uniquement local » ou résolu par un DNS mondial, mon navigateur ne fait aucunement la différence quand il s’agit de remplir l’en-tête Referer.

Exploitation via un script Python

La bibliothèque requests possède les mêmes capacités qu’un navigateur web, il peut créer des requêtes, les émettre et stocker la réponse du serveur dans un objet qui peut à son tour être traité et modifié. Ici, il suffit de créer une requête et de forger l’en-tête Referer en fonction de ce qui est attendu par le serveur. On utilise pour cela un argument supplémentaire passé à la fonction requests.get() :

headers = {'Referer' : 'http://natas5.natas.labs.overthewire.org/'}
res = requests.get(url, auth=HTTPBasicAuth(user, password), headers=headers) 

Je crée une liste nommée headers, cela me permet de créer ou modifier des en-têtes. La bibliothèque requests se charge habituellement de créer les en-têtes nécessaires sans que je n’ai à les gérer, ce qui facilite grandement son utilisation.

Plus de détails sur l’utilisation de la librairie requests et de la gestion des en-têtes ici : https://2.python-requests.org/en/master/user/quickstart/#custom-headers

Dans un contexte réel, le serveur web ne nous indique pas explicitement de quel site l’utilisateur doit provenir. Le plus souvent, la liste des domaines/pages acceptés lorsque ce type de contrôle est mis en place est le site lui-même ou quelques sites de confiance.

Pour le sport, on peut ajouté l’obtention automatique du nom de domaine à mettre dans notre Referer avec une expression régulière et la bibliothèque re (https://docs.python.org/3/library/re.html).

Pour tester vos expressions régulières facilement avant implémentation, je vous conseille d’utiliser le site regex101.com :

Voici à quoi ressemble ma fonction de recherche de l’URL à ajouter en Referer , je lui donne en paramètre d’entré mon objet HTML, elle me retourne l’URL, que j’ajoute ensuite dans mes en-têtes de requête :

def findReferer(HTMLResponse):
        # Recherche basée sur une expression régulière
	h = re.search('only from "(.*)"', HTMLResponse.text)
        # Si l'objet "h" n'est pas vide, c'est qu'une expression a été trouvée
	if h :
		return(h.group(1))

# Première requête pour obtenir le texte dans lequel rechercher.
res = requests.get(url, auth=HTTPBasicAuth(user, password))

# Utilisation de la fonction de recherche du bon Referer sur le résultat de cette première requête
acceptedReferer = findReferer(res)

# Je crée une liste d'en-tête à ajouter à la requête
headers = {'Referer' : acceptedReferer} 

# J'envoi la seconde requête avec le Referer attendu
res = requests.get(url, auth=HTTPBasicAuth(user, password),headers=headers)

Le script final peut être trouvé ici : https://github.com/ogma-sec/natas_scripts/blob/master/natas-04.py

Bonnes pratiques de sécurité

Le principal problème ici est que le serveur se base sur des données qui ne sont pas de confiance pour délivrer à l’utilisateur des informations sensibles. Comme le dit l’adage, « Never trust user input« . Toutes les données qui viennent de l’utilisateur sont à considérer comme suspectes avant d’être traitées, il est donc or de question d’accorder ou non un accès à une données sensibles en fonction de ces seules informations.

Lorsqu’il s’agit d’actions ou de données sensibles, il convient de croiser plusieurs types d’informations. Par exemple, il aurait été intéressant que le serveur web natas04 se base sur le Referer, mais également sur le partage d’un « secret » qui ne serait consultable que sur depuis natas05.

Pour reprendre le cas des attaques CSRF, qui utilisent aussi les Referer (en partie) comme protection, on y ajoute souvent un jeton anti-CSRF qui change dynamiquement à chaque génération du formulaire web. Ainsi, si un utilisateur soumet ce formulaire en provenant d’un site autre, il ne sera pas en connaissance de ce « secret dynamique » et le serveur pourra refuser la requête.

Peu d’autres solutions me viennent à l’esprit en ne se basant que sur le Referer, il s’agit par définition d’un paramètre qui n’est pas de confiance car il est 100% géré côté client, donc modifiable.

Il existe en revanche des solutions pour empêcher les navigateurs d’ajouter cet en-tête dans les requêtes qu’il émet, il s’agit de l’attribut noreferer : Cet attribut permet au serveur web d’indiquer aux navigateurs « lorsque tu suis ce lien, n’indique pas d’en-tête Referer dans ta requête« . Cet attribut est par défaut ajouté par WordPress, par exemple :

Présence de l’attribut « noreferrer » dans les liens HTML

En effet, au delà du contexte du challenge, l’attribut Referer peut être considéré comme vecteur de fuite d’informations car il renseigne le serveur web sur l’origine du trafic, on peut facilement comprendre l’impact d’une telle fuite d’informations si l’on regarde la requête suivante :

GET / HTTP/1.1
Host: ogma-sec.fr
Connection: close
Upgrade-Insecure-Requests: 1
Referer: https://internal-mattermost-supersecret.military-uk.local

Ici, le propriétaire du site ogma-sec.fr peut apprendre l’existence de https://internal-mattermost-supersecret.military-uk.local et au passage du nom de domaine interne dans lequel se trouve l’utilisateur. Le propriétaire de https://internal-mattermost-supersecret.military-uk.local devrait donc ajouter à tous ses liens sortants l’attribut noreferer pour que les navigateurs n’ajoutent pas cette information dans leurs requêtes.

Partager :
Published inChallenges et CTFs

One Comment

Laisser un commentaire

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