
Bibliothèque Python pour Turbo Intruder qui ajoute la prise en charge des positions de charge utile et les types d'attaque Sniper/Clusterbomb/Pitchfork avec génération de tests basée sur des balises pour le fuzzing HTTP.

Haptyc est une bibliothèque Python conçue pour ajouter la prise en charge des positions de payload et des types d'attaque Sniper/Clusterbomb/Batteringram/Pitchfork dans Turbo Intruder. Bien que Haptyc atteigne assez bien ces objectifs, il introduit également une manière plus simple d'exprimer les séquences de tests en général. Bien que cette bibliothèque soit destinée à Turbo Intruder, elle n'a pas de dépendance stricte envers Turbo Intruder et peut être utilisée partout où une génération de tests est nécessaire dans un contexte Python. Malheureusement, pour l'instant, comme Haptyc a été conçu pour un interpréteur Jython, il ne supporte que Python 2.7 (cependant, des changements futurs corrigeront cela).
Les balises Haptyc sont des balises qu'un testeur peut utiliser pour annoter un payload d'entrée original. Un testeur peut utiliser plusieurs balises pour entourer des éléments clés d'une requête HTTP et les encapsuler en tant que payload positionnel. Lors de la génération des tests, Haptyc analysera toutes les balises du payload original et générera des tests conformément aux fonctions associées aux noms des balises. Lorsque Haptyc évalue une balise Haptyc, il exécute la fonction de balise associée (appelée Transformée Haptyc) pour un payload de test à placer à la position de la balise dans la requête. Chaque fonction de balise reçoit un argument et un argument . L'argument data peut contenir les données internes de la balise ou une autre séquence de payload de test. L'argument state est un objet d'état associé à la balise où l'état peut être stocké entre les itérations de test. Examinons un exemple.
Payload original :
GET /animal/[+GuessAnimal]dog[+end] HTTP/1.1
Classe Haptyc et Transformée Haptyc :
from haptyc import *
original = "GET /animal/[+GuessAnimal]dog[+end] HTTP/1.1"
class TestLogic(Transform):
@ApplyList(["snake","cat","owl","lion"])
def test_GuessAnimal(self, data, state):
return data + "?original=" + self.inner() + "&attempt=" + str(state.iter)
TestFactory = TestLogic(original)
for test in TestFactory:
print(test)
Tests générés :
GET /animal/snake?original=dog&attempt=0 HTTP/1.1
GET /animal/cat?original=dog&attempt=1 HTTP/1.1
GET /animal/owl?original=dog&attempt=2 HTTP/1.1
GET /animal/lion?original=dog&attempt=3 HTTP/1.1
Dans l'exemple ci-dessus, nous montrons comment exprimer des tests de manière simple avec Haptyc. D'abord, la bibliothèque Haptyc est importée. Ensuite, nous avons défini les données originales avec nos annotations de balises Haptyc (GuessAnimal). Enfin, la classe TestLogic est définie et étendue en tant que classe Transform. À l'intérieur de cette classe, chaque méthode qui commence par test_ sera enregistrée comme une balise Haptyc pour évaluation dans le payload original. Nous utilisons un décorateur de logique pour appliquer la logique d'état de cette transformée Haptyc. Dans ce cas, nous utilisons le décorateur @ApplyList(list) pour indiquer à Haptyc de générer un test pour chaque élément de la liste spécifiée et de placer cet élément dans la transformée Haptyc en tant qu'argument data. À l'intérieur de la transformée, nous renvoyons une version mutée de data à insérer à la position de la balise. Dans ce cas, la mutation est l'élément de la liste en tant que data concaténé avec les données entourées par la balise (dog), puis concaténé avec la valeur iter dans l'objet state. Enfin, le code Python restant montre la création de l'objet TestFactory et la génération de tous les tests dans une boucle for. C'est un exemple d'attaque standard de type sniper qui cible une seule position de payload. Voyons maintenant d'autres styles d'attaques.
Payload original :
GET /animal?type=[%type]dog[%end]&name=[%name]fido[%end] HTTP/1.1
Classe Haptyc et Transformée Haptyc :
from haptyc import *
original = "GET /animal?type=[%type]dog[%end]&name=[%name]fido[%end] HTTP/1.1"
class TestLogic(Transform):
@ApplyList("snake","cat","owl","lion")
def test_type(self, data, state):
return data
@ApplyList("Frank", "Lisa", "Jin", "Tooth")
def test_name(self, data, state):
return data
TestFactory = TestLogic(original)
for test in TestFactory:
print(test)
Tests générés :
GET /animal?type=snake&name=Frank HTTP/1.1
GET /animal?type=snake&name=Lisa HTTP/1.1
GET /animal?type=snake&name=Jin HTTP/1.1
GET /animal?type=snake&name=Tooth HTTP/1.1
GET /animal?type=cat&name=Frank HTTP/1.1
GET /animal?type=cat&name=Lisa HTTP/1.1
GET /animal?type=cat&name=Jin HTTP/1.1
GET /animal?type=cat&name=Tooth HTTP/1.1
GET /animal?type=owl&name=Frank HTTP/1.1
GET /animal?type=owl&name=Lisa HTTP/1.1
GET /animal?type=owl&name=Jin HTTP/1.1
GET /animal?type=owl&name=Tooth HTTP/1.1
GET /animal?type=lion&name=Frank HTTP/1.1
GET /animal?type=lion&name=Lisa HTTP/1.1
GET /animal?type=lion&name=Jin HTTP/1.1
GET /animal?type=lion&name=Tooth HTTP/1.1
L'exemple 1 a montré comment évaluer les transformées en mode sniper en utilisant l'annotation '+' dans la balise [+tag][+end]. L'exemple 2 montre comment utiliser 2 transformées/positions pour mener une attaque de type clusterbomb. Comme vous pouvez le voir, nous utilisons deux balises de transformée distinctes appelées [%type][%end] et [%name][%end]. Le signe '%' indique à Haptyc d'évaluer ces transformées en mode clusterbomb : pour chaque payload de la première transformée, créer un test avec le payload de la deuxième transformée. Le nombre de tests est le nombre de tests de chaque transformée impliquée multiplié entre eux.
En utilisant exactement le même code Python, nous pouvons passer du style d'attaque clusterbomb à pitchfork en remplaçant '%' par '#'. Les attaques de type pitchfork placent les payloads de position en parallèle. Le nombre de tests est le plus petit nombre de tests parmi toutes les transformées impliquées.
Payload original :
GET /animal?type=[#type]dog[#end]&name=[#name]fido[#end] HTTP/1.1
Tests générés :
GET /animal?type=snake&name=Frank HTTP/1.1
GET /animal?type=cat&name=Lisa HTTP/1.1
GET /animal?type=owl&name=Jin HTTP/1.1
GET /animal?type=lion&name=Tooth HTTP/1.1
Payload original :
GET /animal?type=dog&id=[+idor]0[+end]&process=[@randbool]False[@end] HTTP/1.1
Classe Haptyc et Transformée Haptyc :
from haptyc import *
import random
original = "GET /animal?type=dog&id=[+idor]0[+end]&process=[@randbool]False[@end] HTTP/1.1"
class TestLogic(Transform):
@ApplyIteration(10)
def test_idor(self, data, state):
return str(state.iter)
def per_randbool(self, data):
return random.choice(["True", "False"])
TestFactory = TestLogic(original)
for test in TestFactory:
print(test)
Tests générés :
GET /animal?type=dog&id=0&process=False HTTP/1.1
GET /animal?type=dog&id=1&process=True HTTP/1.1
GET /animal?type=dog&id=2&process=False HTTP/1.1
GET /animal?type=dog&id=3&process=True HTTP/1.1
GET /animal?type=dog&id=4&process=False HTTP/1.1
GET /animal?type=dog&id=5&process=True HTTP/1.1
GET /animal?type=dog&id=6&process=False HTTP/1.1
GET /animal?type=dog&id=7&process=True HTTP/1.1
GET /animal?type=dog&id=8&process=False HTTP/1.1
GET /animal?type=dog&id=9&process=False HTTP/1.1
Les transformées persistantes sont indiquées par le signe '@' et les fonctions de transformée commencent toujours par per_ car ces transformées ne sont pas itératives : elles ne créent pas de tests ni ne conservent d'état. Ce sont simplement des transformations naïves que vous pouvez appliquer n'importe où dans le payload pour une transformation sans état, sans affecter les transformées avec état. Comme elles ne prescrivent aucun test, vous ne pouvez pas générer de tests uniquement avec des transformées persistantes ; elles sont destinées à être mélangées avec des transformées itératives. Dans l'exemple ci-dessus, nous avons une transformée de type sniper de 10 tests plaçant un identifiant incrémental. Nous avons également une transformée persistante qui place un booléen aléatoire à sa position.
Il peut arriver qu'avant le début d'une séquence de tests, le testeur souhaite effectuer un traitement/une initialisation. Pour prendre en charge cela, Haptyc exécute toutes les transformées impliquées dans une phase d'initialisation avant d'exécuter la transformée pour la génération de tests. Cette étape d'initialisation peut être utilisée pour effectuer toute initialisation requise par le testeur et la stocker dans l'objet state. Pour cela, le testeur peut utiliser state.init comme un booléen pour déterminer si l'exécution est en phase d'initialisation. Toutes les données renvoyées par l'étape d'initialisation seront ignorées.
Payload original :
GET /animal?data=[+b64mutate]SGVsbG8gSGFja2VyIQ==[+end] HTTP/1.1
Classe Haptyc et Transformée Haptyc :
from haptyc import *
import base64
original = "GET /animal?data=[+b64mutate]SGVsbG8gSGFja2VyIQ==[+end] HTTP/1.1"
class TestLogic(Transform):
@ApplyIteration(10)
def test_b64mutate(self, data, state):
if state.init:
state.decoded = base64.b64decode(data)
return
return base64.b64encode(random_insert(state.decoded, ["'"]))
TestFactory = TestLogic(original)
for test in TestFactory:
print(test)
Tests générés :
GET /animal?data=SGVsbG8gSCdhY2tlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFja2VyISc= HTTP/1.1
GET /animal?data=SGVsbG8gSGFjaydlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGEnY2tlciE= HTTP/1.1
GET /animal?data=SCdlbGxvIEhhY2tlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFjaydlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFja2VyISc= HTTP/1.1
GET /animal?data=SCdlbGxvIEhhY2tlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFjJ2tlciE= HTTP/1.1
GET /animal?data=SGVsbG8gSGFjJ2tlciE= HTTP/1.1
Dans l'exemple ci-dessus, le test utilise state.init pour décoder en base64 le payload interne encapsulé une seule fois au début de la séquence de test et stocker ce résultat dans state.decoded. Ensuite, pour toutes les exécutions normales de génération de tests, state.decoded est utilisé comme données internes décodées à traiter. Ce type de motif est utile pour améliorer les performances de votre transformée car un seul décodage a lieu au début (contrairement au décodage du même payload à chaque génération de test).
[+tag]inner[+end] - Transformée itérative de type Sniper[%tag]inner[%end] - Transformée itérative de type Clusterbomb[#tag]inner[#end] - Transformée itérative de type Batteringram/Pitchfork[@tag]inner[@end] - Transformée persistante sans état| Nom | Arguments | Entrée data | Description |
|---|---|---|---|
| @ApplyIteration(n) | n = nombre d'itérations | valeur interne de la balise haptyc | Logique pour générer N tests avec inner comme data |
| @ApplyRange(b,e,s=1) | b = valeur de début, e = valeur maximale, s = pas | valeur générée de la plage | Logique pour générer un test pour chaque valeur pas à pas avec la valeur donnée comme data |
| @ApplyList(L) | L = liste Python | élément de la liste | Logique pour générer un test pour chaque valeur de la liste donnée comme data |
| @ApplyFilelist(path) | path = chemin du système de fichiers | élément de la liste | Logique pour générer un test pour chaque valeur de la liste de fichiers donnée comme data |
| @ApplyPayloads(name) | name = nom de liste intégrée | élément de la liste | Logique pour générer un test pour chaque valeur de la liste intégrée donnée comme data |
| Nom | Arguments | Description |
|---|---|---|
| @CloneTransform(srcname, destname) | srcname=chaîne d'une méthode de transformée à copier, destname=chaîne d'une méthode de transformée inexistante dans laquelle copier | CloneTransform est utilisé pour copier l'implémentation d'une transformée dans un autre espace de noms sans avoir à copier/coller. Utile dans les attaques de style '%' et '#' lorsque vous devez réutiliser la même implémentation de transformée à plusieurs positions |
| Nom | Description |
|---|---|
| self.inner() | Récupère le payload interne de la balise |
| self.stop() | Interrompt immédiatement la génération de tests de cette transformée |
| self.me() | Renvoie le nom du contexte actuel de la transformée |
| self.set_label(label) | Définit l'étiquette pour ce test actuel |
| self.get_label(label) | Récupère l'étiquette pour ce test actuel |
| Nom | Description |
|---|---|
| state.iter | Compteur d'itération actuel de la transformée (base 0) |
| state.init | Booléen indiquant si on est en phase d'initialisation |
| Nom | Description |
|---|---|
| radamsa(data) | Cette fonction exécute radamsa sur les données d'entrée et renvoie le résultat (radamsa doit être installé) |
| index_insert(data, list, index) | Cette fonction insère un payload de la liste dans les données fournies à l'index donné |
| random_insert(data, list) | Cette fonction insère un payload de la liste dans les données fournies à un index aléatoire |
Il y a 2 façons d'installer Haptyc
turbo-intruder-all_w_haptyc.jar jointe à ce dépôtQuelle que soit la méthode choisie, ces versions n'incluent pas radamsa ; si vous voulez le support de radamsa, vous devez l'installer à partir de ce dépôt : (Optionnel) Installez radamsa via https://gitlab.com/akihe/radamsa
turbo-intruder-all_w_haptyc.jar dans le répertoire releaseAddSelect File ... et choisissez turbo-intruder-all_w_haptyc.jar./install.sh <chemin absolu du répertoire contenant turbo-intruder-all.jar>