Modello di sicurezza di Windows per sviluppatori di driver
Il modello di sicurezza di Windows si basa su oggetti a protezione diretta. Ogni componente del sistema operativo deve garantire la sicurezza degli oggetti per cui è responsabile. I driver, pertanto, devono salvaguardare la sicurezza dei dispositivi e degli oggetti dispositivo.
Questo argomento riepiloga il modo in cui il modello di sicurezza di Windows si applica ai driver in modalità kernel.
Modello di sicurezza di Windows
Il modello di sicurezza di Windows si basa principalmente sui diritti per oggetto, con un numero ridotto di privilegi a livello di sistema. Gli oggetti che possono essere protetti includono, ad esempio processi, thread, eventi e altri oggetti di sincronizzazione, nonché file, directory e dispositivi.
Per ogni tipo di oggetto, i diritti di lettura, scrittura ed esecuzione generici eseguono il mapping in diritti dettagliati specifici dell'oggetto. Ad esempio, per file e directory, i diritti possibili includono il diritto di leggere o scrivere il file o la directory, il diritto di leggere o scrivere attributi di file estesi, il diritto di attraversare una directory e il diritto di scrivere il descrittore di sicurezza di un oggetto.
Il modello di sicurezza prevede i concetti seguenti:
- Identificatori di sicurezza (SID)
- Token di accesso
- Descrittori di sicurezza
- Elenchi di controllo di accesso (ACL)
- Privilegi
Identificatori di sicurezza (SID)
Un identificatore di sicurezza (SID, detto anche entità) identifica un utente, un gruppo o una sessione di accesso. Ogni utente ha un SID univoco, recuperato dal sistema operativo all'accesso.
I SID vengono emessi da un'autorità, ad esempio il sistema operativo o un server di dominio. Alcuni SID sono noti e hanno nomi e identificatori. Ad esempio, il SID S-1-1-0 identifica Everyone (o World).
Token di accesso
Ogni processo ha un token di accesso. Il token di accesso descrive il contesto di sicurezza completo del processo. Contiene il SID dell'utente, il SID dei gruppi a cui appartiene l'utente e il SID della sessione di accesso, nonché un elenco dei privilegi a livello di sistema concessi all'utente.
Per impostazione predefinita, il sistema usa il token di accesso primario per un processo ogni volta che un thread del processo interagisce con un oggetto a protezione diretta. Tuttavia, un thread può rappresentare un account client. Quando un thread rappresenta, ha un token di rappresentazione oltre al proprio token primario. Il token di rappresentazione descrive il contesto di sicurezza dell'account utente rappresentato dal thread. La rappresentazione è particolarmente comune nella gestione rpc (Remote Procedure Call).
Un token di accesso che descrive un contesto di sicurezza limitato per un thread o un processo è denominato token con restrizioni. I SID in un token con restrizioni possono essere impostati solo per negare l'accesso, non consentire l'accesso, agli oggetti a protezione diretta. Inoltre, il token può descrivere un set limitato di privilegi a livello di sistema. Il SID e l'identità dell'utente rimangono invariati, ma i diritti di accesso dell'utente sono limitati mentre il processo usa il token con restrizioni. La funzione CreateRestrictedToken crea un token con restrizioni.
Descrittori di sicurezza
Ogni oggetto Windows denominato ha un descrittore di sicurezza; anche alcuni oggetti senza nome. Il descrittore di sicurezza descrive i SID del proprietario e del gruppo per l'oggetto insieme ai relativi ACL.
Il descrittore di sicurezza di un oggetto viene in genere creato dalla funzione che crea l'oggetto. Quando un driver chiama la routine IoCreateDevice o IoCreateDeviceSecure per creare un oggetto dispositivo, il sistema applica un descrittore di sicurezza all'oggetto dispositivo creato e imposta gli ACL per l'oggetto. Per la maggior parte dei dispositivi, gli elenchi di controllo di accesso vengono specificati nel file INF (Device Information).
Per altre informazioni sui descrittori di sicurezza nella documentazione del driver del kernel.
Elenchi di controllo di accesso
Controllo di accesso Elenchi (ACL) consentono di controllare con granularità fine l'accesso agli oggetti. Un elenco di controllo di accesso fa parte del descrittore di sicurezza per ogni oggetto.
Ogni ACL contiene zero o più voci Controllo di accesso (ACE). Ogni ACE, a sua volta, contiene un singolo SID che identifica un utente, un gruppo o un computer e un elenco di diritti negati o consentiti per tale SID.
ACL per gli oggetti dispositivo
L'ACL per un oggetto dispositivo può essere impostato in uno dei tre modi seguenti:
- Impostare nel descrittore di sicurezza predefinito per il tipo di dispositivo.
- Creato a livello di codice dalla funzione RtlCreateSecurityDescriptor e impostato dalla funzione RtlSetDaclSecurityDescriptor .
- Specificato in Security Descriptor Definition Language (SDDL) nel file INF del dispositivo o in una chiamata alla routine IoCreateDeviceSecure .
Tutti i driver devono usare SDDL nel file INF per specificare gli elenchi di controllo di accesso per gli oggetti dispositivo.
SDDL è un linguaggio di descrizione estendibile che consente ai componenti di creare elenchi di controllo di accesso in un formato stringa. SDDL viene usato sia dal codice in modalità utente che dal codice in modalità kernel. La figura seguente illustra il formato delle stringhe SDDL per gli oggetti dispositivo.
Il valore access specifica il tipo di accesso consentito. Il valore SID specifica un identificatore di sicurezza che determina a chi si applica il valore di Access, ad esempio un utente o un gruppo.
Ad esempio, la stringa SDDL seguente consente al sistema (SY) di accedere a tutti gli elementi e consente a tutti gli altri (WD) solo l'accesso in lettura:
“D:P(A;;GA;;;SY)(A;;GR;;;WD)”
Il file di intestazione wdmsec.h include anche un set di stringhe SDDL predefinite adatte agli oggetti dispositivo. Ad esempio, il file di intestazione definisce SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX come indicato di seguito:
"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GRGWGX;;;RC)"
Il primo segmento di questa stringa consente il controllo completo del kernel e del sistema operativo (SY) sul dispositivo. Il secondo segmento consente a chiunque nel gruppo Amministrazione istrators (BA) predefinito di accedere all'intero dispositivo, ma non di modificare l'ACL. Il terzo segmento consente a tutti (WD) di leggere o scrivere nel dispositivo e il quarto segmento concede gli stessi diritti al codice non attendibile (RC). I driver possono usare le stringhe predefinite così come sono o come modelli per stringhe specifiche dell'oggetto dispositivo.
Tutti gli oggetti dispositivo in uno stack devono avere gli stessi elenchi di controllo di accesso. La modifica degli elenchi di controllo di accesso in un oggetto dispositivo nello stack modifica gli elenchi di controllo di accesso nell'intero stack di dispositivi.
Tuttavia, l'aggiunta di un nuovo oggetto dispositivo allo stack non modifica gli ACL, quelli del nuovo oggetto dispositivo (se contiene ACL) o quelli di qualsiasi oggetto dispositivo esistente nello stack. Quando un driver crea un nuovo oggetto dispositivo e lo collega all'inizio dello stack, il driver deve copiare gli elenchi di controllo di accesso per lo stack nel nuovo oggetto dispositivo copiando il campo DeviceObject.Characteristics dal driver inferiore successivo.
La routine IoCreateDeviceSecure supporta un subset di stringhe SDDL che usano SID predefiniti, ad esempio WD e SY. Le API in modalità utente e i file INF supportano la sintassi SDDL completa.
Controlli di sicurezza che usano elenchi di controllo di accesso
Quando un processo richiede l'accesso a un oggetto, i controlli di sicurezza confrontano gli ACL per l'oggetto con i SID nel token di accesso del chiamante.
Il sistema confronta gli ACL in un ordine dall'alto verso il basso e si arresta sulla prima corrispondenza pertinente. Pertanto, quando si crea un ACL, è consigliabile inserire sempre gli ACL negati sopra gli ACL di concessione corrispondenti. Negli esempi seguenti viene illustrato come procede il confronto.
Esempio 1: Confronto di un ACL con un token di accesso
Nell'esempio 1 viene illustrato come il sistema confronta un ACL con il token di accesso per il processo di un chiamante. Si supponga che il chiamante voglia aprire un file con l'elenco di controllo di accesso illustrato nella tabella seguente.
ACL di file di esempio
Autorizzazione | SID | Accesso |
---|---|---|
Consenti | Contabilità | Scrivere, eliminare |
Consenti | Vendite | Aggiunta |
Nega | Note legali | Accodamento, scrittura, eliminazione |
Consenti | Tutti | Lettura |
Questo ACL ha quattro ACL, che si applicano in modo specifico ai gruppi Accounting, Sales, Legal e Everyone.
Si supponga quindi che il token di accesso per il processo di richiesta contenga i SID per un utente e tre gruppi, nell'ordine seguente:
Utente Jim (S-1-5-21...)
Contabilità gruppo (S-1-5-22...)
Legale gruppo (S-1-5-23...)
Raggruppa tutti (S-1-1-0)
Quando si confronta un ACL di file con un token di accesso, il sistema cerca prima un ace per l'utente Jim nell'ACL del file. Non viene visualizzato nessuno, quindi successivamente cerca un ACE per il gruppo Accounting. Come illustrato nella tabella precedente, un ace per il gruppo Accounting viene visualizzato come prima voce nell'ACL del file, quindi al processo di Jim viene concesso il diritto di scrivere o eliminare il file e il confronto si arresta. Se l'ACE per il gruppo Legale precede invece l'ACE per il gruppo Accounting nell'ACL, il processo verrà negato scrittura, accodamento ed eliminazione dell'accesso al file.
Esempio 2: Confronto di un ACL con un token con restrizioni
Il sistema confronta un ACL con un token con restrizioni nello stesso modo in cui confronta quelli in un token che non è limitato. Tuttavia, un SID denial in un token con restrizioni può corrispondere solo a Deny ACE in un ACL.
Nell'esempio 2 viene illustrato come il sistema confronta l'ACL di un file con un token con restrizioni. Si supponga che il file abbia lo stesso ACL illustrato nella tabella precedente. In questo esempio, tuttavia, il processo ha un token con restrizioni che contiene i SID seguenti:
Utente Jim (S-1-5-21...) Negare
Contabilità gruppo (S-1-5-22...) Negare
Legale gruppo (S-1-5-23...) Negare
Raggruppa tutti (S-1-1-0)
L'ACL del file non elenca il SID di Jim, quindi il sistema procede al SID del gruppo Accounting. Anche se l'ACL del file ha un ACE per il gruppo Accounting, questa ACE consente l'accesso; pertanto, non corrisponde al SID nel token con restrizioni del processo, che nega l'accesso. Di conseguenza, il sistema procede al SID del gruppo legale. L'ACL per il file contiene un ace per il gruppo Legale che nega l'accesso, pertanto il processo non può scrivere, aggiungere o eliminare il file.
Privilegi
Un privilegio è il diritto per un utente di eseguire un'operazione correlata al sistema nel computer locale, ad esempio il caricamento di un driver, la modifica dell'ora o l'arresto del sistema.
I privilegi sono diversi dai diritti di accesso perché si applicano alle attività e alle risorse correlate al sistema anziché agli oggetti e perché vengono assegnati a un utente o a un gruppo da un amministratore di sistema, anziché dal sistema operativo.
Il token di accesso per ogni processo contiene un elenco dei privilegi concessi al processo. I privilegi devono essere abilitati in modo specifico prima dell'uso. Per altre informazioni sui privilegi, vedere Privilegi nella documentazione del driver del kernel.
Scenario del modello di sicurezza di Windows: Creazione di un file
Il sistema usa i costrutti di sicurezza descritti nel modello di sicurezza di Windows ogni volta che un processo crea un handle per un file o un oggetto.
Il diagramma seguente illustra le azioni correlate alla sicurezza attivate quando un processo in modalità utente tenta di creare un file.
Il diagramma precedente mostra come il sistema risponde quando un'applicazione in modalità utente chiama la funzione CreateFile . Le note seguenti fanno riferimento ai numeri cerchiati nella figura:
- Un'applicazione in modalità utente chiama la funzione CreateFile , passando un nome di file Microsoft Win32 valido.
- La modalità utente Kernel32.dll passa la richiesta a Ntdll.dll, che converte il nome Win32 in un nome file di Microsoft Windows NT.
- Ntdll.dll chiama la funzione NtCreateFile con il nome file di Windows. All'interno di Ntoskrnl.exe, Gestione I/O gestisce NtCreateFile.
- Gestione I/O raggruppa nuovamente la richiesta in una chiamata di Gestione oggetti.
- Gestione oggetti risolve i collegamenti simbolici e garantisce che l'utente disponga dei diritti di attraversamento per il percorso in cui verrà creato il file. Per altre informazioni, vedere Controlli di sicurezza in Gestione oggetti.
- Gestione oggetti chiama il componente di sistema proprietario del tipo di oggetto sottostante associato alla richiesta. Per una richiesta di creazione di file, questo componente è l'I/O Manager, proprietario di oggetti dispositivo.
- Gestione I/O controlla il descrittore di sicurezza per l'oggetto dispositivo rispetto al token di accesso per il processo dell'utente per assicurarsi che l'utente abbia l'accesso necessario al dispositivo. Per altre informazioni, vedere Controlli di sicurezza in Gestione I/O.
- Se il processo utente ha l'accesso necessario, Gestione I/O crea un handle e invia una richiesta di IRP_MJ_CREATE al driver per il dispositivo o il file system.
- Il driver esegue controlli di sicurezza aggiuntivi in base alle esigenze. Ad esempio, se la richiesta specifica un oggetto nello spazio dei nomi del dispositivo, il driver deve assicurarsi che il chiamante disponga dei diritti di accesso necessari. Per altre informazioni, vedere Controlli di sicurezza nel driver.
Controlli di sicurezza in Gestione oggetti
La responsabilità di controllare i diritti di accesso appartiene al componente di livello più alto in grado di eseguire tali controlli. Se Gestione oggetti può verificare i diritti di accesso del chiamante, lo fa. In caso contrario, Gestione oggetti passa la richiesta al componente responsabile del tipo di oggetto sottostante. Tale componente, a sua volta, verifica l'accesso, se possibile; se non riesce, passa la richiesta a un componente ancora inferiore, ad esempio un driver.
Gestione oggetti controlla gli elenchi di controllo di accesso per tipi di oggetto semplici, ad esempio eventi e blocchi mutex. Per gli oggetti con uno spazio dei nomi, il proprietario del tipo esegue i controlli di sicurezza. Ad esempio, Gestione I/O viene considerato il proprietario del tipo per oggetti dispositivo e oggetti file. Se Gestione oggetti trova il nome di un oggetto dispositivo o di un oggetto file durante l'analisi di un nome, passa il nome a Gestione I/O, come nello scenario di creazione di file presentato in precedenza. Gestione I/O controlla quindi i diritti di accesso, se possibile. Se il nome specifica un oggetto all'interno di uno spazio dei nomi del dispositivo, Gestione I/O disattiva il nome al driver del dispositivo (o del file system) e tale driver è responsabile della convalida dell'accesso richiesto.
Controlli di sicurezza in Gestione I/O
Quando gestione I/O crea un handle, controlla i diritti dell'oggetto rispetto al token di accesso del processo e quindi archivia i diritti concessi all'utente insieme all'handle. Quando arrivano richieste di I/O successive, Gestione I/O controlla i diritti associati all'handle per assicurarsi che il processo abbia il diritto di eseguire l'operazione di I/O richiesta. Ad esempio, se il processo richiede in seguito un'operazione di scrittura, Gestione I/O controlla i diritti associati all'handle per assicurarsi che il chiamante disponga dell'accesso in scrittura all'oggetto.
Se l'handle è duplicato, i diritti possono essere rimossi dalla copia, ma non aggiunti.
Quando gestione I/O crea un oggetto, converte le modalità di accesso Win32 generiche in diritti specifici dell'oggetto. Ad esempio, i diritti seguenti si applicano a file e directory:
Modalità di accesso Win32 | Diritti specifici dell'oggetto |
---|---|
GENERIC_READ | Readdata |
GENERIC_WRITE | WriteData |
GENERIC_EXECUTE | ReadAttributes |
GENERIC_ALL | Tutte le date |
Per creare un file, un processo deve avere diritti di attraversamento per le directory padre nel percorso di destinazione. Ad esempio, per creare \Device\CDROM0\Directory\File.txt, un processo deve avere il diritto di attraversare \Device, \Device\CDROM0 e \Device\CDROM0\Directory. Gestione I/O controlla solo i diritti di attraversamento per queste directory.
Gestione I/O controlla i diritti di attraversamento quando analizza il nome del file. Se il nome del file è un collegamento simbolico, Gestione I/O lo risolve in un percorso completo e quindi controlla i diritti di attraversamento, a partire dalla radice. Si supponga, ad esempio, che il collegamento simbolico \DosDevices\D sia mappato al nome del dispositivo Windows NT \Device\CDROM0. Il processo deve avere diritti di attraversamento per la directory \Device.
Per altre informazioni, vedere Handle di oggetti e sicurezza degli oggetti.
Controlli di sicurezza nel driver
Il kernel del sistema operativo considera ogni driver, in effetti, come un file system con il proprio spazio dei nomi. Di conseguenza, quando un chiamante tenta di creare un oggetto nello spazio dei nomi del dispositivo, Gestione I/O verifica che il processo abbia diritti di attraversamento per le directory nel percorso.
Con i driver WDM, Gestione I/O non esegue controlli di sicurezza sullo spazio dei nomi, a meno che l'oggetto dispositivo non sia stato creato specificando FILE_DEVICE_edizione Standard CURE_OPEN. Quando FILE_DEVICE_edizione Standard CURE_OPEN non è impostato, il driver è responsabile della sicurezza dello spazio dei nomi. Per altre informazioni, vedere Controllo dell'accesso dello spazio dei nomi dei dispositivi e Protezione degli oggetti dispositivo.
Per i driver WDF, il flag di FILE_DEVICE_edizione Standard CURE_OPEN viene sempre impostato, in modo che sia presente un controllo del descrittore di sicurezza del dispositivo prima di consentire a un'applicazione di accedere a qualsiasi nome all'interno dello spazio dei nomi del dispositivo. Per altre informazioni, vedere Controllo dell'accesso ai dispositivi nei driver KMDF.
Limiti di sicurezza di Windows
I driver che comunicano tra loro e con i chiamanti in modalità utente di diversi livelli di privilegio possono essere considerati attraversare un limite di attendibilità. Un limite di attendibilità è qualsiasi percorso di esecuzione del codice che attraversa un processo con privilegi inferiore in un processo con privilegi più elevati.
Maggiore è la disparità nei livelli di privilegio, più interessante è il limite per gli utenti malintenzionati che vogliono eseguire attacchi come un attacco di escalation dei privilegi contro il driver o il processo mirato.
Parte del processo di creazione di un modello di minaccia consiste nell'esaminare i limiti di sicurezza e cercare percorsi imprevisti. Per altre informazioni, vedere Modellazione delle minacce per i driver.
Tutti i dati che superano un limite di attendibilità non sono attendibili e devono essere convalidati.
Questo diagramma mostra tre driver del kernel e due app, una in un contenitore di app e un'app eseguita con diritti di amministratore. Le linee rosse indicano limiti di attendibilità di esempio.
Poiché il contenitore dell'app può fornire vincoli aggiuntivi e non è in esecuzione a livello di amministratore, il percorso (1) è un percorso di rischio più elevato per un attacco di escalation perché il limite di attendibilità è tra un contenitore di app (un processo con privilegi molto limitati) e un driver kernel.
Il percorso (2) è un percorso di rischio inferiore, perché l'app viene eseguita con diritti di amministratore e chiama direttamente nel driver del kernel. Amministrazione è già un privilegio piuttosto elevato sul sistema, quindi la superficie di attacco dall'amministratore al kernel è meno di un obiettivo interessante per gli utenti malintenzionati, ma ancora un degno di nota limite di attendibilità.
Path (3) è un esempio di percorso di esecuzione del codice che supera più limiti di attendibilità che potrebbero non essere superati se non viene creato un modello di minaccia. In questo esempio esiste un limite di attendibilità tra il driver 1 e il driver 3, perché il driver 1 accetta l'input dall'app in modalità utente e lo passa direttamente al driver 3.
Tutti gli input provenienti dal driver dalla modalità utente non sono attendibili e devono essere convalidati. Gli input provenienti da altri driver potrebbero anche non essere attendibili a seconda che il driver precedente fosse solo un semplice pass-through (ad esempio i dati ricevuti dal driver 1 dall'app 1 , il driver 1 non ha eseguito alcuna convalida sui dati e ne è stato appena passato al driver 3). Assicurarsi di identificare tutte le superfici di attacco e i limiti di attendibilità e convalidare tutti i dati che li attraversano, creando un modello di minaccia completo.
Sicurezza di Windows modello Consigli
- Impostare elenchi di controllo di accesso predefiniti sicuri nelle chiamate alla routine IoCreateDeviceSecure .
- Specificare gli elenchi di controllo di accesso nel file INF per ogni dispositivo. Questi ACL possono allentare gli ACL predefiniti stretti, se necessario.
- Impostare la caratteristica FILE_DEVICE_edizione Standard CURE_OPEN per applicare le impostazioni di sicurezza degli oggetti dispositivo allo spazio dei nomi del dispositivo.
- Non definire IOCTLs che consentono di FILE_ANY_ACCESS a meno che tale accesso non possa essere sfruttato in modo dannoso.
- Usare la routine IoValidateDeviceIoControlAccess per rafforzare la sicurezza su IOCTLS esistenti che consentono FILE_ANY_ACCESS.
- Creare un modello di minaccia per esaminare i limiti di sicurezza e cercare percorsi imprevisti. Per altre informazioni, vedere Modellazione delle minacce per i driver.
- Per altre raccomandazioni sulla sicurezza dei driver, vedere Elenco di controllo per la sicurezza dei driver.