DeviceIoControl

0

Metoda przekazywania bufora zalezy od najnizszych 3 bitow kodu ioctl.
Czy da sie jakos zabezpieczyc swoj driver (i w konsekwencji system) przed atakiem DoS?

chce umozliwoc kazdemu klientowi dostep do drivera ktory robi bardzo prosta rzecz, w zasadzie nawet nie kolejkuje irp tylko konczy je z dispatchXXX.
Uzywa do tego celu method_neither, bo co innego jest po prostu zbedne bo skoro brak kolejni (IoCompleteRequest() z dispatch) irp to zawsze bede w kontexcie procesu ktory wykonal ioctl.
Wszsytyko ok, ale ja nie chce by ktos przekazal mi wielki bufor i jeszcze dal kod na method_buffered, tylko po to, bym w swoim driverze go odrzucil. Nie ma to sensu zeby iomanager kopiowal mi gigabajty danych tylko po to, zebym zaraz je zwolnil i zwrocil blad. Chcialbym zeby jesli user przekaze cos innego niz ja chce w IOCTL.TransferType to zeby io manager zwrocil mu jakis blad bez kopiowania niczego.

0

Niestety w przypadku metod METHOD_BUFFERED oraz METHOD_IN_DIRECT/METHOD_OUT_DIRECT nie masz wpływu na zachowanie menadżera I/O. Jeśli użytkownik przekaże niepusty parametr InputBufferLength i wybierze jedną z tych metod menadżer usiłuje zaalokować blok pamięci o wskazanej długości z puli niestronicowanej. Jeśli rozmiar bufora będzie zbyt duży w kodzie funkcji IopXxxControlFile zostanie wygenerowany wyjątek, przez co twój sterownik nawet nie dowie się o próbie przekazania takiego żadania I/O.

Naturalnie procedura przetwarzająca żadania I/O powinna dokładnie sprawdzać przekazywane parametry bez względu na to czy pochodzą one z kontekstu zaufanego procesu czy nie. Żeby skutecznie zabespieczyć sterownik wpierw powinieneś zadbać o poprawną walidację przekazywanych parametrów a następnie dodatkowo ograniczyć dostęp do tworzonego urządzenia. Można zrobić to na wiele sposobów oraz na wielu poziomach. Najprostszy a zarazem najniższy poziom bezpieczeństwa zapewnia przekazanie TRUE dla parametru Exclusive podczas tworzenia urządzenia. Dzięki temu tylko jeden proces może uzyskać uchwyt reprezentujący to urządzenie. Niestety jeśli proces (twój process), który pozyskał uchwyt nie jest zabezpieczony, to inny proces może zduplikować uchwyt urządzenia i bez problemu z niego korzystać. Aby temu zapobiec można w procedurze przetwarzającej żądania I/O sprawdzać czy adres obiektu procesu jest zgodny z adresem zachowanym podczas żadania IRP_MJ_CREATE. Warunek jest jeden - twój program musi jako pierwszy uzyskać uchwyt do tworzonego urządzenia.
Kolejna metoda polega na manipulacji deskryptorem bezpieczeństwa związanym z tworzonym urządzeniem. W celu utworzenie urządzenie można skorzystać z funkcji IoCreateDeviceSecure zamiast IoCreateDevice. W parametrze DefaultSDDLString należy przekazać predefiniowany string określający kto (jaki użytkownik) może uzyskać dostęp do tworzonego urządzenia. Nic nie stoi na przeszkodzie by stworzyć zupełnie nowy deskryptor, zamiast korzystać ze standardowych rozwiązań.

lord_zero

0

aha no dzieki, czyli nie da sie.
wiem ze moge uzyc exclusive, ale raz nie mam gwarancji ze akurat 'moj' program bedzie pierwszy a dwa ten sterownik jest dla wielu programow na raz.

a jesli cos zduplikuje handle, to znaczy ze ma dostep (process_dup_handle) do przestrzeni adresowej procesu z ktorego go duplikuje. Zazwyczaj bedzie i tak mial DebugPrivilege/TakeOwnershipPrivilege, bo zwykly user nie otworzy procesu admina ze wzgledu na brak wpisu w DACL. Wiec po co sprawdzac w driverze z jakiego procesu przychodzi request, jak moze on wqrzucic mi zdalny watek.

chozi mi bardziej o optymalizacje: nie chce by dispatchXXX dostawala metody direct/buffered, lecz tylko wylacznie neither. Nie po to implementuje neither, by celowo odrzucac buffered i i tak cierpiec przez bezcelowe kopiowanie danych w te i nazad.

0

Teoretycznie menadżer I/O mógłby wiedzieć z jakiej metody korzystasz gdybyś ustawił odopwiednią flagę w obiekcie urządzenia (w twoim przypadku nie ustawiał nic, bo chyba standardowo jest tak, że jeśli nie ustawisz flagi DO_BUFFERED_IO lub DO_DIRECT_IO to teoretycznie korzystasz z DO_NEITHER_IO (nie ma zdefiniowanej takiej flagi)). Niestety funkcja IopXxxControlFile nie korzysta z tej cennej informacji. Z drugiej jednak strony nie jest powiedziane, że twój driver będzie korzystał tylko z jednaj metody. Zazwyczaj jest tak, że do przesyłania dużych bloków używasz NEITHER lub DIRECT a do mniejszych BUFFERED. Tu jest fragment funkcji IopXxxControlFile, która nie sprawdza w żaden sposób czy zwrócony adres puli jest prawidłowy, czy nie, zamiast tego kod opakowany jest w __try/__except i generuje wyjątek gdy funkcja usiłuje skopiować coś pod NULL'a. Zatem durny użytkownik może generować żądania I/O przekazując kosmiczne rozmiary buforów a menadżer I/O i tak je zaakceptuje usiłując przydzielić zasób z góry nie możliwy do przydzielenia.

case METHOD_BUFFERED:

        //
        // For this case, allocate a buffer that is large enough to contain
        // both the input and the output buffers.  Copy the input buffer to
        // the allocated buffer and set the appropriate IRP fields.
        //

        irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID) NULL;

        try {

            if (InputBufferLength || OutputBufferLength) {
                irp->AssociatedIrp.SystemBuffer =
                    ExAllocatePoolWithQuota( poolType,
                                             (InputBufferLength > OutputBufferLength) ? InputBufferLength : OutputBufferLength );

                if (ARGUMENT_PRESENT( InputBuffer )) {
                    RtlCopyMemory( irp->AssociatedIrp.SystemBuffer,
                                   InputBuffer,
                                   InputBufferLength );
                }
                irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
                irp->UserBuffer = OutputBuffer;
                if (ARGUMENT_PRESENT( OutputBuffer )) {
                    irp->Flags |= IRP_INPUT_OPERATION;
                }
            } else {
                irp->Flags = 0;
                irp->UserBuffer = (PVOID) NULL;
            }

        } except(EXCEPTION_EXECUTE_HANDLER) {

            //
            // An exception was incurred while either allocating the
            // the system buffer or moving the caller's data.  Determine
            // what actually happened, cleanup accordingly, and return
            // an appropriate error status code.
            //

            IopExceptionCleanup( fileObject,
                                 irp,
                                 eventObject,
                                 (PKEVENT) NULL );

            return GetExceptionCode();
        }

        break;

lord_zero

0

IO manager kozysta z flag DO_BUFFERED i DO_DIRECT ale tylko przy irm_mj_read/write.
Przy ioctl bylo by to mozliwe, ale direct io to method_in_direct i method_out_direct. roznica polega na tym, ze in_direct nie wymaga praw zapisu do bufora out (device->mdladdress).
ale jesli 1 device_object ma obslugiwac kilka typow IO, to nie bedzie mozliwe. Dokumentacja mowi ze wlasnie ioctl ignoruje te flagi zeby typ buforowania nie byl z gory narzucony na kazdy irp.
Aczkolwiek struktura irp powinna miec flage jak buforowac io.

Zatem durny użytkownik może generować żądania I/O przekazując kosmiczne rozmiary buforów a menadżer I/O i tak je zaakceptuje usiłując przydzielić zasób z góry nie możliwy do przydzielenia.

ale moze przekazac kosmiczny bufor o takiej wielkosci aze ssytem jednak znajdzie pamiec i go przekazac.
ktos pusci milion irp_mj_device_control na sekunde podajac 5 kilobajtow bufora i wiekszasc non-paged pool'a idzie w zapomneinie. Gdy akurat trafie na przypadek ze io manager zaalokowal prawie caly nonpaed pool na method_buffered, ale jeszcze go nie zwolnil, a ja bede probowal zaalokowac pamiec na costam, to bede mial problem i atak sie uda bo bede musial zapewne zakonczyc swoj program/przerwac ladowanie drivera, lub nawet dostane bugchecka jesli uzyje NonPagedPoolMustSucceed/NonPagedPoolCacheAlignedMustS. Nie wiem co znaczy 'startup only', ale wydaje mi sie ze predzej czy pozniej system zaladuje moj driver przed innym ktory kozysta z ExAllocatePool z *MustSucceed.

0

[quote]IO manager kozysta z flag DO_BUFFERED i DO_DIRECT ale tylko przy irm_mj_read/write.[/quote]
Powiem szczerze, że nie pamiętałem o tym. Skoro tak jest, to czemu nie skorzystasz z MJ_READ/WRITE zamiast MJ_DEVICE_CONTROL?

Pytanie - dlaczego ktoś miałby robić takie głupoty, zresztą sprawdź jak się zachowa system jeśli zasypiesz menadżer I/O tego typu żądaniami, w których podany rozmiar bufora jest wystarczająco mały przez co może zostać przydzielony z puli (sam jestem ciekaw). Nie wydaje mi się by tak łatwo można było rzucić system na kolana, nawet jeśli wykonasz przedstawiony przez siebie scenariusz. Zakładanie też, że będziesz potrzebował nie wiadomo jak dużego obszaru puli niestronicowanej jest lekko naciągane. Powinno się unikać korzystania z puli niestronicowanej kiedy jest to tylko możliwe.

lord_zero

0

Powinno się unikać korzystania z puli niestronicowanej kiedy jest to tylko możliwe.

method_buffered - jest koneczne. z zalozenia bufor ma byc dostepny w irql => dispatch_level.

test bylby bardzo prosty, NtQuerySystemInformation z PSYSTEM_PROCESS_INFORMATION zwraca ilosc nonpaged poola, wiec wystarczy podac te ilosc i jest duza szansa ze przez ilestam milisekund system bedzie mial 0 pamieci rezydentnej. Co jesli przez te kilka milisekund jakis driver bedzie chcial sie zaladowac? Drivery laduja czesc swojego obrazu do nonpaged pool, wiec latwo sie domyslec ze bylby to DoS - nic by sie nie zaladowalo.
Jakby teraz puscic w petli DeviceIoControl (a raczej NtDeviceIoControlFile by bylo szybciej) z method_buffered - system praktycznie lezy i kwiczy. A jesli driver jeszcze obsluguje wszsytkie 4 metody?

1 użytkowników online, w tym zalogowanych: 0, gości: 1