
Plugin IDA Pro pour la recherche basée sur des requêtes dans le binaire, utile principalement pour la recherche de vulnérabilités.
L'outil VulFi (Vulnerability Finder) est un plugin pour IDA Pro qui peut être utilisé pour aider lors de la chasse aux bugs dans les binaires. Son objectif principal est de fournir une vue unique avec toutes les références croisées vers les fonctions les plus intéressantes (telles que strcpy, sprintf, system, etc.). Pour les cas où un décompilateur Hexrays peut être utilisé, il tente d'écarter les appels à ces fonctions qui ne sont pas intéressants du point de vue de la recherche de vulnérabilités (pensez à quelque chose comme strcpy(dst,"Hello World!")). Sans le décompilateur, les règles sont beaucoup plus simples (pour ne pas dépendre de l'architecture) et n'écartent donc que les cas les plus évidents.
Voir Changelog pour plus d'informations sur les mises à jour.
Placez les fichiers vulfi.py, vulfi_prototypes.json et vulfi_rules.json dans le dossier des plugins IDA (cp vulfi* <IDA_PLUGIN_FOLDER>).
Avant d'exécuter VulFi, assurez-vous de bien comprendre le binaire avec lequel vous travaillez. Essayez d'identifier toutes les fonctions standard (strcpy, memcpy, etc.) et nommez-les en conséquence. Le plugin est insensible à la casse, donc MEMCPY, Memcpy et memcpy sont tous des noms valides. Cependant, notez que la recherche de la fonction nécessite une correspondance exacte. Cela signifie que memcpy? ou std_memcpy (ou toute autre variante) ne seront pas détectés comme une fonction standard et ne seront donc pas pris en compte lors de la recherche de vulnérabilités potentielles. Si vous travaillez avec un binaire inconnu, vous devez d'abord définir les options du compilateur Options > Compiler. Ensuite, VulFi fera de son mieux pour filtrer tous les faux positifs évidents (comme un appel à printf avec une chaîne constante comme premier paramètre). Veuillez noter que même si le plugin est conçu sans lien avec une architecture spécifique, certains processeurs ne prennent pas entièrement en charge la spécification des types et dans ce cas, VulFi marquera simplement toutes les références croisées aux fonctions standard potentiellement dangereuses pour vous permettre de procéder à une analyse manuelle. Dans ces cas, vous pouvez bénéficier des fonctionnalités de suivi du plugin.
Pour lancer l'analyse, sélectionnez l'option Search > VulFi dans la barre de menu supérieure. Cela lancera soit une nouvelle analyse, soit lira les résultats précédents stockés dans le fichier idb/i64. Les données sont automatiquement sauvegardées chaque fois que vous sauvegardez la base de données.
Une fois l'analyse terminée ou une fois les résultats précédents chargés, un tableau sera présenté avec une vue contenant les colonnes suivantes :
Not Checked est attribué à chaque nouvel élément. Les autres statuts sont False Positive, Suspicious et Vulnerable. Ceux-ci peuvent être définis à l'aide d'un menu contextuel (clic droit) sur un élément donné et doivent refléter les résultats de la révision manuelle de l'appel de fonction donné.High, Medium et Low (également Info pour les cas où le scanner n'a pas pu identifier correctement tous les paramètres). Les priorités sont définies avec d'autres règles dans le fichier vulfi_rules.json.Dans le cas où il n'y a pas de données dans le fichier idb/i64 ou si l'utilisateur décide d'effectuer une nouvelle analyse, le plugin demandera s'il doit exécuter l'analyse en utilisant les règles incluses par défaut ou s'il doit utiliser un fichier de règles personnalisé. Veuillez noter que lancer une nouvelle analyse avec des données déjà existantes n'écrase pas les éléments précédemment trouvés identifiés par la règle portant le même nom que celui des résultats précédemment stockés. Par conséquent, relancer l'analyse ne supprime pas les commentaires existants ni les mises à jour de statut.

Dans le menu contextuel du clic droit dans la vue VulFi, vous pouvez également supprimer l'élément des résultats ou supprimer tous les éléments. Veuillez noter que tous les commentaires ou mises à jour de statut seront perdus après cette opération.
Depuis la version 2.1, VulFi prend également en charge les opérations effectuées sur plusieurs éléments sélectionnés à la fois. Cela permet de marquer plusieurs éléments avec un certain statut, de les supprimer ou d'ajouter le même commentaire à plusieurs lignes. Il arrive parfois que l'opération ne se rafraîchisse pas correctement. Lorsque cela se produit, rafraîchissez l'interface utilisateur (Ctrl+U) plusieurs fois jusqu'à ce que vous voyiez les changements reflétés.
Lorsque vous souhaitez inspecter l'instance détectée d'une fonction potentiellement vulnérable, double-cliquez simplement n'importe où dans la ligne souhaitée et IDA vous amènera à l'emplacement mémoire identifié comme potentiellement intéressant. En utilisant le clic droit et l'option Set Vulfi Comment, vous pouvez saisir un commentaire pour l'instance donnée (pour justifier le statut par exemple).
Le plugin permet également de créer des règles personnalisées. Ces règles peuvent être définies dans l'interface IDA (idéal pour des fonctions uniques) ou fournies sous la forme d'un fichier de règles personnalisé (idéal pour des règles visant à couvrir plusieurs fonctions).
Lorsque vous souhaitez suivre une fonction personnalisée, identifiée au cours de l'analyse, faites un clic droit n'importe où dans son corps et sélectionnez Add <name> function to VulFi. Vous pouvez également surligner et faire un clic droit sur un nom de fonction dans la vue de désassemblage/décompilation actuelle pour éviter de passer dans le corps de la fonction.

Il est également possible de charger un fichier personnalisé avec un ensemble de plusieurs règles. Pour créer un fichier de règles personnalisé avec la structure ci-dessous, vous pouvez utiliser le fichier modèle inclus ici.
[ // Un tableau de règles
{
"name": "NOM DE LA RÈGLE", // Le nom de la règle
"function_names":[
"nom_de_la_fonction_à_rechercher" // Liste de tous les noms de fonctions qui doivent correspondre aux conditions définies dans cette règle
],
"wrappers":true, // Rechercher également les wrappers des fonctions ci-dessus (notez que la fonction wrappée doit également correspondre à la règle)
"mark_if":{
"High":"True", // Si évalue à True, marquer avec priorité High (voir Règles ci-dessous)
"Medium":"False", // Si évalue à True, marquer avec priorité Medium
"Low": "False" // Si évalue à True, marquer avec priorité Low
}
}
]
Un exemple de règle qui recherche toutes les références croisées à la fonction malloc et vérifie si son paramètre n'est pas constant et si la valeur de retour de la fonction est vérifiée est présenté ci-dessous :
{
"name": "Possible Null Pointer Dereference",
"function_names":[
"malloc"
],
"wrappers":false,
"mark_if":{
"High":"not param[0].is_constant() and not function_call.return_value_checked()",
"Medium":"False",
"Low": "False"
}
}
Loop Check - Marque toutes les boucles. Dans ce cas, param[0] est le compteur de la boucle (si détecté) et param[1] est la condition par rapport à laquelle le compteur est vérifié pour terminer la boucle.Array Access - Marque tous les accès aux tableaux. Dans ce cas, param[0] est le tableau et param[1] est l'index.Les mêmes fonctions peuvent être utilisées lors de la création de règles que pour les paramètres de fonction normaux (tout ce qui peut être appelé sur param[<index>]).
param[<index>] : Utilisé pour accéder au paramètre d'un appel de fonction (l'index commence à 0)function_call : Utilisé pour accéder à l'événement d'appel de fonctionparam_count : Contient le nombre de paramètres qui ont été passés à une fonctionparam[<index>].is_constant()param[<index>].number_value()param[<index>].string_value()param[<index>].set_to_null_after_call()function_call.return_value_checked(<constant_to_check>)param[<index>].used_in_call_<before|after>(["function1","function2"])function_call.reachable_from("<function_name>")param[<index>].is_sign_compared()param[<index>].used_as_index()param[2].number_value() > 5"%s" in param[1].string_value()not param[1].is_constant()function_call.return_value_checked(param_count)function_call.return_value_checked()all(not p.is_constant() for p in param[2:])any(p.is_constant() for p in param)Truestrlen : Low. C'est une façon d'éviter de manquer des références croisées lorsqu'il n'a pas été possible d'obtenir correctement tous les paramètres (cela s'applique principalement au mode de désassemblage).Ctrl+U pour rafraîchir manuellement la vue (plusieurs fois).not param[1].is_constant() and not param[1].used_in_call_before(["strlen"])read : function_call.reachable_from("read")