
Fuzzer HTTP/1 basé sur une grammaire avec capacité de mutation
T-Reqs (pour Two Requests - deux requêtes) est un fuzzer HTTP basé sur une grammaire, développé dans le cadre de l'article paper intitulé "T-Reqs: HTTP Request Smuggling with Differential Fuzzing" présenté à ACM CCS 2021.
BibTeX de l'article :
@inproceedings{ccs2021treqs,
title={T-Reqs: HTTP Request Smuggling with Differential Fuzzing},
author={Jabiyev, Bahruz and Sprecher, Steven and Onarlioglu, Kaan and Kirda, Engin},
booktitle={Proceedings of the 2021 ACM SIGSAC Conference on Computer and Communications Security},
pages={1805--1820},
year={2021}
}
T-Reqs permet de fuzzer des serveurs HTTP en envoyant des requêtes HTTP mutées avec des versions 1.1 et antérieures. Il comporte trois composants principaux : 1) la génération d'entrées, 2) la mutation des entrées générées et 3) leur distribution vers le(s) serveur(s) cible(s).
Une grammaire CFG (grammaire hors contexte) introduite dans le fuzzer est utilisée pour générer des requêtes HTTP. Comme la grammaire exemple ci-dessous est adaptée au fuzzing de la ligne de requête, chaque composant de la ligne de requête et les valeurs possibles pour chacun sont explicitement spécifiés. Cela permet de générer des requêtes valides avec diverses formes de ligne de requête et aussi de traiter chaque composant de la ligne de requête comme une unité distincte du point de vue de la mutation.
'<start>':
['<request>'],
'<request>':
['<request-line><base><the-rest>'],
'<request-line>':
['<method-name><space><uri><space><protocol><separator><version><newline>'],
'<method-name>':
['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'],
'<space>':
[' '],
'<uri>':
['/_URI_'],
'<protocol>':
['HTTP'],
'<separator>':
['/'],
'<version>':
['0.9', '1.0', '1.1'],
'<newline>':
['\r\n'],
'<base>':
['Host: _HOST_\r\nConnection:close\r\nX-Request-ID: _REQUEST_ID_\r\n'],
'<the-rest>':
['Content-Length: 5\r\n\r\nBBBBBBBBBB'],
Chaque composant peut être marqué de deux façons : mutable en chaîne et mutable en arbre (voir l'exemple de configuration). Si un composant est mutable en chaîne, alors un caractère aléatoire peut être supprimé, remplacé ou inséré à une position aléatoire. Dans l'exemple ci-dessous (côté gauche), le dernier caractère de la version du protocole (1) est supprimé, la troisième lettre du nom de la méthode (S) est remplacée par R, et une barre oblique est insérée au début de l'URI. En revanche, si un composant est mutable en arbre, alors un composant aléatoire peut être supprimé, remplacé ou inséré à une position aléatoire sous ce composant. L'exemple ci-dessous (côté droit) montre trois mutations d'arbre appliquées au composant ligne de requête : 1) method est remplacé par protocol, 2) un URI supplémentaire est inséré après l'URI actuel, et 3) le proto existant est supprimé.

Le fuzzer doit être informé des préférences de l'utilisateur concernant la génération et la mutation des entrées. Plus précisément, la grammaire d'entrée, les composants mutables, les préférences de mutation, entre autres, doivent être spécifiés dans le fichier de configuration (voir un exemple de configuration).
Pour pouvoir reproduire les entrées générées et mutées à chaque itération, un nombre de départ (seed) est utilisé. En fait, ce nombre de départ sert de graine pour les générations de nombres aléatoires lors de la formation et de la mutation d'une entrée. Selon la façon dont ces graines sont fournies au fuzzer, celui-ci fonctionne dans l'un de ces deux modes : individuel et par lot. Dans le mode individuel, les entrées sont générées et mutées en fonction des graines spécifiées par l'utilisateur. Dans la commande ci-dessous, une seule graine (par exemple 505) est spécifiée. Alternativement, une liste de graines peut être spécifiée avec l'option -f (voir la page d'aide pour plus d'informations).
python3 main.py -i -c config -s 505
Alors que dans le mode par lot (qui est par défaut), il commence à zéro comme valeur de départ et l'incrémente à chaque itération jusqu'à atteindre le nombre de fin. Les nombres de début et de fin peuvent être personnalisés.
python3 main.py -c config
Nous partageons également un Dockerfile pour vous permettre d'exécuter le code t-reqs. Vous pouvez exécuter les commandes ci-dessous pour commencer :
# run the command below under the directory which has the Dockerfile
docker build -t test/treqs .
# create a container after you built the image using the command above
docker run -ti test/treqs bash
# run the commands below in the started docker shell
cd t-reqs/
python3 code/main.py -c config -n -i -s90
La contrebande de requêtes HTTP (HTTP Request Smuggling) repose sur des comportements différents d'analyse du corps entre serveurs, où un serveur utilise l'en-tête Transfer-Encoding tandis que l'autre préfère l'en-tête Content-Length pour déterminer les limites du corps d'une requête, ou bien un serveur ignore le corps d'une requête alors que l'autre le traite.
Pour analyser l'analyse du corps des serveurs en réponse à diverses mutations sous diverses formes d'une requête HTTP, nous devons installer un mécanisme de retour sur ces serveurs pour nous renseigner sur le comportement d'analyse du corps. Une façon d'installer un mécanisme de retour sur un serveur est de le faire fonctionner en mode proxy inverse et de lui faire transférer les requêtes vers un script "fournisseur de retour" (feedback provider) exécuté en tant que service. Ce service mesure la longueur du corps dans les requêtes reçues et l'enregistre pour la comparer ultérieurement avec d'autres serveurs.
Un exemple de script "feedback provider" est disponible dans ce dépôt. Cependant, ce script renvoie l'information de longueur du corps dans une réponse en supposant que cette information est stockée côté client.
T-Reqs est licencié sous licence MIT.