Caricamento protetto delle librerie per impedire il precaricamento di attacchi DLL

Il supporto per Windows Vista Service Pack 1 (SP1) termina il 12 luglio 2011. Per continuare a ricevere gli aggiornamenti della sicurezza per Windows, assicurati di eseguire Windows Vista con Service Pack 2 (SP2). Per altre informazioni, vedi questa pagina Web Microsoft: il supporto per alcune versioni di Windows sta per terminare.

Quando un'applicazione carica dinamicamente una DLL (Dynamic Link Library) senza specificare un percorso completo, Windows tenta di individuare la DLL cercando un set ben definito di directory. Se un utente malintenzionato ottiene il controllo di una delle directory, può forzare l'applicazione a caricare una copia dannosa della DLL invece della DLL prevista. Questi attacchi sono noti come "attacchi di precaricamento DLL" e sono comuni a tutti i sistemi operativi che supportano il caricamento dinamico delle librerie DLL condivise. L'effetto di tali attacchi potrebbe essere che un utente malintenzionato può eseguire codice nel contesto dell'utente che esegue l'applicazione. Quando l'applicazione viene eseguita come amministratore, questo potrebbe portare a un elevazione locale dei privilegi. Sappiamo del rinnovato interesse per questi attacchi. Per limitare l'effetto di questo problema sui nostri clienti comuni, stiamo rilasciando questo documento alla community di sviluppatori per assicurarci che siano a conoscenza del problema e abbiano gli strumenti necessari per risolvere il problema nelle loro applicazioni.

Riepilogo

Descrizione degli attacchi di precaricamento DLL

Attacchi basati su LoadLibrary

Quando un'applicazione carica dinamicamente una DLL senza specificare un percorso completo, Windows tenta di individuare questa DLL mediante una ricerca lineare in un set ben definito di directory, noto come DLL Search Order. Se Windows individua la DLL all'interno dell'ordine di ricerca DLL, la DLL verrà caricata. Tuttavia, se la DLL non viene individuata in alcuna directory nell'ordine di ricerca DLL, verrà restituito un errore all'operazione di caricamento DLL. Di seguito è riportato l'ordine di ricerca DLL per le funzioni LoadLibrary e LoadLibraryEx , utilizzate per caricare dinamicamente le DLL:

  1. Directory da cui è stata caricata l'applicazione
  2. Directory di sistema
  3. Directory di sistema a 16 bit
  4. Directory Di Windows
  5. Directory di lavoro corrente (CWD)
  6. Directory elencate nella variabile di ambiente PATH

                
Si consideri lo scenario seguente:

  • Un'applicazione carica una DLL senza specificare un percorso completo che si prevede di trovare in CWD dell'applicazione.
  • L'applicazione è completamente preparata per gestire il caso quando non trova la DLL.
  • L'autore dell'attacco conosce queste informazioni sull'applicazione e controlla il CWD.
  • L'utente malintenzionato copia la propria versione appositamente predisposto della DLL in CWD. Ciò presuppone che l'aggressore abbia l'autorizzazione per farlo.
  • Windows esegue la ricerca nelle directory nell'ordine di ricerca DLL e trova la DLL in CWD dell'applicazione.

In questo scenario, la DLL appositamente predisposto viene eseguita all'interno dell'applicazione e ottiene i privilegi dell'utente corrente.

Consiglio

Per evitare questo attacco, le applicazioni possono rimuovere la directory di lavoro corrente (CWD) dal percorso di ricerca DLL chiamando l'API SetDllDirectory utilizzando una stringa vuota (""). Se un'applicazione dipende dal caricamento di una DLL dalla directory corrente, ottenere la directory di lavoro corrente e utilizzarla per passare un percorso completo di LoadLibrary.

Siamo anche consapevoli che alcuni sviluppatori utilizzano LoadLibrary per verificare se una DLL specifica è presente al fine di determinare quale versione di Windows è in esecuzione dall'utente. Tenere presente che ciò potrebbe rendere l'applicazione vulnerabile. Se la libreria interessata in effetti non esiste nella versione di Windows su cui viene eseguita l'applicazione, un utente malintenzionato potrebbe introdurre una libreria con lo stesso nome in CWD. È consigliabile evitare di usare questa tecnica. Usare invece le tecniche consigliate descritte nell'articolo MSDN "Getting the System Version".

Un'applicazione che carica plug-in di terze parti e che non può forzare i plug-in a utilizzare un percorso qualificato per le chiamate LoadLibrary deve chiamare SetDllDirectory("") per rimuovere CWD e quindi chiamare SetDllDirectory("percorso di installazione plug-in") per aggiungere la directory di installazione del plug-in al percorso di ricerca DLL.

Attacchi basati su SearchPath

Un attacco simile si verifica quando un'applicazione utilizza l'API SearchPath per individuare una DLL e caricare dinamicamente il percorso restituito da SearchPath. Di seguito è riportato l'ordine di ricerca predefinito per l'API SearchPath:

  • Directory da cui è stata caricata l'applicazione
  • Directory di lavoro corrente (CWD)
  • Directory di sistema
  • Directory di sistema a 16 bit
  • Directory Di Windows
  • Directory elencate nella variabile di ambiente PATH

Questo modello non è consigliato perché non è sicuro. Non è consigliabile usare la funzione SearchPath come metodo per l'individuazione di un file di .dll se l'uso previsto dell'output è in una chiamata alla funzione LoadLibrary. Questo può causare l'individuazione del file di .dll errato perché l'ordine di ricerca della funzione SearchPath è diverso dall'ordine di ricerca usato dalla funzione LoadLibrary. Se è necessario individuare e caricare un file .dll, usare la funzione LoadLibrary.

ShellExecute e CreateProcess

Le varianti di questi problemi possono verificarsi anche quando gli sviluppatori chiamano funzioni simili come ShellExecute e CreateProcess per caricare i file eseguibili esterni. Consigliamo agli sviluppatori di prestare attenzione durante il caricamento dei file binari e di specificare il percorso completo. Questa operazione dovrebbe comportare una minore complessità quando si carica un file binario anziché una raccolta.

È consigliabile che gli sviluppatori esercino le operazioni seguenti:

  • Convalidare le applicazioni per le istanze di caricamenti di libreria non protetti (esempi di ognuno sono illustrati più avanti in questo articolo). Ecco alcuni esempi:

    • Uso di SearchPath per identificare la posizione di una raccolta o di un componente.
    • Utilizzo di LoadLibrary per identificare la versione del sistema operativo.
  • Usare i percorsi completi per tutte le chiamate a LoadLibrary, CreateProcess e ShellExecute, dove è possibile.

  • Implementare chiamate a SetDllDirectory con una stringa vuota ("") per rimuovere la directory di lavoro corrente dall'ordine di ricerca DLL predefinito dove è necessario. Tenere presente che SetDllDirectory influisce sull'intero processo. Pertanto, è necessario eseguire questa operazione una volta prima dell'inizializzazione del processo, non prima e dopo le chiamate a LoadLibrary. Poiché SetDllDirectory influisce sull'intero processo, più thread che chiamano SetDllDirectory con valori diversi potrebbero causare un comportamento non definito. Inoltre, se il processo è progettato per caricare DLL di terze parti, il test sarà necessario per determinare se l'esecuzione di un'impostazione a livello di processo causerà incompatibilità. Un problema noto è che quando un'applicazione dipende da Visual Basic, Applications Edition, un'impostazione a livello di processo può causare incompatibilità.

  • Usare la funzione SetSearchPathMode per abilitare la modalità di ricerca del processo sicuro per il processo. La directory di lavoro corrente viene spostata nell'ultima posizione dell'elenco di ricerca di SearchPath per tutta la durata del processo.

  • Evitare di utilizzare SearchPath per verificare l'esistenza di una DLL senza specificare un percorso completo, anche se la modalità di ricerca sicura è abilitata, perché questo può comunque causare attacchi di precaricamento DLL.

Indicazioni sull'identificazione dei caricamenti di raccolte non sicure

Nel codice sorgente, di seguito sono riportati esempi di caricamenti di libreria non sicuri:

  • Nell'esempio di codice seguente l'applicazione cerca "schannel.dll" usando il percorso di ricerca meno sicuro. Se un utente malintenzionato può inserire schannel.dll in CWD, verrà caricato anche prima che l'applicazione cerchi la libreria appropriata nelle directory di Windows.

    DWORD retval = SearchPath(NULL, "schannel", ".dll", err, result, NULL); 
    HMODULE handle = LoadLibrary(result);
    
  • Nell'esempio di codice seguente l'applicazione tenta di caricare la libreria dai vari percorsi dell'applicazione e del sistema operativo descritti all'inizio di questo documento per la chiamata LoadLibrary(). Se c'è il rischio che il file non sia presente, l'applicazione potrebbe provare a caricare il file dalla directory di lavoro corrente. Questo scenario è leggermente meno pericoloso dell'esempio precedente. Tuttavia, espone ancora l'utente dell'applicazione al rischio se l'ambiente non è del tutto prevedibile.

    HMODULE handle = LoadLibrary("schannel.dll");
    

                
                
Di seguito sono riportati esempi di caricamenti di libreria migliori e più sicuri:

  • Nell'esempio di codice seguente la libreria viene caricata direttamente usando un percorso completo. Non c'è alcun rischio che l'autore dell'attacco introduca codice dannoso a meno che non abbia già le autorizzazioni di scrittura per la directory di destinazione dell'applicazione.

    HMODULE handle = LoadLibrary("c:\\windows\\system32\\schannel.dll");
    

    Nota Per informazioni su come determinare la directory di sistema, vedere le risorse seguenti:

    GetSystemDirectory
    http://msdn.microsoft.com/en-us/library/ms724373%28VS.85%29.aspx SHGetKnownFolderPath
    http://msdn.microsoft.com/en-us/library/bb762188%28v=VS.85%29.aspx

  • Nell'esempio di codice seguente, la directory di lavoro corrente viene rimossa dal percorso di ricerca prima di chiamare LoadLibrary. Questo riduce significativamente il rischio, in quanto l'utente malintenzionato dovrebbe controllare sia la directory dell'applicazione, la directory di Windows, o eventuali directory che sono specificati nel percorso dell'utente al fine di utilizzare un attacco di precaricamento DLL.

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
  • In tutti i sistemi in cui è installato l'aggiornamento della sicurezza 963027 (descritto in MS09-014), il codice seguente sposterebbe definitivamente CWD nell'ultima posizione dell'ordine di ricerca. Le chiamate successive alla funzione SetSearchPathMode dall'interno di tale processo che tentano di modificare la modalità di ricerca non riusciranno.

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
  • Nell'esempio di codice seguente, la directory di lavoro corrente viene rimossa dal percorso di ricerca prima di chiamare LoadLibrary. Questo riduce in modo significativo il rischio, in quanto l'autore dell'attacco dovrebbe controllare sia la directory dell'applicazione, la directory di Windows, o qualsiasi directory che sono specificati nel percorso dell'utente al fine di utilizzare un attacco di precaricamento DLL.

    SetSearchPathMode (BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT );
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
    

Utilizzo di Process Monitor per rilevare dinamicamente i carichi non sicuri

Microsoft pubblica uno strumento denominato Process Monitor. Questo strumento consente agli sviluppatori e agli amministratori di tenere traccia da vicino del comportamento di un processo in esecuzione. Process Monitor può essere usato per rilevare in modo dinamico se una delle applicazioni potrebbe essere vulnerabile a questo tipo di problema.

  • Per scaricare Process Monitor, visita la seguente pagina Web Microsoft:
    http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

  • Provare ad avviare l'applicazione utilizzando CWD impostato su una directory specifica. Ad esempio, fare doppio clic su un file con estensione il cui gestore file è assegnato all'applicazione.

  • Configurare Process Monitor con i filtri seguenti:

    371495f2-14de-f99c-c55a-f75d31fe9ca8

  • Se viene colpito un percorso vulnerabile, vedrai un valore simile al seguente: 9acdd1ae-29b9-e499-9de9-8bc665b95e76

     La chiamata alla condivisione file remota per caricare una DLL indica che si tratta di un programma vulnerabile.

Altre informazioni

Per ulteriori informazioni, visita le pagine Web Microsoft seguenti:

Ordine di ricerca libreria a collegamento dinamico

http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx Documentazione MSDN sulla funzione SearchPath

http://msdn.microsoft.com/en-us/library/aa365527 (VS.85).aspx Documentazione MSDN sulla funzione LoadLibrary

http://msdn.microsoft.com/en-us/library/ms684175 (VS.85).aspx Documentazione MSDN sulla funzione SetDllDirectory

http://msdn.microsoft.com/en-us/library/ms686203 (VS.85).aspx Documentazione MSDN sulla funzione SetSearchPathMode

http://msdn.microsoft.com/en-us/library/dd266735 (VS.85).aspx Post di blog di David Leblanc, Principal Security Engineer con Microsoft Office

http://blogs.msdn.com/b/david_leblanc/archive/2008/02/20/dll-preloading-attacks.aspx Post di blog di Andrew Roths, team di progettazione MSRC sugli attacchi di precaricamento DLL

http://blogs.technet.com/b/srd/archive/2009/04/14/ms09-014-addressing-the-safari-carpet-bomb-vulnerability.aspx

Altre risorse