1 / 21

Avoiding Driver Security Pitfalls

Avoiding Driver Security Pitfalls . Matt Miller Senior Software Security Engineer SWI Security Science mamill@microsoft.com. Agenda. Introduction Why driver security is important Principles of driver security Common driver security pitfalls & how to avoid them

charo
Download Presentation

Avoiding Driver Security Pitfalls

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Avoiding Driver Security Pitfalls Matt Miller Senior Software Security Engineer SWI Security Science mamill@microsoft.com

  2. Agenda • Introduction • Why driver security is important • Principles of driver security • Common driver security pitfalls & how to avoid them • Interacting with user-mode data • Object security • Device security • Additional tips for helping to secure your drivers

  3. Why Driver Security Is Important • Device drivers play a critical role in keeping the kernel secure • Kernel mode drivers share the keys to the castle • Driver flaws can allow the entire system to be compromised • Drivers are becoming an increased target of public security scrutiny • Wireless drivers • Video drivers • We must work together to keep customers (and the kernel) secure

  4. Principles of Driver Security • Uphold established security guarantees • Separation of kernel and user • Isolation of processes • Enforcement of granted privileges and rights • It pays to be paranoid • Do not trust any user-mode data, network data, file data, etc. • Assumptions are the root of all evil • Do not assume security is provided elsewhere • Do not assume security checks will kill performance

  5. Common Driver Security PitfallsandHow to Avoid Them

  6. Checking Buffer Lengths case IOCTL_SET_DATA: // METHOD_BUFFERED IOCTL PUCHAR InBuffer = Irp->AssociatedIrp.SystemBuffer; PUCHAR OutBuffer = Irp->AssociatedIrp.SystemBuffer; RtlCopyMemory(GlobalData, InBuffer, Stack->Param…InputBufferLength); RtlCopyMemory(OutBuffer, &GlobalDataStats, sizeof(GlobalDataStats)); case IOCTL_SET_DATA: // METHOD_BUFFERED IOCTL PUCHAR InBuffer = Irp->AssociatedIrp.SystemBuffer; PUCHAR OutBuffer = Irp->AssociatedIrp.SystemBuffer; if (Stack->Param…InputBufferLength > sizeof(GlobalData)) break; if (Stack->Param…OutputBufferLength != sizeof(GlobalDataStats)) break; RtlCopyMemory(GlobalData, InBuffer, Stack->Param…InputBufferLength); RtlCopyMemory(OutBuffer, &GlobalDataStats, sizeof(GlobalDataStats)); • Pitfall: Failing to check the length of buffers provided by user mode • User mode can pass buffers that are smaller or larger than you expect • Can lead to buffer overflows or kernel data leakage • Solution: Always check the lengths associated with untrusted buffers • WdfRequestRetrieveXxxBuffer takes MinimumRequiredSize

  7. Integer Wraps When Using Untrusted Values case IOCTL_SET_NAME: PNAMEBUF Buf = Irp->AssociatedIrp.SystemBuffer; ULONG BufLen = Stack->Parameters.DeviceIoControl.InputBufferLength; if ((BufLen != sizeof(NAMEBUF)) || (Buf->NameLength + sizeof(CHAR) > 256)) break; RtlCopyMemory(GlobalName, Buf->Name, Buf->NameLength); case IOCTL_SET_NAME: // METHOD_BUFFERED IOCTL PNAMEBUF Buf = Irp->AssociatedIrp.SystemBuffer; ULONG BufLen = Stack->Parameters.DeviceIoControl.InputBufferLength; if (BufLen != sizeof(NAMEBUF)) break; Status = RtlULongAdd(Buf->NameLength, sizeof(CHAR), &Length); if (!NT_SUCCESS(Status) || Length > 256 ) break; RtlCopyMemory(GlobalName, Buf->Name, Buf->NameLength); typedefstruct { ULONG NameLength; CHAR Name[256]; } *PNAMEBUF; • Pitfall: Using untrusted integers can lead to integer wraps • Conditionals with integer wrapping problems can lead to overflows • Passing wrapped integers to allocation routines causes small allocations • Solution: Use safe integer manipulation routines to catch wraps • Routines can be found in ntintsafe.h (RtlULongAdd, RtlULongMult, …) NameLength = -1 -1 + 1 = 0 < 256

  8. Probing User-Mode Buffers case IOCTL_SET_DATA: // METHOD_NEITHER IOCTL PUCHAR InBuffer = Stack->Parameters.DeviceIoControl.Type3InputBuffer; PUCHAR OutBuffer = Irp->UserBuffer; … // Buffer sizes are checked try { ProbeForRead(InBuffer, Stack->Param…InputBufferLength, 1); ProbeForWrite(OutBuffer, Stack->Param…OutputBufferLength, 1); } except(EXCEPTION_EXECUTE_HANDLER) { break; } RtlCopyMemory(OutBuffer, GlobalData, Stack->Param…OutputBufferLength); RtlCopyMemory(GlobalData, InBuffer, Stack->Param…InputBufferLength); case IOCTL_SET_DATA: // METHOD_NEITHER IOCTL PUCHAR InBuffer = Stack->Parameters.DeviceIoControl.Type3InputBuffer; PUCHAR OutBuffer = Irp->UserBuffer; … // Buffer sizes are checked RtlCopyMemory(OutBuffer, GlobalData, Stack->Param…OutputBufferLength); RtlCopyMemory(GlobalData, InBuffer, Stack->Param…InputBufferLength); • Pitfall: Not ensuring that user-mode pointers are valid • User-mode pointers can come from many places (IOCTLs, FSCTLs, embedded in structures, etc.) • Can allow kernel memory to be indirectly referenced from user mode • Solution: Always probe user-mode pointers • Probing must occur inside a try/except block!

  9. Accessing User-Mode Buffers case IOCTL_SET_DATA: // METHOD_NEITHER IOCTL PUCHAR InBuffer = Stack->Parameters.DeviceIoControl.Type3InputBuffer; PUCHAR OutBuffer = Irp->UserBuffer; … // Buffer sizes are checked try { ProbeForRead(InBuffer, Stack->Param…InputBufferLength, 1); ProbeForWrite(OutBuffer, Stack->Param…OutputBufferLength, 1); } except(EXCEPTION_EXECUTE_HANDLER) { break; } RtlCopyMemory(OutBuffer, GlobalData, Stack->Param…OutputBufferLength); RtlCopyMemory(GlobalData, InBuffer, Stack->Param…InputBufferLength); case IOCTL_SET_DATA: // METHOD_NEITHER IOCTL PUCHAR InBuffer = Stack->Parameters.DeviceIoControl.Type3InputBuffer; PUCHAR OutBuffer = Irp->UserBuffer; … // Buffer sizes are checked try { ProbeForRead(InBuffer, Stack->Param…InputBufferLength, 1); ProbeForWrite(OutBuffer, Stack->Param…OutputBufferLength, 1); RtlCopyMemory(OutBuffer, GlobalData, Stack->Param…OutputBufferLength); RtlCopyMemory(GlobalData, InBuffer, Stack->Param…InputBufferLength); } except(EXCEPTION_EXECUTE_HANDLER) { break; } • Pitfall: Accessing user-mode memory in a non-recoverable fashion • A fault can occur at any time (e.g. another thread could unmap memory) • Solution: Always wrap user-mode buffer access in try/except • Any fault that occurs can be handled and an error returned

  10. Capturing User Mode Buffers case IOCTL_SET_NAME: // METHOD_NEITHER IOCTL PNAMEBUF UserBuf = Stack->Parameters…Type3InputBuffer; ULONG UserBufLen = Stack->Parameters…InputBufferLength; try { ProbeForRead(UserBuf, UserBufLen, 1); if ((UserBufLen != sizeof(NAMEBUF)) || (UserBuf->NameLength > 256)) break; RtlCopyMemory(Name, UserBuf->Name, UserBuf->NameLength); } except(EXCEPTION_EXECUTE_HANDLER) { break; } case IOCTL_SET_NAME: // METHOD_NEITHER IOCTL NAMEBUF CapturedBuf; volatile NAMEBUF* UserBuf = Stack->Parameters…Type3InputBuffer; ULONG UserBufLen = Stack->Parameters…InputBufferLength; try { ProbeForRead(UserBuf, UserBufLen, 1); if (UserBufLen != sizeof(NAMEBUF)) break; CapturedBuf = *UserBuf; if (CapturedBuf.NameLength > 256)) break; RtlCopyMemory(Name, CapturedBuf.Name, CapturedBuf.NameLength); } except(EXCEPTION_EXECUTE_HANDLER) { break; } typedefstruct { ULONG NameLength; CHAR Name[256]; } NAMEBUF, *PNAMEBUF; • Pitfall: Failing to capture data from user memory • The contents and layout of user-mode memory may change asynchronously • Leads to time-of-check, time-of-use issues • Solution: Always capture user-mode data prior to checking or using it • User-mode memory changes do not affect captured buffer • Be aware of the need for the volatile keyword! NameLength could change after this check

  11. Accessing Pointers within User Mode Buffers case IOCTL_SET_NAME: // METHOD_NEITHER IOCTL NAMEBUF CapturedBuf; volatile NAMEBUF* UserBuf = Stack->Parameters…Type3InputBuffer; ULONG UserBufLen = Stack->Parameters…InputBufferLength; try { ProbeForRead(UserBuf, UserBufLen, 1); if (UserBufLen != sizeof(NAMEBUF)) break; CapturedBuf = *UserBuf; if (CapturedBuf.NameLength > 256)) break; RtlCopyMemory(Name, CapturedBuf.Name, CapturedBuf.NameLength); } except(EXCEPTION_EXECUTE_HANDLER) { break; } case IOCTL_SET_NAME: // METHOD_NEITHER IOCTL NAMEBUF CapturedBuf; volatile NAMEBUF* UserBuf = Stack->Parameters…Type3InputBuffer; ULONG UserBufLen = Stack->Parameters…InputBufferLength; try { ProbeForRead(UserBuf, UserBufLen, 1); if (UserBufLen != sizeof(NAMEBUF)) break; CapturedBuf = *UserBuf; if (CapturedBuf.NameLength > 256)) break; ProbeForRead(CapturedBuf.Name, CapturedBuf.NameLength, 1); RtlCopyMemory(Name, CapturedBuf.Name, CapturedBuf.NameLength); } except(EXCEPTION_EXECUTE_HANDLER) { break; } typedefstruct { ULONG NameLength; PCHAR Name; } NAMEBUF, *PNAMEBUF; case IOCTL_SET_NAME: // METHOD_NEITHER IOCTL PNAMEBUF UserBuf = Stack->Parameters…Type3InputBuffer; ULONG UserBufLen = Stack->Parameters…InputBufferLength; try { ProbeForRead(UserBuf, UserBufLen, 1); if ((UserBufLen != sizeof(NAMEBUF)) || (UserBuf->NameLength > 256)) break; RtlCopyMemory(Name, UserBuf->Name, UserBuf->NameLength); } except(EXCEPTION_EXECUTE_HANDLER) { break; } • Pitfall: Accessing pointers contained within user memory unsafely • Pointer fields in structures obtained from user mode cannot be trusted • Could be invalid or kernel-mode addresses as mentioned previously • Solution: Follow access rules for all pointers obtained from user mode • Probe, check lengths, capture containing buffer (if needed), probe field, then access

  12. Getting the Object Associated with a Handle case IOCTL_UPDATE_FILE: // METHOD_BUFFER IOCTL … FileHandle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; Status = ObReferenceObjectByHandle( FileHandle, // Handle FILE_READ_DATA, // Desired access NULL, // Object type KernelMode, // Access mode &FileObject, // Object pointer NULL); // Object handle information case IOCTL_UPDATE_FILE: // METHOD_BUFFER IOCTL … FileHandle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; Status = ObReferenceObjectByHandle( FileHandle, // Handle FILE_READ_DATA, // Desired access *IoFileObjectType, // Object type Irp->RequestorMode, // Access mode &FileObject, // Object pointer NULL); // Object handle information FileHandle could be associated with a non-file object FileHandle could be a kernel handle Caller may not be granted desired rights • Pitfall: Passing incorrect parameters when getting a handle’s object • A NULL ObjectType disables object type validation • Passing KernelMode as the AccessMode disables security access checks • Solution: Always pass a valid ObjectType and correct AccessMode

  13. Creating a Handle to an Object OBJECT_ATTRIBUTES Attributes; InitializeObjectAttributes( &Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwCreateSection( &SectionHandle, SECTION_ALL_ACCESS, &Attributes, …); OBJECT_ATTRIBUTES Attributes; InitializeObjectAttributes( &Attributes, NULL, 0, NULL, NULL); Status = ZwCreateSection( &SectionHandle, SECTION_ALL_ACCESS, &Attributes, …); • Pitfall: Creating a non-kernel handle to an object • Allows the calling user-mode process to modify the object associated with the handle • User mode can close the handle and open a new handle to a different resource • Solution: Use OBJ_KERNEL_HANDLE when creating a handle to an object • Includes ObOpenObjectByPointer, ZwCreateFile, ZwCreateSection, etc

  14. Device Object Creation Status = IoCreateDevice(DriverObject, // Driver object    0, // Device extension    &DeviceName, // Device name    FILE_DEVICE_UNKNOWN, // Device type0, // Device characteristics     FALSE, // Exclusive &NewDeviceObject); // Device object Status = IoCreateDevice(DriverObject, // Driver object    0, // Device extension    &DeviceName, // Device name    FILE_DEVICE_UNKNOWN, // Device typeFILE_DEVICE_SECURE_OPEN, // Device characteristics     FALSE, // Exclusive &NewDeviceObject); // Device object • Pitfall: Inadvertently disabling device namespace open access checks • ACLs checked when opening \Device\Foobut not \Device\Foo\Bar • Allows device object to be opened by nonprivileged callers • Solution: Pass FILE_DEVICE_SECURE_OPEN to IoCreateDevice • WdfDeviceCreate automatically sets this characteristic

  15. Tools & Tips

  16. Tools and Tips for Improving Driver Security • Use Driver Verifier • Contains a handful of checks capable of detecting security issues • Test teams should “fuzz” driver interfaces • Write tools to provide intentionally bogus input to the driver • Targets for fuzzing include IOCTLs, network I/O, file I/O, etc • Peer or industry review of driver code • Play devil’s advocate!

  17. Wrapping Up

  18. Driver Security Recap • Interacting with user-mode data • Encapsulate: Always encapsulate access to user-mode memory in try/except • Probe: Always probe pointers obtained from user mode • Capture: Always capture user-mode memory that is accessed multiple times • Validate: Always check the lengths of user-mode buffers before accessing • Object security • Use correct object type and access mode when getting a handle’s object • Use OBJ_KERNEL_HANDLE when creating handles • Device security • Use FILE_DEVICE_SECURE_OPEN when creating a device

  19. Call to Action • Apply what you’ve learned • Review your driver code for the security issues discussed • Be security conscious while developing your drivers • Challenge security assumptions • Limit your exposure to untrusted data (reduce attack surface) • Only you can prevent driver security issues!

  20. Resources • Driver security information on WHDC Web Sitehttp://www.microsoft.com/whdc/driver/security/default.mspx • Windows Security Model: What Every Driver Writer Needs to Knowhttp://go.microsoft.com/fwlink/?LinkID=56770 • Common Driver Reliability Issueshttp://go.microsoft.com/fwlink/?LinkID=56774 • Driver verifier • Driver verifier on WHDC Web sitehttp://www.microsoft.com/whdc/DevTools/tools/DrvVerifier.mspx • DDC 2008: Driver Verifier Advancements in Windows 7Mon. 5:15-6:15 and Wed. 1:30-2:30

More Related