Problemi di sicurezza per i driver di rete
Per una discussione generale sulla scrittura di driver sicuri, vedere Creazione di driver reliable Kernel-Mode.
Oltre alle procedure di codifica sicura e alle indicazioni generali sul driver di dispositivo, i driver di rete devono eseguire le operazioni seguenti per migliorare la sicurezza:
- Tutti i driver di rete devono convalidare i valori letti dal Registro di sistema. In particolare, il chiamante di NdisReadConfiguration o NdisReadNetworkAddress non deve effettuare ipotesi sui valori letti dal Registro di sistema e deve convalidare ogni valore del Registro di sistema letto. Se il chiamante di NdisReadConfiguration determina che un valore non è limitato, deve usare invece un valore predefinito. Se il chiamante NdisReadNetworkAddress determina che un valore non è limitato, deve usare l'indirizzo MAC (Permanent Medium Access Control) o un indirizzo predefinito.
Problemi specifici dell'OID
Un driver miniport, nella relativa funzione MiniportOidRequest o MiniportCoOidRequest , deve convalidare qualsiasi valore OID (Object Identifier) richiesto dal driver. Se il driver determina che il valore da impostare non è limitato, deve non riuscire la richiesta impostata. Per altre informazioni sugli identificatori di oggetto, vedere Ottenere e impostare le informazioni sul driver miniport e il supporto NDIS per WMI.
Se la funzione MiniportOidRequest di un driver intermedio non passa un'operazione impostata a un driver miniport sottostante, la funzione deve convalidare il valore OID. Per altre informazioni, vedere Query del driver intermedio e Operazioni di set.
Linee guida per la sicurezza di Query OID
La maggior parte degli OID query può essere rilasciata da qualsiasi applicazione usermode nel sistema. Seguire queste linee guida specifiche per gli ID query.
Convalidare sempre le dimensioni del buffer è sufficiente per l'output. Qualsiasi gestore OID di query senza un controllo delle dimensioni del buffer di output ha un bug di sicurezza.
if (oid->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG)) { oid->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG); return NDIS_STATUS_INVALID_LENGTH; }
Scrivere sempre un valore corretto e minimo in Byte Scritti. È un flag rosso da assegnare
oid->BytesWritten = oid->InformationBufferLength
come l'esempio seguente.// ALWAYS WRONG oid->DATA.QUERY_INFORMATION.BytesWritten = DATA.QUERY_INFORMATION.InformationBufferLength;
Il sistema operativo copia byte scritti in un'applicazione usermode. Se BytesScritto è maggiore del numero di byte effettivamente scritto dal driver, il sistema operativo potrebbe terminare la copia della memoria kernel non inizializzata in usermode, che sarebbe una vulnerabilità di divulgazione delle informazioni. Usare invece codice simile al seguente:
oid->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);
Non leggere mai i valori dal buffer. In alcuni casi, il buffer di output di un OID viene mappato direttamente in un processo usermode ostile. Il processo ostile può modificare il buffer di output dopo averlo scritto. Ad esempio, il codice seguente può essere attaccato, perché un utente malintenzionato può modificare NumElements dopo la scrittura:
output->NumElements = 4; for (i = 0 ; i < output->NumElements ; i++) { output->Element[i] = . . .; }
Per evitare la lettura dal buffer, mantenere una copia locale. Ad esempio, per correggere l'esempio precedente, introdurre una nuova variabile stack:
ULONG num = 4; output->NumElements = num; for (i = 0 ; i < num; i++) { output->Element[i] = . . .; }
Con questo approccio, il ciclo for legge nuovamente dalla variabile
num
stack del driver e non dal relativo buffer di output. Il driver deve anche contrassegnare il buffer di output con lavolatile
parola chiave, per impedire al compilatore di annullare automaticamente questa correzione.
Impostare le linee guida per la sicurezza OID
La maggior parte degli OID set può essere rilasciata da un'applicazione usermode in esecuzione nei gruppi di sicurezza Administrators o System. Anche se queste applicazioni sono generalmente attendibili, il driver miniport non deve comunque consentire il danneggiamento della memoria o l'inserimento del codice kernel. Seguire queste regole specifiche per Set OIDs:
Convalidare sempre l'input è abbastanza grande. Qualsiasi gestore del set OID senza un controllo delle dimensioni del buffer di input presenta una vulnerabilità di sicurezza.
if (oid->DATA.SET_INFORMATION.InformationBufferLength < sizeof(ULONG)) { return NDIS_STATUS_INVALID_LENGTH; }
Ogni volta che si convalida un OID con un offset incorporato, è necessario verificare che il buffer incorporato si trovi all'interno del payload OID. Ciò richiede diversi controlli. Ad esempio, OID_PM_ADD_WOL_PATTERN può fornire un modello incorporato, che deve essere controllato. La convalida corretta richiede il controllo:
InformationBufferSize >= sizeof(NDIS_PM_PACKET_PATTERN)
PmPattern = (PNDIS_PM_PACKET_PATTERN) InformationBuffer; if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) { Status = NDIS_STATUS_BUFFER_TOO_SHORT; *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); break; }
Pattern-PatternOffset + Pattern-PatternSize>> non sovraflow
ULONG TotalSize = 0; if (!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternSize, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }
Questi due controlli possono essere combinati usando codice come l'esempio seguente:
ULONG TotalSize = 0; if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN) || !NT_SUCCESS(RtlUlongAdd(Pattern->PatternSize, Pattern->PatternOffset, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }
InformationBuffer + Pattern-PatternOffset + Pattern-PatternLength>> non esegue il overflow
ULONG TotalSize = 0; if (!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternLength, &TotalSize) || (!NT_SUCCESS(RtlUlongAdd(TotalSize, InformationBuffer, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }
Pattern-PatternOffset + Pattern-PatternLength>>< = InformationBufferSize
ULONG TotalSize = 0; if(!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternLength, &TotalSize) || TotalSize > InformationBufferLength)) { return NDIS_STATUS_INVALID_LENGTH; }
Linee guida per la sicurezza del metodo OID
Gli OID del metodo possono essere rilasciati da un'applicazione usermode in esecuzione nei gruppi di sicurezza Administrators o System. Sono una combinazione di set e query, quindi entrambi gli elenchi precedenti di linee guida si applicano anche agli ID metodo.
Altri problemi di sicurezza dei driver di rete
Molti driver miniport NDIS espongono un dispositivo di controllo usando NdisRegisterDeviceEx. Queste operazioni devono controllare i gestori IOCTL, con tutte le stesse regole di sicurezza di un driver WDM. Per altre informazioni, vedere Problemi di sicurezza per i codici di controllo I/O.
I driver miniport NDIS ben progettati non devono basarsi sulla chiamata in un particolare contesto di processo, né interagire molto con usermode (con IOCTLs & OID che sono l'eccezione). Si tratta di un flag rosso per visualizzare un miniport che ha aperto handle usermode, eseguito attese utentemode o memoria allocata rispetto alla quota utentemode. Tale codice deve essere analizzato.
La maggior parte dei driver miniport NDIS non deve essere coinvolta nell'analisi dei payload dei pacchetti. In alcuni casi, tuttavia, potrebbe essere necessario. In tal caso, questo codice deve essere controllato con attenzione, poiché il driver analizza i dati da un'origine non attendibile.
Come è standard quando si alloca la memoria in modalità kernel, i driver NDIS devono usare i meccanismi di pool NX appropriati Opt-In. In WDK 8 e versioni successive, la
NdisAllocate*
famiglia di funzioni viene esplicitata correttamente.