Bagikan melalui


Praktik terbaik untuk membatasi perilaku berhak istimewa tinggi dalam driver mode kernel

Topik ini merangkum pola pengembangan yang tidak aman yang dapat menyebabkan eksploitasi dan penyalahgunaan kode driver kernel Windows Anda. Topik ini menyediakan rekomendasi pengembangan dan sampel kode untuk membantu membatasi perilaku istimewa. Mengikuti praktik terbaik ini akan membantu meningkatkan keamanan melakukan perilaku istimewa di kernel Windows.

Gambaran umum perilaku driver tidak aman

Meskipun diharapkan bahwa driver Windows melakukan perilaku istimewa tinggi dalam mode kernel, tidak melakukan pemeriksaan keamanan dan menambahkan batasan pada perilaku istimewa tidak dapat diterima. Program Kompatibilitas Perangkat Keras Windows (WHCP), sebelumnya WHQL, memerlukan pengiriman driver baru untuk mematuhi persyaratan ini.

Contoh perilaku yang tidak aman dan berbahaya termasuk, tetapi tidak terbatas pada, berikut ini:

Menyediakan kemampuan untuk membaca dan menulis MSR

Meningkatkan keamanan saat membaca dari MSR

Dalam contoh ReadMsr ini, driver memungkinkan perilaku tidak aman dengan memungkinkan setiap dan semua register dibaca secara sewenang-wenang menggunakan intrinsik register khusus model __readmsr. Ini dapat mengakibatkan penyalahgunaan oleh proses berbahaya dalam mode pengguna.

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

Jika skenario Anda memerlukan pembacaan dari MSR, driver harus selalu memeriksa apakah register untuk dibaca dibatasi pada indeks atau rentang yang diharapkan. Berikut ini dua contoh cara menerapkan operasi baca yang aman.

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;
}

Meningkatkan keamanan dalam menulis ke Pembaca Pita Magnetik (MSR)

Dalam contoh WriteMsr pertama, driver memungkinkan perilaku yang tidak aman dengan memungkinkan semua register ditulis dengan cara sewenang-wenang. Ini dapat mengakibatkan penyalahgunaan oleh proses berbahaya untuk meningkatkan hak istimewa dalam mode pengguna dan menulis ke semua MSR.

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

Jika skenario Anda memerlukan penulisan ke MSR, driver harus selalu memeriksa apakah register untuk menulis dibatasi ke indeks atau rentang yang diharapkan. Dua contoh cara mengimplementasikan operasi tulis yang aman adalah sebagai berikut.

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;
}

Menyediakan kemampuan untuk mengakhiri proses

Perhatian ekstrem harus digunakan saat menerapkan fungsionalitas di driver Anda yang memungkinkan proses dihentikan. Proses yang dilindungi dan proses yang memiliki perlindungan ringan (PPL), seperti yang digunakan oleh solusi anti-malware dan anti-virus, tidak boleh dihentikan. Mengekspos fungsionalitas ini memungkinkan penyerang untuk mengakhiri perlindungan keamanan pada sistem.

Jika skenario Anda memerlukan penghentian proses, pemeriksaan berikut harus diimplementasikan untuk melindungi dari penghentian proses arbitrer, menggunakan PsLookupProcessByProcessId dan 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;
}

Menyediakan kemampuan untuk membaca dan menulis ke input dan output Port

Meningkatkan keamanan membaca dari Port IO

Harus berhati-hati ketika memberikan kemampuan untuk membaca ke input/output Port (I/O). Contoh kode yang menggunakan __indword tidak aman.

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

Untuk mencegah penyalahgunaan dan eksploitasi driver, port input yang diharapkan harus dibatasi ke batas penggunaan yang diperlukan.

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; 
}

Meningkatkan keamanan penulisan ke Port IO

Perhatian harus dilakukan saat memberikan kemampuan untuk menulis ke port input/output (I/O). Contoh kode yang menggunakan __outword tidak aman.

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

Untuk mencegah penyalahgunaan dan eksploitasi driver, port input yang diharapkan harus dibatasi ke batas penggunaan yang diperlukan.

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; 
	}
}

Menyediakan kemampuan untuk membaca dan menulis kernel, fisik, atau memori perangkat

Meningkatkan keamanan Memcpy

Kode sampel ini menunjukkan penggunaan memori fisik yang tidak dibatasi dan tidak aman.

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

Jika skenario Anda memerlukan pembacaan dan penulisan kernel, memori fisik atau perangkat, driver harus selalu memeriksa bahwa sumber dan tujuan dibatasi pada indeks atau rentang yang diharapkan.

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;
	}
}

Meningkatkan keamanan ZwMapViewOfSection

Contoh berikut mengilustrasikan metode yang tidak aman dan tidak tepat untuk membaca dan menulis memori fisik dari mode pengguna menggunakan ZwOpenSection dan API ZwMapViewOfSection.

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

Untuk mencegah penyalahgunaan dan eksploitasi perilaku baca/tulis driver dengan proses mode pengguna berbahaya, driver harus memvalidasi alamat input dan membatasi pemetaan memori hanya ke batas penggunaan yang diperlukan untuk skenario.

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;
	}
}

Meningkatkan keamanan MmMapLockedPagesSpecifyCache

Contoh berikut mengilustrasikan metode yang tidak aman dan tidak tepat untuk membaca dan menulis memori fisik dari mode pengguna menggunakan MmMapIoSpace, IoAllocateMdl dan MMMapLockedPagesSpecifyCache API.

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

Untuk mencegah penyalahgunaan dan eksploitasi perilaku baca/tulis driver dengan proses mode pengguna berbahaya, driver harus memvalidasi alamat input dan membatasi pemetaan memori hanya ke batas penggunaan yang diperlukan untuk skenario.

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;
	}
}

Lihat Juga

daftar periksa keamanan Driver