Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Ogólne omówienie pisania bezpiecznych sterowników można znaleźć w temacie Creating Reliable Kernel-Mode Drivers (Tworzenie niezawodnych sterowników Kernel-Mode).
Oprócz następujących bezpiecznych praktyk kodowania i ogólnych wskazówek dotyczących sterowników urządzeń, sterowniki sieciowe powinny wykonać następujące czynności, aby zwiększyć bezpieczeństwo:
- Wszystkie sterowniki sieciowe powinny zweryfikować wartości odczytywane z rejestru. W szczególności obiekt wywołujący NdisReadConfiguration lub NdisReadNetworkAddress nie może podjąć żadnych założeń dotyczących wartości odczytanych z rejestru i musi zweryfikować każdą odczytywaną wartość rejestru. Jeśli obiekt wywołujący NdisReadConfiguration ustali, że wartość jest poza granicami, powinna zamiast tego użyć wartości domyślnej. Jeśli obiekt wywołujący NdisReadNetworkAddress stwierdzi, że wartość jest poza granicami, powinien użyć stałego adresu MAC lub adresu domyślnego.
Problemy specyficzne dla OID
Sterownik miniportu, w funkcjach MiniportOidRequest lub MiniportCoOidRequest , powinien zweryfikować dowolną wartość identyfikatora obiektu (OID), którą sterownik ma ustawić. Jeśli sterownik ustali, że wartość, która ma zostać ustawiona, jest poza granicami, powinna zakończyć się niepowodzeniem ustawionego żądania. Aby uzyskać więcej informacji na temat identyfikatorów obiektów, zobacz Uzyskiwanie i ustawianie informacji o sterownikach miniportu i obsługa NDIS dla usługi WMI.
Jeśli funkcja MiniportOidRequest sterownika pośredniego nie przekazuje operacji ustawienia do bazowego sterownika miniportu, funkcja powinna zweryfikować wartość identyfikatora OID. Aby uzyskać więcej informacji, zobacz Pośrednie zapytanie dotyczące sterownika i operacje zestawu.
Wytyczne dotyczące zabezpieczeń OID zapytań
Większość identyfikatorów OID zapytań może być wystawiana przez dowolną aplikację usermode w systemie. Postępuj zgodnie z tymi konkretnymi wytycznymi dotyczącymi identyfikatorów OID zapytań.
Zawsze sprawdź, czy rozmiar buforu jest wystarczająco duży dla danych wyjściowych. Każda procedura obsługi OID zapytania bez sprawdzania rozmiaru buforu wyjściowego zawiera usterkę zabezpieczeń.
if (oid->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG)) { oid->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG); return NDIS_STATUS_INVALID_LENGTH; }Zawsze zapisuj poprawną i minimalną wartość do BytesWritten. Przypisanie
oid->BytesWritten = oid->InformationBufferLengthw taki sposób, jak w poniższym przykładzie, to czerwona flaga.// ALWAYS WRONG oid->DATA.QUERY_INFORMATION.BytesWritten = DATA.QUERY_INFORMATION.InformationBufferLength;System operacyjny skopiuje bajty BytesWritten z powrotem do aplikacji w trybie użytkownika. Jeśli BytesWritten jest większy niż liczba bajtów, które faktycznie zapisał sterownik, system operacyjny może skopiować niezainicjowaną pamięć jądra do trybu użytkownika, co byłoby luką w zabezpieczeniach umożliwiającą ujawnienie informacji. Zamiast tego użyj kodu podobnego do następującego:
oid->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);Nigdy nie odczytaj wartości z powrotem z buforu. W niektórych przypadkach bufor wyjściowy OID jest bezpośrednio mapowany do wrogiego procesu w trybie użytkownika. Wrogi proces może zmienić bufor wyjściowy po zapisaniu go. Na przykład poniższy kod może zostać zaatakowany, ponieważ osoba atakująca może zmienić NumElements po jego zapisaniu:
output->NumElements = 4; for (i = 0 ; i < output->NumElements ; i++) { output->Element[i] = . . .; }Aby uniknąć odczytu z buforu, zachowaj kopię lokalną. Aby na przykład rozwiązać powyższy przykład, wprowadź nową zmienną stosu:
ULONG num = 4; output->NumElements = num; for (i = 0 ; i < num; i++) { output->Element[i] = . . .; }Dzięki temu podejściu pętla for odczytuje z powrotem ze zmiennej stosu sterownika
num, a nie z jego buforu wyjściowego. Sterownik powinien również oznaczyć bufor wyjściowy za pomocą słowa kluczowegovolatile, aby uniemożliwić kompilatorowi dyskretne cofnięcie tej poprawki.
Ustawianie wytycznych dotyczących zabezpieczeń OID
Większość zestawów identyfikatorów OID może być wystawiana przez aplikację działającą w trybie użytkownika, znajdującą się w grupach zabezpieczeń Administratorzy lub System. Mimo że są to ogólnie zaufane aplikacje, sterownik miniportu nadal nie może zezwalać na uszkodzenie pamięci ani iniekcję kodu jądra. Postępuj zgodnie z tymi specyficznymi regułami dla ustawiania identyfikatorów OID:
Zawsze sprawdź, czy dane wejściowe są wystarczająco duże. Każda procedura obsługi zestawu identyfikatorów OID bez sprawdzania rozmiaru buforu wejściowego ma lukę w zabezpieczeniach.
if (oid->DATA.SET_INFORMATION.InformationBufferLength < sizeof(ULONG)) { return NDIS_STATUS_INVALID_LENGTH; }Za każdym razem, gdy weryfikujesz OID z osadzonym przesunięciem, musisz sprawdzić, czy osadzony bufor znajduje się w danych OID. Wymaga to kilku kontroli. Na przykład OID_PM_ADD_WOL_PATTERN może dostarczyć osadzony wzorzec, który należy sprawdzić. Poprawna walidacja wymaga sprawdzenia:
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 nie powoduje przepełnienia
ULONG TotalSize = 0; if (!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternSize, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }Te dwa testy można połączyć przy użyciu kodu, takiego jak w poniższym przykładzie:
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 nie powoduje przepełnienia
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; }
Wytyczne dotyczące zabezpieczeń OID metody
Identyfikatory OID metod mogą być wystawiane przez aplikację działającą w trybie użytkownika w kontekście zabezpieczeń grup Administratorzy lub System. Są one kombinacją zestawu i zapytania, więc obie wcześniejsze listy wskazówek mają również zastosowanie do identyfikatorów metod OID.
Inne problemy z zabezpieczeniami sterowników sieciowych
Wiele sterowników miniportów NDIS udostępnia urządzenie sterujące za pomocą NdisRegisterDeviceEx. Te, które to robią, muszą przeprowadzać inspekcję swoich procedur obsługi IOCTL przy użyciu wszystkich tych samych reguł zabezpieczeń co sterownik WDM. Aby uzyskać więcej informacji, zobacz Security Issues for I/O Control Codes (Problemy z zabezpieczeniami kodów kontroli we/wy).
Dobrze zaprojektowane sterowniki miniportu NDIS nie powinny polegać na ich wywoływaniu w określonym kontekście procesu ani zbyt blisko współdziałać z trybem użytkownika (wyjątkiem są IOCTL i OID). Byłby to sygnał ostrzegawczy, gdyby miniport otwierał uchwyty w trybie użytkownika, wykonywał oczekiwania w trybie użytkownika lub przydzielał pamięć względem limitu w trybie użytkownika. Należy zbadać ten kod.
Większość sterowników miniportów NDIS nie powinna być zaangażowana w analizowanie ładunków pakietów. Jednak w niektórych przypadkach może to być konieczne. Jeśli tak, ten kod powinien być bardzo uważnie poddany inspekcji, ponieważ sterownik analizuje dane z niezaufanego źródła.
Podobnie jak w przypadku przydzielania pamięci w trybie jądra, sterowniki NDIS powinny używać odpowiednich mechanizmów puli NX Opt-In. W zestawie WDK 8 i nowszych rodzina funkcji
NdisAllocate*jest prawidłowo skonfigurowana.