
Fuzzer basé sur la couverture pour applications Python
pip3 install frelatage
Le fuzzer Python que le monde mérite
Installation | Fonctionnement | Fonctionnalités | Utiliser Frelatage | Configuration
Frelatage est une bibliothèque de fuzzing Python basée sur la couverture qui peut être utilisée pour fuzzer du code Python. Le développement de Frelatage a été inspiré par divers autres fuzzers, notamment AFL/AFL++, Atheris et PythonFuzz. L'objectif principal du projet est de tirer parti des meilleures fonctionnalités de ces fuzzers et de les rassembler dans un nouvel outil afin de fuzzer efficacement les applications Python.
AVERTISSEMENT : Ce projet est au stade alpha et peut encore provoquer de nombreux comportements inattendus. Frelatage ne doit pas être utilisé dans un environnement de production pour le moment.
pip3 install frelatage
Recommandé pour les développeurs. Clone automatiquement la branche principale du dépôt frelatage et installe à partir des sources.
# Clone automatiquement le dépôt Frelatage et installe Frelatage depuis les sources
bash <(wget -q https://raw.githubusercontent.com/Rog3rSm1th/Frelatage/main/scripts/autoinstall.sh -O -)
L'idée derrière la conception de Frelatage est l'utilisation d'un algorithme génétique pour générer des mutations qui couvriront le plus de code possible. Le fonctionnement d'un cycle de fuzzing peut être résumé approximativement par ce diagramme :
graph TB
m1(Mutation 1) --> |input| function(Fonction fuzzée)
m2(Mutation 2) --> |input| function(Fonction fuzzée)
mplus(Mutation ...) --> |input| function(Fonction fuzzée)
mn(Mutation n) --> |input| function(Fonction fuzzée)
function --> generate_reports(Générer des rapports)
generate_reports --> rank_reports(Classer les rapports)
rank_reports --> select(Sélectionner les n meilleurs rapports)
select --> |mutate| nm1(Mutation 1) & nm2(Mutation 2) & nmplus(Mutation ...) & nmn(Mutation n)
subgraph Cycle mutations
direction LR
m1
m2
mplus
mn
end
subgraph Mutations du cycle suivant
direction LR
nm1
nm2
nmplus
nmn
end
style function fill:#5388e8,stroke:white,stroke-width:4px
Frelatage permet de fuzzer une fonction en passant un fichier en entrée.
import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.parse(data)
input = frelatage.Input(value="initial_value")
f = frelatage.Fuzzer(MyFunctionFuzz, [[input]])
f.fuzz()
Frelatage vous offre la possibilité de fuzzer des paramètres d'entrée de type fichier. Pour initialiser la valeur de ces fichiers, vous devez créer des fichiers dans le dossier d'entrée (./in par défaut).
Si nous voulons initialiser la valeur d'un fichier utilisé pour le fuzzing, nous pouvons le faire ainsi :
echo "initial value" > ./in/input.txt
Et ensuite exécuter le fuzzer :
import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.load_file(data)
input = frelatage.Input(file=True, value="input.txt")
f = frelatage.Fuzzer(MyFunctionFuzz, [[input]])
f.fuzz()
import frelatage
import my_vulnerable_library
input = frelatage.Input(file=True, value="input.txt")
@frelatage.instrument([[input]])
def MyFunctionFuzz_1(data):
my_vulnerable_library.load_file(data)
@frelatage.instrument([[input]])
def MyFunctionFuzz_1(data):
my_vulnerable_library.load_file_but_different(data)
# Et ainsi de suite
# Il fuzzera les méthodes instrumentées les unes après les autres
frelatage.Fuzzer.fuzz_all()
Si vous avez besoin de charger plusieurs fichiers dans un corpus en une seule fois (utile si vous utilisez un gros corpus), vous pouvez utiliser la fonction intégrée de Frelatage load_corpus. Cette fonction retourne une liste d'entrées (inputs).
load_corpus(directory: str, file_extensions: list) -> list[Input]
./, ./images["jpeg", "gif"], ["pdf"]import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.load_file(data)
my_vulnerable_library.load_file(data2)
# Charge tous les fichiers du répertoire ./in
corpus_1 = frelatage.load_corpus(directory="./")
# Charge tous les fichiers .gif/.jpeg du sous-répertoire ./in/images
corpus_2 = frelatage.load_corpus(directory="./images", file_extension=["gif", "jpeg"])
f = frelatage.Fuzzer(MyFunctionFuzz, [corpus_1, corpus_2])
f.fuzz()
Vous pouvez copier un ou plusieurs dictionnaires situés ici dans le répertoire dédié aux dictionnaires (./dict par défaut).
Le fuzzing différentiel est une technique de test logiciel populaire qui tente de détecter des bogues en fournissant la même entrée à plusieurs bibliothèques/programmes et en observant les différences dans leurs comportements. Vous trouverez un exemple ici d'une utilisation du fuzzing différentiel avec Frelatage avec les bibliothèques json et ujson.
Vous pouvez trouver plus d'exemples de fuzzers et de corpus dans le répertoire des exemples.
Chaque rapport de crash est sauvegardé dans le dossier de sortie (./out par défaut), dans un dossier nommé : id:<ID du crash>,err:<type d'erreur>,err_pos:<erreur>,err_file:<fichier d'erreur>.
Le dossier de rapport a la forme suivante :
├── out
│ ├── id:<ID du crash>,err:<type d'erreur>,err_file:<fichier d'erreur>,err_pos:<position de l'erreur>
│ ├── message
│ ├── input
│ ├── 0
│ ├── <fichier_entrée1>
│ ├── ...
│ ├── ...
Chaque rapport de crash contient un fichier message qui contient le message d'erreur.
Chaque rapport d'augmentation de couverture est sauvegardé dans le dossier de couverture (./cov par défaut), dans un dossier nommé : coverage_<ID>, et le dossier de rapport a la même forme que les dossiers de rapports de crash.
Les entrées passées à une fonction sont sérialisées à l'aide du module pickle avant d'être sauvegardées dans le fichier <dossier_rapport>/input. Il est donc nécessaire de le désérialiser pour pouvoir lire le contenu du fichier. Cette action peut être effectuée avec la commande frelatage-report.
$ frelatage-report input
Il y a deux façons de configurer Frelatage :
Un exemple de configuration :
export FRELATAGE_DICTIONARY_ENABLE=1 &&
export FRELATAGE_SAVE_NEW_COVERAGE=1 &&
export FRELATAGE_TIMEOUT_DELAY=2 &&
export FRELATAGE_INPUT_FILE_TMP_DIR="/tmp/frelatage" &&
export FRELATAGE_INPUT_MAX_LEN=4096 &&
export FRELATAGE_MAX_THREADS=8 &&
export FRELATAGE_MAX_STAGES=1000000 &&
export FRELATAGE_MAX_CYCLES_WITHOUT_NEW_PATHS=5000 &&
export FRELATAGE_INPUT_DIR="./in" &&
export FRELATAGE_DICTIONARY_DIR="./dict" &&
python3 fuzzer.py
import frelatage
def myfunction(input1_string, input2_int):
pass
input1 = frelatage.Input(value="initial_value")
input2 = frelatage.Input(value=2)
f = frelatage.Fuzzer(
# La méthode que vous voulez fuzzer
method=myfunction,
# Corpus
corpus=[[input1], [input2]],
# Nombre de threads
threads_count=8,
# Exceptions qui seront prises en compte
exceptions_whitelist=(OSError),
# Exceptions qui ne seront pas prises en compte
exceptions_blacklist=(),
# Répertoire où seront stockés les rapports d'erreur
output_directory="./out",
# Répertoire où seront stockés les rapports d'augmentation de couverture
coverage_directory="./cov",
# Activer ou désactiver le mode silencieux
silent=False,
# Activer ou désactiver le fuzzing infini
infinite_fuzz=False
)
f.fuzz()
Veuillez garder à l'esprit que, comme pour de nombreuses autres tâches nécessitant beaucoup de calculs, le fuzzing peut mettre à rude épreuve votre matériel et le système d'exploitation. En particulier :
Votre CPU chauffera et aura besoin d'un refroidissement adéquat. Dans la plupart des cas, si le refroidissement est insuffisant ou cesse de fonctionner correctement, les vitesses du CPU seront automatiquement réduites. Cela dit, surtout lorsque vous fuzzez sur du matériel moins adapté (ordinateurs portables, smartphones, etc.), il n'est pas totalement impossible que quelque chose explose.
Les programmes ciblés peuvent finir par consommer de manière erratique des gigaoctets de mémoire ou remplir l'espace disque avec des fichiers indésirables. Frelatage essaie d'imposer des limites de mémoire de base, mais ne peut pas empêcher tous les incidents possibles. En fin de compte, vous ne devriez pas faire de fuzzing sur des systèmes où le risque de perte de données n'est pas acceptable.
Le fuzzing implique des milliards de lectures et d'écritures sur le système de fichiers. Sur les systèmes modernes, cela sera généralement fortement mis en cache, ce qui se traduira par des entrées/sorties "physiques" assez modestes - mais de nombreux facteurs peuvent modifier cette équation. Il est de votre responsabilité de surveiller les problèmes potentiels ; avec des entrées/sorties très lourdes, la durée de vie de nombreux disques durs et SSD peut être réduite.
Un bon moyen de surveiller les entrées/sorties disque sous Linux est la commande 'iostat' :
$ iostat -d 3 -x -k [...ID du disque optionnel...]
Je suis Rog3rSm1th, j'ai 21 ans et je suis un passionné français d'informatique et de cybersécurité. J'aime développer des outils (OSINT, Fuzzing...) et participer à des CTFs/Wargames. Pour en savoir plus sur moi et mes projets, cliquez simplement ici.
➜ Si vous souhaitez m'embaucher pour l'un de vos projets (Programmation, cybersécurité...), contactez-moi à l'adresse [email protected] et nous évaluerons ensemble vos besoins.
Pour toute remarque, suggestion, signalement de bogue, ou si vous avez trouvé un bogue en utilisant Frelatage, vous pouvez me contacter à l'adresse [email protected] ou sur Twitter @Rog3rSm1th.
| Variable d'environnement | Description | Valeurs Possibles | Valeur par défaut |
|---|
| FRELATAGE_DICTIONARY_ENABLE | Activer l'utilisation de mutations basées sur les éléments du dictionnaire | 1 pour activer, 0 sinon | 1 |
| FRELATAGE_SAVE_NEW_COVERAGE | Sauvegarder les nouvelles entrées de couverture pour les réutiliser plus tard | 1 pour activer, 0 sinon | 1 |
| FRELATAGE_TIMEOUT_DELAY | Délai en secondes après lequel une fonction retournera une TimeoutError | 1 - infini | 2 |
| FRELATAGE_INPUT_FILE_TMP_DIR | Dossier temporaire où les fichiers d'entrée sont stockés | chemin absolu vers un dossier, par ex. /tmp/custom_dir | /tmp/frelatage |
| FRELATAGE_INPUT_MAX_LEN | Taille maximale d'une variable d'entrée en octets | 4 - infini | 4094 |
| FRELATAGE_MAX_THREADS | Nombre maximum de threads simultanés | 8 - infini | 8 |
| FRELATAGE_MAX_STAGES | Nombre maximum d'étapes pour une fonction fuzzée | 1 - infini | 1000000 |
| FRELATAGE_MAX_CYCLES_WITHOUT_NEW_PATHS | Nombre de cycles sans nouveaux chemins trouvés après lequel on passe à l'étape suivante | 10 - infini | 5000 |
| FRELATAGE_INPUT_DIR | Répertoire contenant les fichiers d'entrée initiaux. Doit être un chemin relatif (par rapport au fichier de fuzzing) | chemin relatif vers un dossier, par ex. ./in | ./in |
| FRELATAGE_DICTIONARY_DIR | Répertoire par défaut pour les dictionnaires. Doit être un chemin relatif (par rapport au fichier de fuzzing) | chemin relatif vers un dossier, par ex. ./dict | ./dict |
| FRELATAGE_DEBUG_MODE | Activer le mode débogage (afficher l'erreur en cas de crash de Frelatage) | 1 pour activer, 0 sinon | 1 |