Unsachgemäße Adressvalidierung in IOCTL mit METHOD_NEITHER I/O Control Code
Beschreibung
Unsachgemäße Adressvalidierung in IOCTL mit METHOD_NEITHER I/O Control Code ist eine Kernel-Modus-Schwachstelle spezifisch für Windows-Treiber, bei der ein IOCTL-Handler mit METHOD_NEITHER vom Benutzer bereitgestellte Speicheradressen nicht ordnungsgemäß validiert. Wenn METHOD_NEITHER in einem IOCTL-Control-Code angegeben ist, übergibt der I/O-Manager rohe User-Mode-Pointer direkt an den Treiber ohne jegliches Puffern oder Validierung. Der Treiber wird vollständig dafür verantwortlich, dass die Adressen gültige User-Mode-Adressen sind, bevor er darauf zugreift. Bei fehlender Validierung können Angreifer beliebige Speicheradressen angeben und möglicherweise Kernel-Speicher lesen oder schreiben.
Risiko
Diese Schwachstelle ermöglicht schwerwiegende Sicherheitsangriffe. Wenn ein Treiber beliebige Adressen ohne Validierung akzeptiert, können Angreifer sensiblen Kernel-Speicher lesen (einschließlich Anmeldedaten, Verschlüsselungsschlüssel oder Kernel-Datenstrukturen) oder in beliebige Kernel-Speicherorte schreiben. Das Schreiben in Kernel-Speicher kann vollständige Systemkompromittierung durch Code-Ausführung auf höchster Privilegstufe (SYSTEM/Kernel) ermöglichen. Selbst ohne Code-Ausführung können Angreifer das System zum Absturz bringen (BSOD), indem sie den Treiber veranlassen, auf ungültige Adressen zuzugreifen.
Lösung
Validieren Sie User-Space-Adressen immer bei Verwendung von METHOD_NEITHER. Verwenden Sie ProbeForRead() vor dem Lesen aus User-Puffern und ProbeForWrite() vor dem Schreiben in User-Puffer. Diese Routinen verifizieren, dass Adressen im gültigen User-Mode-Adressraum liegen. Umschließen Sie alle User-Puffer-Zugriffe mit try-except-Blöcken, um Zugriffsverletzungen elegant zu behandeln. Bevorzugen Sie METHOD_BUFFERED, METHOD_IN_DIRECT oder METHOD_OUT_DIRECT anstelle von METHOD_NEITHER wenn möglich, da diese Übertragungsmethoden den I/O-Manager automatisch Validierung durchführen lassen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Speicher lesen - Angreifer können beliebigen Kernel-Speicher lesen einschließlich sensibler Daten und Anmeldedaten. |
| Integrität | Bereich: Integrität Speicher ändern - Angreifer können in beliebigen Speicher schreiben und Kernel-Datenstrukturen korrumpieren. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz - Ungültiger Speicherzugriff verursacht Systemabsturz (BSOD). |
| Zugriffskontrolle | Bereich: Zugriffskontrolle Privilegien erlangen - Beliebiges Kernel-Schreiben ermöglicht Code-Ausführung auf SYSTEM-Ebene. |
Beispielcode
Verwundbarer Code
// Verwundbar: Keine Adressvalidierung mit METHOD_NEITHER
#include <ntddk.h>
#define IOCTL_VULNERABLE_READ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, \
METHOD_NEITHER, FILE_ANY_ACCESS)
NTSTATUS VulnerableDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_VULNERABLE_READ)
{
// Mit METHOD_NEITHER sind dies rohe User-Pointer
PVOID userInputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
PVOID userOutputBuffer = Irp->UserBuffer;
// Verwundbar: Keine Validierung von User-Adressen
// Angreifer könnte Kernel-Adresse angeben!
ULONG dataToRead = *(PULONG)userInputBuffer; // Beliebiges Lesen!
// Verwundbar: Schreiben an angreifergesteuerte Adresse
*(PULONG)userOutputBuffer = SensitiveKernelData; // Beliebiges Schreiben!
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
Gefixter Code
// Gefixt: Ordnungsgemäße Adressvalidierung mit METHOD_NEITHER
#include <ntddk.h>
#define IOCTL_FIXED_READ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, \
METHOD_NEITHER, FILE_ANY_ACCESS)
NTSTATUS FixedDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
ULONG dataToRead = 0;
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_FIXED_READ)
{
PVOID userInputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
PVOID userOutputBuffer = Irp->UserBuffer;
ULONG inputLen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
ULONG outputLen = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
// Gefixt: Puffergrößen validieren
if (inputLen < sizeof(ULONG) || outputLen < sizeof(ULONG)) {
status = STATUS_BUFFER_TOO_SMALL;
goto Complete;
}
__try {
// Gefixt: Prüfen dass Adressen im User-Space liegen
ProbeForRead(userInputBuffer, sizeof(ULONG), sizeof(ULONG));
ProbeForWrite(userOutputBuffer, sizeof(ULONG), sizeof(ULONG));
// Gefixt: Zugriff innerhalb try-Block falls Seite ausgelagert
dataToRead = *(PULONG)userInputBuffer;
// Anfrage verarbeiten
ULONG result = ProcessRequest(dataToRead);
// Gefixt: Ergebnis innerhalb try-Block schreiben
*(PULONG)userOutputBuffer = result;
Irp->IoStatus.Information = sizeof(ULONG);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// Gefixt: Zugriffsverletzung elegant behandeln
status = GetExceptionCode();
DbgPrint("Exception beim User-Puffer-Zugriff: 0x%X\n", status);
}
}
Complete:
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
// Gefixt: METHOD_BUFFERED stattdessen verwenden (bevorzugt)
#include <ntddk.h>
// METHOD_BUFFERED - I/O Manager behandelt Validierung
#define IOCTL_SAFE_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, \
METHOD_BUFFERED, FILE_ANY_ACCESS)
NTSTATUS SafeBufferedIoctl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SAFE_BUFFERED)
{
// Mit METHOD_BUFFERED alloziert I/O Manager Kernel-Puffer
// und kopiert Daten - keine User-Adressvalidierung nötig
PVOID systemBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG inputLen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
ULONG outputLen = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
if (inputLen < sizeof(ULONG)) {
status = STATUS_BUFFER_TOO_SMALL;
goto Complete;
}
// Sicher: systemBuffer ist Kernel-Speicher, bereits validiert
ULONG inputData = *(PULONG)systemBuffer;
ULONG result = ProcessRequest(inputData);
if (outputLen >= sizeof(ULONG)) {
*(PULONG)systemBuffer = result;
Irp->IoStatus.Information = sizeof(ULONG);
}
}
Complete:
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
CVE-Beispiele
- CVE-2006-2373: Windows File-Sharing-Protokoll-Treiber erlaubte beliebige Code-Ausführung über METHOD_NEITHER IOCTL.
- CVE-2009-0686: Antivirus-Produkt verwundbar für Privilegieneskalation durch unsachgemäße Adressvalidierung.
- CVE-2008-5724: Personal Firewall erlaubte SYSTEM-Privilegieneskalation über unsachgemäße IOCTL-Behandlung.
Referenzen
- MITRE Corporation. "CWE-781: Improper Address Validation in IOCTL with METHOD_NEITHER I/O Control Code." https://cwe.mitre.org/data/definitions/781.html
- Microsoft. "Buffer Descriptions for I/O Control Codes." Windows Driver Documentation.
- Microsoft. "Using Neither Buffered Nor Direct I/O." Windows Driver Documentation.