Introduction

Audit d'une application protégée par jeton CSRF avec Stepper, une extension Burp

Le jeton CSRF est une protection qui requiert l’insertion d’une valeur aléatoire et dynamique dans une requête. Cette valeur est ensuite analysée par le serveur pour déterminer si la requête est légitime. Lors de vos tests d’intrusion, il vous est surement déjà arrivé de tomber sur une application utilisant ces jetons CSRF. Dans ce cas, vous avez sans doute constaté à quel point il est déroutant d’analyser une telle application avec Burp.

En effet, les outils natifs de Burp ne permettent pas l’insertion arbitraire d’une telle valeur dynamique dans les requêtes. Il y a bien la possibilité de créer nativement des macros, mais cette fonctionnalité n’est pas intuitive et ne répond pas toujours à nos besoins. C’est pour cette raison que nous avons choisi de vous parler de Stepper, une extension Burp qui fait le café et bien plus encore.

Exemple de contexte

Comme expliqué précédemment, on cherche à analyser une application qui utilise des jetons CSRF comme protection. Considérons la pire situation : le jeton CSRF est à usage unique. Cela signifie qu’une nouvelle valeur de jeton CSRF est requise à chaque fois qu’une requête est envoyée.

Situation

On peut imaginer une fonction de login qui requiert au préalable l’obtention d’un jeton auprès d’un autre endpoint.

Endpoint CSRF Token

Requête :

GET /user/csrf-token HTTP/2
Host: some-example.site.com
Cookie: session=0228df79-2459-48fa-b642-85962e6f24d7;

Réponse :

HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 0
Set-Cookie: csrf_token=c91074f8-2929-4068-8adc-e2c7092d1d61; Path=/
Set-Cookie: session=0228df79-2459-48fa-b642-85962e6f24d7; Expires=Fri, 08-Apr-2022 08:04:32 GMT; HttpOnly; Path=/
Strict-Transport-Security: max-age=15724800; includeSubDomains

Ici, notre endpoint donne la nouvelle valeur de jeton CSRF à utiliser.

Endpoint de login

Sur l’endpoint de login, on observe le comportement suivant :

Requête :

POST /user/login HTTP/2
Host: some-example.site.com
Cookie: session=0228df79-2459-48fa-b642-85962e6f24d7; csrf_token=c91074f8-2929-4068-8adc-e2c7092d1d61;
X-Csrf-Token: c91074f8-2929-4068-8adc-e2c7092d1d61

{"email":"[email protected]","password":"*****************"}

Réponse si le jeton CSRF a été mis à jour avec la nouvelle valeur :

HTTP/2 200 OK
Content-Type: application/json
Content-Length: 3
Set-Cookie: session=0228df79-2459-48fa-b642-85962e6f24d7; Expires=Fri, 08-Apr-2022 08:05:57 GMT; HttpOnly; Path=/

{}

Réponse sinon :

HTTP/2 403 Forbidden
Content-Type: application/json
Content-Length: 38

{"message": "Invalid CSRF token""}

Ici, on observe que la valeur du cookie csrf_token et de l’en-tête X-Csrf-Token doivent être mis à jour avant la requête POST login sinon elle est refusée par le serveur.

Problématique

Cette situation est problématique, car nativement, Burp ne possède pas de solution (intuitive) pour effectuer un scan tout en renouvelant pour chaque requête le jeton CSRF. Heureusement, on peut outrepasser cette limitation avec l’extension Stepper.

Principe de Stepper

Stepper fonctionne en exécutant un ensemble d’une ou plusieurs requêtes appelées Séquences. Pour chaque séquence, il est possible de définir des variables dont les valeurs dépendent des réponses à ces requêtes. Ces variables sont mises à jour à chaque exécution de la Séquence. L’exécution préalable de ces Séquences et la consultation des variables sont réalisables depuis les autres outils de burp. Ce qui a pour effet de mettre à disposition des variables dynamiques qui dépendent de requêtes prédéfinies.

Définition de la Séquence CSRF

Pour satisfaire notre exemple, nous devons créer une séquence qui contient la requête à l’endpoint /user/csrf-token. Pour cela, nous pouvons directement sélectionner notre requête dans le proxy et l’ajouter à une nouvelle Séquence grâce au menu contextuel du clic droit (Extensions > Stepper > Add 1 item to Stepper > New Sequence).

Création d’une nouvelle séquence Stepper depuis le menu contextuel d’une requête du proxy
Définition du nom de la nouvelle séquence

Après la définition du nom de la nouvelle séquence, la requête est ajoutée dans Stepper.

Définition des variables globales

Stepper dispose d’un menu pour définir des variables globales. Ce sont des constantes qui pourront être réutilisées dans les Séquences. Depuis ce menu, je définis la valeur de mon cookie de session avant authentification. Ainsi je peux réutiliser sa valeur dans la requête d’obtention du jeton CSRF.

NB : La définition de cette variable globale n’est pas nécessaire dans le contexte de notre exemple. C’est simplement une démonstration d’usage de cette fonctionnalité.

Définition de la variable globale session avec mon cookie de session préauthentification

L’appel à une variable depuis une séquence s’effectue comme ceci :

$VAR:<VARNAME>$

Ainsi, pour utiliser la variable session depuis la Séquence CSRF , il suffit d’effectuer cette modification dans la Séquence :

- Cookie: [...]; session=0228df79-2459-48fa-b642-85962e6f24d7;
+ Cookie: [...]; session=$VAR:session$;	

Exécution de la séquence

Il est possible d’exécuter manuellement une séquence via le bouton Execute Step. Dans notre cas, cela nous permet d’obtenir la réponse du serveur.

La variable globale $VAR:session$ a été remplacé par sa valeur dans la requête de séquence.

Exécution manuelle de la séquence

C’est dans cette réponse serveur que nous allons récupérer la valeur de notre jeton CSRF. Pour cela, nous avons besoin de déclarer une variable post exécution.

Définition des variables post-exécution

Les variables post-exécution sont des variables qui sont mises à jour après chaque exécution de la séquence. Il est possible de définir une expression régulière qui viendra extraire la valeur voulue dans la réponse du serveur. L’exemple ci-dessous montre comment définir une variable post-exécution qui viendra extraire la valeur de notre jeton CSRF.

Définition du nom de la variable post-exécution

L’expression régulière pour capturer la valeur du cookie csrf_token est la suivante :

csrf_token=([\-0-9a-f]+); Path
Vue après définition de la variable post-exécution pour capturer la valeur du jeton CSRF

Cette variable CSRF pourra être consultée par les outils de Burp. Il est même possible d’automatiser l’exécution de la Séquence pour mettre à jour la valeur du jeton avant chaque requête.

Utilisation de Stepper depuis Repeater

Maintenant, si l’on reprend notre requête de login et qu’on cherche à la rejouer avec le Repeater de Burp, nous nous retrouvons dans la situation évoquée plus haut.

Rejeu de la requête de login sans mise à jour du jeton CSRF

Sans la mise à jour de notre jeton CSRF, le serveur ne traite pas notre requête. Cependant, maintenant que nous avons défini la Séquence CSRF, il nous est possible d’utiliser ses variables.

L’utilisation des variables depuis un autre outil s’effectue de cette façon :

$VAR:<Sequence_name>:<var_name>$

Enfin, pour indiquer que la séquence doit être exécutée avant l’envoi de la requête, cet en-tête doit être ajouté à la requête :

X-Stepper-Execute-Before: <Sequence_name>

Ainsi, pour correspondre à notre besoin, la requête doit être modifiée comme suit :

POST /user/login HTTP/2
Host: some-example.site.com
- Cookie: session=0228df79-2459-48fa-b642-85962e6f24d7; csrf_token=c91074f8-2929-4068-8adc-e2c7092d1d61;
+ Cookie: session=$VAR:CSRF:session$; csrf_token=$VAR:CSRF:CSRF$;
- X-Csrf-Token: c91074f8-2929-4068-8adc-e2c7092d1d61
+ X-Csrf-Token: $VAR:CSRF:CSRF$
+ X-Stepper-Execute-Before: CSRF

{"email":"[email protected]","password":"*****************"}

Ainsi, à chaque fois que la requête est envoyée, la Séquence est préalablement exécutée et le jeton CSRF est mis à jour.

Rejeu de la requête de login avec mise à jour automatique du jeton CSRF

Ceci s’observe par la réussite de la requête de login ci-dessus. Preuve que notre requête a bien été traitée par le serveur.

Utilisation de Stepper dans Intruder

Bon, c’est bien joli, mais ce que nous avons effectué pour l’instant n’est pas bien impressionnant. Pour vraiment servir à quelque chose, il faut étendre cette configuration à d’autres outils.

Imaginons maintenant que nous souhaitions effectuer une bruteforce attack sur l’endpoint de login. C’est une attaque qui consiste à effectuer de nombreuses tentatives de login jusqu’à obtenir le bon mot de passe.

Ici, la requête de Repeater qui contient notre paramétrage Stepper peut directement être envoyée dans l’outil Intruder.

Réception de la requête par Intruder

Dès lors, il ne reste plus qu’à sélectionner notre liste de mots de passe potentiels pour l’attaque.

Définition de la liste de mots de passe potentiels ( bruteforce attack )

L’attaque peut être lancée sans se soucier du reste. Dans les résultats de l’attaque, aucune erreur liée au jeton CSRF n’est observée.

Résultat de l’attaque

La gestion du jeton CSRF a été traitée par Stepper. D’ailleurs, si l’on consulte le détail des requêtes envoyées par Burp lors de l’attaque, on observe bien qu’avant chaque tentative de login, une requête pour récupérer la nouvelle valeur du jeton CSRF est envoyée.

Logger après l’attaque de Intruder

Conclusion

Les exemples ci-dessus ne sont bien sûr qu’une introduction. Il est possible d’appliquer cette automatisation dans d’autres outils comme le Scanner de Burp, ou bien de définir une séquence plus complexe ou avec plus de variables. Mais le potentiel de stepper est bien présent. Merci à Corey Arthur pour cette superbe extension.