
Plugin de IDA Pro para búsqueda basada en consultas dentro del binario, útil principalmente para la investigación de vulnerabilidades.
La herramienta VulFi (Vulnerability Finder) es un plugin para IDA Pro que puede usarse para ayudar durante la búsqueda de errores en binarios. Su objetivo principal es proporcionar una vista única con todas las referencias cruzadas a las funciones más interesantes (como strcpy, sprintf, system, etc.). En los casos en que se pueda usar un descompilador Hexrays, intentará descartar las llamadas a estas funciones que no sean interesantes desde una perspectiva de investigación de vulnerabilidades (piense en algo como strcpy(dst,"Hello World!")). Sin el descompilador, las reglas son mucho más simples (para no depender de la arquitectura) y, por lo tanto, solo descartan los casos más obvios.
Consulte Registro de cambios para obtener más información sobre las actualizaciones.
Coloque los archivos vulfi.py, vulfi_prototypes.json y vulfi_rules.json en la carpeta de plugins de IDA (cp vulfi* <CARPETA_DE_PLUGINS_DE_IDA>).
Antes de ejecutar VulFi, asegúrese de comprender bien el binario con el que trabaja. Intente identificar todas las funciones estándar (strcpy, memcpy, etc.) y asígneles el nombre correspondiente. El plugin no distingue entre mayúsculas y minúsculas, por lo que MEMCPY, Memcpy y memcpy son nombres válidos. Sin embargo, tenga en cuenta que la búsqueda de la función requiere una coincidencia exacta. Esto significa que memcpy? o std_memcpy (o cualquier otra variante) no se detectarán como una función estándar y, por lo tanto, no se considerarán al buscar posibles vulnerabilidades. Si está trabajando con un binario desconocido, primero debe configurar las opciones del compilador Options > Compiler. Después de eso, VulFi hará todo lo posible para filtrar todos los falsos positivos obvios (como una llamada a printf con una cadena constante como primer parámetro). Tenga en cuenta que, aunque el plugin está diseñado sin vínculos con una arquitectura específica, algunos procesadores no ofrecen soporte completo para especificar tipos y, en tal caso, VulFi simplemente marcará todas las referencias cruzadas a funciones potencialmente peligrosas para permitirle continuar con el análisis manual. En estos casos, puede beneficiarse de las funciones de seguimiento del plugin.
Para iniciar el escaneo, seleccione la opción Search > VulFi en el menú de la barra superior. Esto iniciará un nuevo escaneo o leerá los resultados anteriores almacenados en el archivo idb/i64. Los datos se guardan automáticamente cada vez que guarda la base de datos.
Una vez que se completa el escaneo o se cargan los resultados anteriores, se presentará una tabla con una vista que contiene las siguientes columnas:
Not Checked a cada nuevo elemento. Los otros estados son False Positive, Suspicious y Vulnerable. Se pueden establecer usando el menú contextual de clic derecho sobre un elemento dado y deben reflejar los resultados de la revisión manual de la llamada a la función en cuestión.High, Medium y Low (también Info para los casos en que el escáner no pudo identificar todos los parámetros correctamente). Las prioridades se definen junto con otras reglas en el archivo vulfi_rules.json.En caso de que no haya datos dentro del archivo idb/i64 o el usuario decida realizar un nuevo escaneo, el plugin preguntará si debe ejecutar el escaneo usando las reglas incluidas por defecto o si debe usar un archivo de reglas personalizado. Tenga en cuenta que ejecutar un nuevo escaneo con datos ya existentes no sobrescribe los elementos encontrados previamente identificados por la regla con el mismo nombre que los resultados almacenados anteriormente. Por lo tanto, volver a ejecutar el escaneo no elimina los comentarios ni las actualizaciones de estado existentes.

En el menú contextual de clic derecho dentro de la vista de VulFi, también puede eliminar el elemento de los resultados o eliminar todos los elementos. Tenga en cuenta que cualquier comentario o actualización de estado se perderá después de realizar esta operación.
A partir de la versión 2.1, VulFi también admite operaciones realizadas en varios elementos seleccionados a la vez. Esto permite marcar varios elementos con cierto estado, eliminarlos o agregar el mismo comentario a varias filas. A veces sucede que la operación no se actualiza correctamente. Cuando esto ocurra, actualice la interfaz de usuario (Ctrl+U) varias veces hasta que vea reflejados los cambios.
Cada vez que desee inspeccionar la instancia detectada de una posible función vulnerable, simplemente haga doble clic en cualquier lugar de la fila deseada e IDA lo llevará a la ubicación de memoria que se identificó como potencialmente interesante. Usando el clic derecho y la opción Set Vulfi Comment, puede ingresar un comentario para la instancia dada (por ejemplo, para justificar el estado).
El plugin también permite crear reglas personalizadas. Estas reglas se pueden definir en la interfaz de IDA (ideal para funciones individuales) o proporcionarse como un archivo de reglas personalizado (ideal para reglas que apuntan a cubrir múltiples funciones).
Cuando desee rastrear una función personalizada, identificada durante el análisis, haga clic derecho en cualquier lugar de su cuerpo y seleccione Add <name> function to VulFi. También puede resaltar y hacer clic derecho en un nombre de función dentro de la vista de desensamblaje/descompilador actual para evitar cambiar al cuerpo de la función.

También es posible cargar un archivo personalizado con un conjunto de múltiples reglas. Para crear un archivo de reglas personalizado con la estructura a continuación, puede usar el archivo de plantilla incluido aquí.
[ // Un array de reglas
{
"name": "NOMBRE DE LA REGLA", // El nombre de la regla
"function_names":[
"nombre_de_la_función_a_buscar" // Lista de todos los nombres de funciones que deben coincidir con las condiciones definidas en esta regla
],
"wrappers":true, // También buscar envoltorios de las funciones anteriores (tenga en cuenta que la función envuelta también debe coincidir con la regla)
"mark_if":{
"High":"True", // Si se evalúa como True, marcar con prioridad High (consulte Reglas a continuación)
"Medium":"False", // Si se evalúa como True, marcar con prioridad Medium (consulte Reglas a continuación)
"Low": "False" // Si se evalúa como True, marcar con prioridad Low (consulte Reglas a continuación)
}
}
]
A continuación se muestra un ejemplo de una regla que busca todas las referencias cruzadas a la función malloc y verifica si su parámetro no es constante y si se verifica el valor de retorno de la función:
{
"name": "Posible desreferencia de puntero nulo",
"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 - Marca todos los bucles. En tal caso, param[0] es el contador del bucle (si se detecta) y param[1] es la condición contra la cual se verifica el contador para terminar el bucle.Array Access - Marca todos los accesos a arrays. En este caso, param[0] es el array y param[1] es el índice.Se pueden usar las mismas funciones que para los parámetros de funciones normales (todo lo que se pueda llamar en param[<índice>]).
param[<índice>]: Se usa para acceder al parámetro de una llamada a función (el índice comienza en 0)function_call: Se usa para acceder al evento de llamada a funciónparam_count: Contiene la cantidad de parámetros que se pasaron a una funciónparam[<índice>].is_constant()param[<índice>].number_value()param[<índice>].string_value()param[<índice>].set_to_null_after_call()function_call.return_value_checked(<constante_a_verificar>)param[<índice>].used_in_call_<before|after>(["función1","función2"])function_call.reachable_from("<nombre_de_función>")param[<índice>].is_sign_compared()param[<índice>].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. Esta es una forma de evitar perder referencias cruzadas donde no fue posible obtener correctamente todos los parámetros (esto se aplica principalmente al modo de desensamblaje).Ctrl+U para actualizar manualmente la vista (varias veces).not param[1].is_constant() and not param[1].used_in_call_before(["strlen"])read: function_call.reachable_from("read")