Sdílet prostřednictvím


Osvědčené postupy pro omezení vysokého privilegovaného chování v ovladačích režimu jádra

Toto téma shrnuje nebezpečné vývojové vzory, které můžou vést ke zneužití a zneužití kódu ovladače jádra Windows. Toto téma obsahuje doporučení pro vývoj a ukázky kódu, které pomáhají omezit privilegované chování. Následující osvědčené postupy vám pomůžou zlepšit bezpečnost výkonu privilegovaného chování v jádru Windows.

Přehled nebezpečného chování ovladačů

Přestože se očekává, že ovladače systému Windows budou provádět privilegované operace v režimu jádra, neprovádění kontrol zabezpečení a nepřidávání omezení na privilegované chování je nepřijatelné. Program WHCP (Windows Hardware Compatibility Program), dříve WHQL, vyžaduje, aby nové odeslání ovladačů splňovalo tento požadavek.

Mezi příklady nezabezpečeného a nebezpečného chování patří:

Poskytování možnosti čtení a zápisu msrs

Zvýšení zabezpečení čtení z msrs

V tomto příkladu ReadMsr ovladač umožňuje nebezpečné chování tím, že umožňuje libovolný a všechny registry libovolně číst pomocí vnitřního registru specifického pro model __readmsr. To může vést ke zneužití škodlivými procesy v uživatelském režimu.

Func ReadMsr(int dwMsrIdx) 
{
	int value = __readmsr(dwMsrIdx); // Unsafe, can read from any MSR
	return value;
}

Pokud váš scénář vyžaduje čtení z msRs, ovladač musí vždy zkontrolovat, jestli je registr, ze kterého se má číst, omezený na očekávaný index nebo rozsah. Dva příklady implementace bezpečné operace čtení následují.

Func ConstrainedReadMsr(int dwMsrIdx) 
{
    int value = 0;
    if (dwMsrIdx == expected_index) // Blocks from reading anything
    {
        value = __readmsr(dwMsrIdx); // Can only read the expected MSR
    }
    else
    {
        return error;
    }
    return value;
}

// OR

Func ConstrainedReadMsr(int dwMsrIdx) 
{
    int value = 0;
    if (min_range <= dwMsrIdx <= max_range) // Blocks from reading anything
    {
        value = __readmsr(dwMsrIdx); // Can only from the expected range of MSRs
    }
    else
    {
        return error;
    }
    return value;
}

Zvýšení zabezpečení zápisu do msRS

V prvním příkladu WriteMsr umožňuje ovladač nebezpečné chování tím, že dovoluje libovolné a nekontrolované zapisování do všech registrů. To může vést ke zneužití škodlivých procesů ke zvýšení oprávnění v uživatelském režimu a zápisu do všech SRS.

Func WriteMsr(int dwMsrIdx) 
{
	int value = __writemsr(dwMsrIdx); // Unsafe, can write to any MSR
	return value;
}

Pokud váš scénář vyžaduje zápis do MSR, musí ovladač vždy zkontrolovat, zda je registr pro zápis omezen na očekávaný index nebo rozsah. Dva příklady implementace bezpečné operace zápisu následují.

Func ConstrainedWriteMsr(int dwMsrIdx) 
{
    int value = 0;
    if (dwMsrIdx == expected_index) // Blocks from reading anything
    {
        value = __writemsr(dwMsrIdx); // Can only write to the expected constrained MSR
    }
    else
    {
        return error;
    }
    return value;
}

// OR

Func ConstrainedWriteMSR(int dwMsrIdx) 
{
    int value = 0;
    if (min_range <= dwMsrIdx <= max_range) // Blocks from reading anything
    {
        value = __writemsr(dwMsrIdx); // Can only write to the expected constrained MSR
    }
    else
    {
        return error;
    }
    return value;
}

Poskytování schopnosti ukončit procesy

Při implementaci funkcí v ovladači musí být použita extrémní opatrnost, která umožňuje ukončení procesů. Chráněné procesy a procesy PPL (Protected Process Light), jako jsou procesy používané antimalwarovým a antivirovým řešením, nesmí být ukončeny. Zveřejnění této funkce umožňuje útočníkům ukončit ochranu zabezpečení v systému.

Pokud váš scénář vyžaduje ukončení procesu, musí být implementovány následující kontroly pro ochranu před libovolným ukončením procesu pomocí PsLookupProcessByProcessId a PsIsProtectedProcess:

Func ConstrainedProcessTermination(DWORD dwProcessId)
{
	// Function to check if a process is a Protected Process Light (PPL)
    NTSTATUS status;
    BOOLEAN isPPL = FALSE;
    PEPROCESS process;
    HANDLE hProcess;

    // Open the process
    status = PsLookupProcessByProcessId(processId, &process);
    if (!NT_SUCCESS(status)) {
        return FALSE;
    }

    // Check if the process is a PPL
    if (PsIsProtectedProcess(process)) {
        isPPL = TRUE;
    }

    // Dereference the process
    ObDereferenceObject(process);
    return isPPL;
}

Poskytnutí možnosti čtení a zápisu do vstupu a výstupu portu

Zvýšení zabezpečení čtení z Port IO

Při poskytování možnosti čtení vstupu a výstupu portu (vstupně-výstupní operace) je třeba opatrnosti. Tento příklad kódu, který používá __indword, je nebezpečný.

Func ArbitraryInputPort(int inPort) 
{
	dwResult = __indword(inPort); // Unsafe, allows for arbitrary reading from Input Port
	return dwResult; 
}

Aby se zabránilo zneužití a využití ovladače, musí být očekávaný vstupní port omezen na stanovenou hranici použití.

Func ConstrainedInputPort(int inPort) 
{
	// The expected input port must be constrained to the required usage boundary to prevent abuse
	if(inPort == expected_InPort)
	{
		dwResult = __indword(inPort);
	}
	else
	{
		return error; 
	}
	return dwResult; 
}

Zvýšení zabezpečení zápisu do portu I/O

Při poskytování možnosti zápisu do vstupu a výstupu portu (V/V) je třeba opatrnosti. Tento příklad kódu, který používá __outword, je nebezpečný.

Func ArbitraryOutputPort(int outPort, DWORD dwValue) 
{
	__outdword(OutPort, dwValue); // Unsafe, allows for arbitrary writing to Output Port
}

Aby se zabránilo zneužití a využití ovladače, musí být vstupní port omezen na požadovanou hranici použití.

Func ConstrainedOutputPort(int outPort, DWORD dwValue) 
{
	// The expected output port must be constrained to the required usage boundary to prevent abuse
	if(outPort == expected_OutputPort)
	{
		__outdword(OutPort, dwValue); // checks on InputPort
	}
	else
	{
		return error; 
	}
}

Poskytnutí možnosti čtení a zápisu jádra, fyzické paměti nebo paměti zařízení

Zvýšení zabezpečení Memcpy

Tento ukázkový kód ukazuje nekontrénované a nebezpečné použití fyzické paměti.

Func ArbitraryMemoryCopy(src, dst, length) 
{
	memcpy(dst, src, length); // Unsafe, can read and write anything from physical memory
}

Pokud váš scénář vyžaduje čtení a zápis jádra, fyzické paměti nebo paměti zařízení, ovladač musí vždy zkontrolovat, že zdroj a cíle jsou omezené na očekávané indexy nebo rozsahy.

Func ConstrainedMemoryCopy(src, dst, length) 
{
	// valid_src and valid_dst must be constrained to required usage boundary to prevent abuse
	if(src == valid_Src && dst == valid_Dst)
	{
		memcpy(dst, src, length); 
	}
	else
	{
		return error;
	}
}

Vylepšení zabezpečení ZwMapViewOfSection

Následující příklad znázorňuje nebezpečnou a nesprávnou metodu čtení a zápisu fyzické paměti z uživatelského režimu s využitím ZwOpenSection a ZwMapViewOfSection API.

Func ArbitraryMap(PHYSICAL_ADDRESS Address)
{
	ZwOpenSection(&hSection, ... ,"\Device\PhysicalMemory");
	ZwMapViewOfSection(hSection, -1, 0, 0, 0, Address, ...);
}

Aby se zabránilo zneužití chování ovladače při čtení a zápisu škodlivými procesy uživatelského režimu, musí ovladač ověřit vstupní adresu a omezit mapování paměti pouze na požadovanou hranici použití pro tento scénář.

Func ConstrainedMap(PHYSICAL_ADDRESS paAddress)
{
	// expected_Address must be constrained to required usage boundary to prevent abuse
	if(paAddress == expected_Address)
	{
		ZwOpenSection(&hSection, ... ,"\Device\PhysicalMemory");
		ZwMapViewOfSection(hSection, -1, 0, 0, 0, paAddress, ...);
	}
	else
	{
		return error;
	}
}

Zvýšení zabezpečení MmMapLockedPagesSpecifyCache

Následující příklad ukazuje nezabezpečenou a nesprávnou metodu čtení a zápisu fyzické paměti z uživatelského režimu s využitím API MmMapIoSpace, IoAllocateMdl a MmMapLockedPagesSpecifyCache.

Func ArbitraryMap(PHYSICAL_ADDRESS paAddress)
{
	lpAddress = MmMapIoSpace(paAddress, qwSize, ...);
	pMdl = IoAllocateMdl( lpAddress, ...);
	MmMapLockedPagesSpecifyCache(pMdl, UserMode, ... );
}

Aby se zabránilo zneužití chování ovladače při čtení a zápisu škodlivými procesy uživatelského režimu, musí ovladač ověřit vstupní adresu a omezit mapování paměti pouze na požadovanou hranici použití pro tento scénář.

Func ConstrainedMap(PHYSICAL_ADDRESS paAddress)
{
	// expected_Address must be constrained to required usage boundary to prevent abuse
	if(paAddress == expected_Address && qwSize == valid_Size) 
	{
		lpAddress = MmMapIoSpace(paAddress, qwSize, ...);
		pMdl = IoAllocateMdl( lpAddress, ...);
		MmMapLockedPagesSpecifyCache(pMdl, UserMode, ... );
	}
	else
	{
		return error;
	}
}

Viz také

kontrolní seznam zabezpečení ovladače