
Plugin per IDA Pro per la ricerca basata su query all'interno del binario, utile principalmente per la ricerca di vulnerabilità.
Lo strumento VulFi (Vulnerability Finder) è un plugin per IDA Pro che può essere utilizzato per assistere durante la caccia ai bug nei binari. Il suo obiettivo principale è fornire una singola vista con tutti i cross-reference alle funzioni più interessanti (come strcpy, sprintf, system, ecc.). Per i casi in cui è disponibile un decompilatore Hexrays, tenterà di escludere le chiamate a queste funzioni che non sono interessanti dal punto di vista della ricerca di vulnerabilità (pensa a qualcosa come strcpy(dst,"Hello World!")). Senza il decompilatore, le regole sono molto più semplici (per non dipendere dall'architettura) e quindi escludono solo i casi più ovvi.
Consulta il Changelog per maggiori informazioni sugli aggiornamenti.
Posiziona i file vulfi.py, vulfi_prototypes.json e vulfi_rules.json nella cartella dei plugin di IDA (cp vulfi* <IDA_PLUGIN_FOLDER>).
Prima di eseguire VulFi, assicurati di avere una buona comprensione del binario con cui lavori. Cerca di identificare tutte le funzioni standard (strcpy, memcpy, ecc.) e nominale di conseguenza. Il plugin non fa distinzione tra maiuscole e minuscole, quindi MEMCPY, Memcpy e memcpy sono tutti nomi validi. Tuttavia, nota che la ricerca della funzione richiede una corrispondenza esatta. Ciò significa che memcpy? o std_memcpy (o qualsiasi altra variante) non verranno rilevati come funzione standard e quindi non saranno considerati nella ricerca di potenziali vulnerabilità. Se stai lavorando con un binario sconosciuto, devi prima impostare le opzioni del compilatore Options > Compiler. Dopodiché VulFi farà del suo meglio per filtrare tutti i falsi positivi evidenti (come una chiamata a printf con una stringa costante come primo parametro). Tieni presente che, sebbene il plugin sia realizzato senza legami con un'architettura specifica, alcuni processori non supportano completamente la specifica dei tipi e in tal caso VulFi segnerà semplicemente tutti i cross-reference alle funzioni standard potenzialmente pericolose per consentirti di procedere con l'analisi manuale. In questi casi, puoi beneficiare delle funzionalità di tracciamento del plugin.
Per avviare la scansione, seleziona l'opzione Search > VulFi dal menu della barra superiore. Questo avvierà una nuova scansione o leggerà i risultati precedenti memorizzati all'interno del file idb/i64. I dati vengono salvati automaticamente ogni volta che salvi il database.
Una volta completata la scansione o caricati i risultati precedenti, verrà presentata una tabella con una vista contenente le seguenti colonne:
Not Checked. Gli altri stati sono False Positive, Suspicious e Vulnerable. Possono essere impostati utilizzando il menu contestuale del tasto destro su un dato elemento e dovrebbero riflettere i risultati della revisione manuale della chiamata di funzione data.High, Medium e Low (anche Info per i casi in cui lo scanner non è stato in grado di identificare correttamente tutti i parametri). Le priorità sono definite insieme ad altre regole nel file vulfi_rules.json.Nel caso in cui non ci siano dati all'interno del file idb/i64 o l'utente decida di effettuare una nuova scansione. Il plugin chiederà se eseguire la scansione utilizzando le regole predefinite incluse o se utilizzare un file di regole personalizzato. Tieni presente che l'esecuzione di una nuova scansione con dati già esistenti non sovrascrive gli elementi precedentemente trovati identificati dalla regola con lo stesso nome di quella dei risultati precedentemente memorizzati. Pertanto, eseguire nuovamente la scansione non elimina i commenti esistenti e gli aggiornamenti di stato.

Nel menu contestuale del tasto destro all'interno della vista VulFi, puoi anche rimuovere l'elemento dai risultati o rimuovere tutti gli elementi. Tieni presente che eventuali commenti o aggiornamenti di stato andranno persi dopo aver eseguito questa operazione.
A partire dalla versione 2.1, VulFi supporta anche operazioni eseguite su più elementi selezionati contemporaneamente. Ciò consente di contrassegnare più elementi con un certo stato, eliminarli o aggiungere lo stesso commento a più righe. A volte capita che l'operazione non si aggiorni correttamente. Quando ciò accade, aggiorna l'interfaccia utente (Ctrl+U) un paio di volte finché non vedi i cambiamenti riflessi.
Ogni volta che desideri ispezionare l'istanza rilevata di una possibile funzione vulnerabile, fai semplicemente doppio clic in un punto qualsiasi della riga desiderata e IDA ti porterà alla posizione di memoria identificata come potenzialmente interessante. Utilizzando il tasto destro e l'opzione Set Vulfi Comment puoi inserire un commento per l'istanza data (per giustificare lo stato, ad esempio).
Il plugin consente anche di creare regole personalizzate. Queste regole possono essere definite nell'interfaccia IDA (ideali per funzioni singole) o fornite come file di regole personalizzato (ideali per regole che mirano a coprire più funzioni).
Quando desideri tracciare una funzione personalizzata, identificata durante l'analisi, fai clic con il tasto destro in un punto qualsiasi del suo corpo e seleziona Add <name> function to VulFi. Puoi anche evidenziare e fare clic con il tasto destro su un nome di funzione nella vista corrente di disassemblaggio/decompilazione per evitare di passare al corpo della funzione.

È anche possibile caricare un file personalizzato con un set di più regole. Per creare un file di regole personalizzato con la struttura seguente, puoi utilizzare il file modello incluso qui.
[ // Un array di regole
{
"name": "NOME REGOLA", // Il nome della regola
"function_names":[
"nome_funzione_da_cercare" // Elenco di tutti i nomi di funzione che devono essere confrontati con le condizioni definite in questa regola
],
"wrappers":true, // Cerca anche i wrapper delle funzioni sopra (nota che la funzione wrappata deve anch'essa corrispondere alla regola)
"mark_if":{
"High":"True", // Se restituisce True, contrassegna con priorità High (vedi Regole sotto)
"Medium":"False", // Se restituisce True, contrassegna con priorità Medium (vedi Regole sotto)
"Low": "False" // Se restituisce True, contrassegna con priorità Low (vedi Regole sotto)
}
}
]
Un esempio di regola che cerca tutti i cross-reference alla funzione malloc e verifica se il suo parametro non è costante e se il valore di ritorno della funzione viene controllato è mostrato di seguito:
{
"name": "Possibile Dereferenziazione di Puntatore Nullo",
"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 - Contrassegna tutti i loop. In tal caso param[0] è il contatore del loop (se rilevato) e param[1] è la condizione rispetto alla quale viene controllato il contatore per terminare il loop.Array Access - Contrassegna tutti gli accessi agli array. In questo caso param[0] è l'array e param[1] è l'indice.Le stesse funzioni possono essere utilizzate durante la creazione delle regole come per i normali parametri di funzione (qualsiasi cosa che può essere chiamata su param[<indice>]).
param[<indice>]: Utilizzato per accedere al parametro di una chiamata di funzione (l'indice parte da 0)function_call: Utilizzato per accedere all'evento di chiamata di funzioneparam_count: Contiene il numero di parametri passati a una funzioneparam[<indice>].is_constant()param[<indice>].number_value()param[<indice>].string_value()param[<indice>].set_to_null_after_call()function_call.return_value_checked(<costante_da_controllare>)param[<indice>].used_in_call_<before|after>(["funzione1","funzione2"])function_call.reachable_from("<nome_funzione>")param[<indice>].is_sign_compared()param[<indice>].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. Questo è un modo per evitare di perdere cross-reference in cui non è stato possibile ottenere correttamente tutti i parametri (questo vale principalmente per la modalità disassemblaggio).Ctrl+U per aggiornare manualmente la vista (un paio di volte).not param[1].is_constant() and not param[1].used_in_call_before(["strlen"])read: function_call.reachable_from("read")