Exponiertes IOCTL mit unzureichender Zugriffskontrolle
Beschreibung
Exponiertes IOCTL mit unzureichender Zugriffskontrolle ist eine Kernel-Modus-Schwachstelle, bei der ein Gerätetreiber eine IOCTL (Input/Output Control)-Schnittstelle mit Funktionalität implementiert, die auf privilegierte Benutzer beschränkt sein sollte, aber keine ordnungsgemäße Zugriffskontrolle durchsetzt. IOCTLs bieten einen Weg für User-Mode-Anwendungen, mit Kernel-Mode-Treibern zu kommunizieren. Wenn sensitive Operationen (wie Speicherzugriff, Privilegänderung oder Hardware-Steuerung) durch IOCTLs ohne ordnungsgemäße Zugriffsbeschränkungen exponiert werden, kann jeder Benutzer oder Prozess, der ein Handle zum Gerät öffnen kann, diese privilegierten Operationen aufrufen.
Risiko
Unzureichend geschützte IOCTLs sind eine schwerwiegende Schwachstelle in Kernel-Treibern. Angreifer können exponierte IOCTLs ausnutzen, um verschiedene bösartige Ziele zu erreichen: beliebigen Speicher lesen oder schreiben, Sicherheitssoftware deaktivieren, Privilegien auf SYSTEM-Ebene eskalieren, Denial of Service verursachen oder auf sensitive Hardware zugreifen. Entwickler nehmen oft an, dass nur vertrauenswürdige Anwendungen mit Treibern kommunizieren, was zu ungenügender Validierung und Zugriffskontrolle führt. Da IOCTLs im Kernel-Modus laufen, gibt erfolgreiche Ausnutzung Angreifern die höchste Zugriffsebene des Systems.
Lösung
Implementieren Sie ordnungsgemäße Zugriffskontrolle für Gerätobjekte und IOCTLs. Unter Windows verwenden Sie Sicherheitsdeskriptoren auf Gerätobjekten, um einzuschränken, welche Benutzer Handles öffnen können. Setzen Sie das FILE_DEVICE_SECURE_OPEN-Flag und erstellen Sie symbolische Links in geschützten Namespaces. Prüfen Sie Aufrufer-Privilegien innerhalb von IOCTL-Handlern mit IoIs32bitProcess(), SeSinglePrivilegeCheck() oder ähnlichen Funktionen. Minimieren Sie die Angriffsfläche, indem Sie nur notwendige Funktionalität exponieren. Validieren Sie alle Eingabeparameter gründlich, auch wenn Zugriffskontrolle vorhanden ist.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Anwendungsdaten ändern - Angreifer können IOCTL-Funktionalität aufrufen, um Speicher oder Systemzustand zu ändern. |
| Vertraulichkeit | Bereich: Vertraulichkeit Anwendungsdaten lesen - Exponierte IOCTLs können das Lesen sensitiver Kernel- oder Hardware-Daten ermöglichen. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz - Bösartige IOCTL-Aufrufe können das System zum Absturz bringen. |
| Zugriffskontrolle | Bereich: Zugriffskontrolle Privilegien erlangen - Angreifer können zu Kernel/SYSTEM-Privilegien durch exponierte Funktionalität eskalieren. |
Beispielcode
Verwundbarer Code
// Verwundbar: Gerät mit permissiver Zugriffskontrolle erstellt
#include <ntddk.h>
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L"\\Device\\VulnerableDriver");
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\DosDevices\\VulnerableDriver");
// Verwundbar: Standard-Sicherheit erlaubt allen Benutzern Gerät zu öffnen
status = IoCreateDevice(
DriverObject,
0,
&deviceName,
FILE_DEVICE_UNKNOWN,
0, // Verwundbar: Kein FILE_DEVICE_SECURE_OPEN Flag
FALSE,
&deviceObject
);
if (!NT_SUCCESS(status)) return status;
// Verwundbar: Symbolischer Link in \\DosDevices für alle Benutzer zugänglich
status = IoCreateSymbolicLink(&symLink, &deviceName);
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VulnerableDeviceControl;
return STATUS_SUCCESS;
}
// Verwundbar: IOCTL-Handler ohne Zugriffskontrollprüfungen
NTSTATUS VulnerableDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_READ_KERNEL_MEMORY:
// Verwundbar: Jeder Benutzer kann Kernel-Speicher lesen
status = ReadKernelMemory(Irp);
break;
case IOCTL_WRITE_KERNEL_MEMORY:
// Verwundbar: Jeder Benutzer kann Kernel-Speicher schreiben
status = WriteKernelMemory(Irp);
break;
case IOCTL_DISABLE_PROTECTION:
// Verwundbar: Jeder Benutzer kann Sicherheitsfunktionen deaktivieren
status = DisableSecurityFeature(Irp);
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
Gefixter Code
// Gefixt: Gerät mit ordnungsgemäßem Sicherheitsdeskriptor erstellt
#include <ntddk.h>
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L"\\Device\\SecureDriver");
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\DosDevices\\SecureDriver");
// Gefixt: Expliziten Sicherheitsdeskriptor verwenden
UNICODE_STRING sddl = RTL_CONSTANT_STRING(
L"D:P(A;;GA;;;SY)(A;;GA;;;BA)" // Nur SYSTEM und Admin
);
status = IoCreateDeviceSecure(
DriverObject,
0,
&deviceName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN, // Gefixt: Öffnungsprüfungen erfordern
FALSE,
&sddl, // Gefixt: Sicherheitsdeskriptor beschränkt Zugriff
NULL,
&deviceObject
);
if (!NT_SUCCESS(status)) return status;
status = IoCreateSymbolicLink(&symLink, &deviceName);
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SecureDeviceControl;
DriverObject->MajorFunction[IRP_MJ_CREATE] = SecureCreate;
return STATUS_SUCCESS;
}
// Gefixt: IOCTL-Handler mit Privilegprüfungen
NTSTATUS SecureDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_READ_KERNEL_MEMORY:
case IOCTL_WRITE_KERNEL_MEMORY:
// Gefixt: Verifizieren dass Aufrufer erforderliche Privilegien hat
if (!IsCallerPrivileged()) {
status = STATUS_ACCESS_DENIED;
break;
}
status = HandleKernelMemoryAccess(Irp);
break;
case IOCTL_REGULAR_OPERATION:
// Weniger privilegierte Operationen weiterhin verfügbar
status = HandleRegularOperation(Irp);
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
// Gefixt: Hilfsfunktion zur Prüfung von Aufrufer-Privilegien
BOOLEAN IsCallerPrivileged(void)
{
SECURITY_SUBJECT_CONTEXT SubjectContext;
BOOLEAN isPrivileged = FALSE;
SeCaptureSubjectContext(&SubjectContext);
// Auf spezifisches Privileg prüfen
isPrivileged = SePrivilegeCheck(
&SeLoadDriverPrivilege,
&SubjectContext,
UserMode
);
SeReleaseSubjectContext(&SubjectContext);
return isPrivileged;
}
CVE-Beispiele
- CVE-2009-2208: OS setzte Berechtigungen auf Netzwerkeinstellungs-IOCTL nicht durch und erlaubte unprivilegierte Änderungen.
- CVE-2008-3831: Direct-Rendering-Manager-Treiber fehlten IOCTL-Zugriffsbeschränkungen.
- CVE-2008-3525: IOCTL fehlte erforderliche Capability-Prüfungen und erlaubte Privilegieneskalation.
- CVE-2008-0322: Unsichere Geräteberechtigungen ermöglichen beliebiges Speicherüberschreiben über IOCTL.
- CVE-2007-4277: Antivirus-Produkt mit schwachen Geräteberechtigungen exponierte Buffer Overflow.
Referenzen
- MITRE Corporation. "CWE-782: Exposed IOCTL with Insufficient Access Control." https://cwe.mitre.org/data/definitions/782.html
- Microsoft. "Controlling Device Access." Windows Driver Documentation.
- Microsoft. "Security Descriptors for Device Objects." Windows Driver Documentation.