add irp buffer validation

This commit is contained in:
lhodges1 2023-11-09 18:30:59 +11:00
parent e0c44632cb
commit 8ea66dbfc9
11 changed files with 190 additions and 26 deletions

View file

@ -775,6 +775,14 @@ ProcLoadInitialiseProcessConfig(
PEPROCESS eprocess;
PDRIVER_INITIATION_INFORMATION information;
status = ValidateIrpInputBuffer(Irp, sizeof(DRIVER_INITIATION_INFORMATION));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate input buffer");
return status;
}
information = (PDRIVER_INITIATION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
status = PsLookupProcessByProcessId(information->protected_process_id, &eprocess);

View file

@ -3,6 +3,7 @@
#include <intrin.h>
#include "common.h"
#include "ioctl.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PerformVirtualizationDetection)
@ -90,6 +91,14 @@ PerformVirtualizationDetection(
{
PAGED_CODE();
NTSTATUS status = ValidateIrpOutputBuffer(Irp, sizeof(HYPERVISOR_DETECTION_REPORT));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate IRP output buffer");
return status;
}
HYPERVISOR_DETECTION_REPORT report;
report.aperf_msr_timing_check = APERFMsrTimingCheck();
report.invd_emulation_check = TestINVDEmulation();

View file

@ -4,6 +4,7 @@
#include "driver.h"
#include "modules.h"
#include "callbacks.h"
#include "ioctl.h"
#include <bcrypt.h>
#include <initguid.h>
@ -155,9 +156,19 @@ GetDriverImageSize(
&modules
);
status = ValidateIrpOutputBuffer(Irp, sizeof(ULONG));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate IRP output buffer");
goto end;
}
Irp->IoStatus.Information = sizeof(ULONG);
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &driver_info->ImageSize, sizeof(ULONG));
end:
if (modules.address)
ExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
@ -834,6 +845,14 @@ RetrieveInMemoryModuleExecutableSections(
return status;
}
status = ValidateIrpOutputBuffer(Irp, bytes_written);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate IRP output buffer");
goto end;
}
Irp->IoStatus.Information = bytes_written;
RtlCopyMemory(
@ -842,6 +861,8 @@ RetrieveInMemoryModuleExecutableSections(
bytes_written
);
end:
if (buffer)
ExFreePoolWithTag(buffer, POOL_TAG_INTEGRITY);
@ -1071,6 +1092,14 @@ ValidateProcessLoadedModule(
PVOID section = NULL;
ULONG section_size = NULL;
status = ValidateIrpInputBuffer(Irp, sizeof(PROCESS_MODULE_INFORMATION));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate IRP input buffer");
return status;
}
module_info = (PPROCESS_MODULE_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
GetProtectedProcessEProcess(&process);
@ -1154,6 +1183,14 @@ ValidateProcessLoadedModule(
bstatus = RtlEqualMemory(in_memory_hash, disk_hash, in_memory_hash_size);
status = ValidateIrpOutputBuffer(Irp, sizeof(PROCESS_MODULE_VALIDATION_RESULT));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate IRP output buffer");
goto end;
}
/*
* Because each module is passed per IRP we don't need to send any reports
* to the queue we can simply pass it back to usermode via the same IRP.

View file

@ -74,6 +74,64 @@ DispatchApcOperation(
return status;
}
/*
* Obviously, its important we check that the output buffer size for each IRP is big enough
* to hold whatever we are passing back to usermode.
*
* Another important thing to note is that the windows IO manager will only zero out the size
* of the input buffer. Given that we use METHOD_BUFFERED for all communication, the input
* and output buffer are the same, with the size used being that of the greatest buffer passed
* to DeviceIoControl. The IO manager will then zero our the buffer to the size of the input
* buffer, so if the output buffer is larger then the input buffer there will be uninitialised
* memory in the buffer so we must zero out the buffer to the length of the output buffer.
*/
NTSTATUS
ValidateIrpOutputBuffer(
_In_ PIRP Irp,
_In_ ULONG RequiredSize
)
{
if (!Irp || !RequiredSize)
return STATUS_INVALID_PARAMETER;
PIO_STACK_LOCATION io = IoGetCurrentIrpStackLocation(Irp);
if (!io)
return STATUS_ABANDONED;
if (io->Parameters.DeviceIoControl.OutputBufferLength < RequiredSize)
return STATUS_BUFFER_TOO_SMALL;
RtlSecureZeroMemory(Irp->AssociatedIrp.SystemBuffer, RequiredSize);
return STATUS_SUCCESS;
}
/*
* Here we just check that the input buffers size matches the expected size..
* It isnt a very secure check but we can work on that later...
*/
NTSTATUS
ValidateIrpInputBuffer(
_In_ PIRP Irp,
_In_ ULONG RequiredSize
)
{
if (!Irp || !RequiredSize)
return STATUS_INVALID_PARAMETER;
PIO_STACK_LOCATION io = IoGetCurrentIrpStackLocation(Irp);
if (!io)
return STATUS_ABANDONED;
if (io->Parameters.DeviceIoControl.InputBufferLength != RequiredSize)
return STATUS_INVALID_BUFFER_SIZE;
return STATUS_SUCCESS;
}
//_Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
NTSTATUS
DeviceControl(
@ -280,7 +338,7 @@ DeviceControl(
case IOCTL_SCAN_FOR_UNLINKED_PROCESS:
status = FindUnlinkedProcesses(Irp);
status = FindUnlinkedProcesses();
if (!NT_SUCCESS(status))
DEBUG_ERROR("FindUNlinekdProcesses failed with status %x", status);
@ -289,7 +347,7 @@ DeviceControl(
case IOCTL_VALIDATE_KPRCB_CURRENT_THREAD:
ValidateKPCRBThreads(Irp);
ValidateKPCRBThreads();
break;
@ -328,6 +386,14 @@ DeviceControl(
goto end;
}
status = ValidateIrpOutputBuffer(Irp, sizeof(SYSTEM_INFORMATION));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate IRP output buffer");
goto end;
}
Irp->IoStatus.Information = sizeof(SYSTEM_INFORMATION);
RtlCopyMemory(

View file

@ -33,4 +33,16 @@ DeviceCreate(
_Inout_ PIRP Irp
);
NTSTATUS
ValidateIrpOutputBuffer(
_In_ PIRP Irp,
_In_ ULONG RequiredSize
);
NTSTATUS
ValidateIrpInputBuffer(
_In_ PIRP Irp,
_In_ ULONG RequiredSize
);
#endif

View file

@ -2,6 +2,7 @@
#include "callbacks.h"
#include "driver.h"
#include "ioctl.h"
#define WHITELISTED_MODULE_TAG 'whte'
@ -698,6 +699,8 @@ HandleValidateDriversIOCTL(
PAGED_CODE();
NTSTATUS status;
PVOID buffer = NULL;
ULONG buffer_size = 0;
SYSTEM_MODULES system_modules = { 0 };
/* Fix annoying visual studio linting error */
@ -745,8 +748,18 @@ HandleValidateDriversIOCTL(
{
DEBUG_LOG("found INVALID drivers with count: %i", head->count);
PVOID buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(MODULE_VALIDATION_FAILURE_HEADER) +
MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT * sizeof(MODULE_VALIDATION_FAILURE), MODULES_REPORT_POOL_TAG);
buffer_size = sizeof(MODULE_VALIDATION_FAILURE_HEADER) +
MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT * sizeof(MODULE_VALIDATION_FAILURE);
status = ValidateIrpOutputBuffer(Irp, buffer_size);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate output buffer.");
goto end;
}
buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, buffer_size, MODULES_REPORT_POOL_TAG);
if (!buffer)
{
@ -815,6 +828,7 @@ HandleValidateDriversIOCTL(
DEBUG_LOG("No INVALID drivers found :)");
}
end:
ExFreePoolWithTag(head, INVALID_DRIVER_LIST_HEAD_POOL);
ExFreePoolWithTag(system_modules.address, SYSTEM_MODULES_POOL);
@ -873,6 +887,14 @@ AnalyseNmiData(
/* Make sure our NMIs were run */
if (!context->nmi_callbacks_run)
{
NTSTATUS status = ValidateIrpOutputBuffer(Irp, sizeof(NMI_CALLBACK_FAILURE));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate output buffer.");
return status;
}
NMI_CALLBACK_FAILURE report;
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
report.kthread_address = NULL;
@ -910,6 +932,14 @@ AnalyseNmiData(
if (flag == FALSE)
{
NTSTATUS status = ValidateIrpOutputBuffer(Irp, sizeof(NMI_CALLBACK_FAILURE));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate output buffer.");
return status;
}
/*
* Note: for now, we only handle 1 report at a time so we stop the
* analysis once we receive a report since we only send a buffer

View file

@ -661,9 +661,7 @@ CheckIfProcessAllocationIsInProcessList(
}
NTSTATUS
FindUnlinkedProcesses(
_Inout_ PIRP Irp
)
FindUnlinkedProcesses()
{
PAGED_CODE();

View file

@ -14,9 +14,7 @@ typedef struct _INVALID_PROCESS_ALLOCATION_REPORT
}INVALID_PROCESS_ALLOCATION_REPORT, * PINVALID_PROCESS_ALLOCATION_REPORT;
NTSTATUS
FindUnlinkedProcesses(
_Inout_ PIRP Irp
);
FindUnlinkedProcesses();
VOID
GetPsActiveProcessHead(

View file

@ -7,6 +7,7 @@
#include "queue.h"
#include "pool.h"
#include "thread.h"
#include "ioctl.h"
#include "common.h"
/*
@ -176,22 +177,29 @@ HandlePeriodicGlobalReportQueueQuery(
_Inout_ PIRP Irp
)
{
PVOID report = NULL;
INT count = 0;
GLOBAL_REPORT_QUEUE_HEADER header;
PVOID report_buffer = NULL;
PREPORT_HEADER report_header;
NTSTATUS status = STATUS_SUCCESS;
PVOID report = NULL;
SIZE_T total_size = NULL;
PVOID report_buffer = NULL;
ULONG report_buffer_size = 0;
PREPORT_HEADER report_header;
GLOBAL_REPORT_QUEUE_HEADER header;
KeAcquireGuardedMutex(&report_queue_config.lock);
report = QueuePop(&report_queue_config.head);
report_buffer_size = sizeof(INVALID_PROCESS_ALLOCATION_REPORT) * MAX_REPORTS_PER_IRP + sizeof(GLOBAL_REPORT_QUEUE_HEADER);
report_buffer = ExAllocatePool2(
POOL_FLAG_NON_PAGED,
sizeof(INVALID_PROCESS_ALLOCATION_REPORT) * MAX_REPORTS_PER_IRP + sizeof(GLOBAL_REPORT_QUEUE_HEADER),
REPORT_QUEUE_TEMP_BUFFER_TAG
);
status = ValidateIrpOutputBuffer(Irp, report_buffer_size);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Failed to validate Irp output buffer");
KeReleaseGuardedMutex(&report_queue_config.lock);
return status;
}
report_buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, report_buffer_size, REPORT_QUEUE_TEMP_BUFFER_TAG);
if (!report_buffer)
{
@ -199,6 +207,8 @@ HandlePeriodicGlobalReportQueueQuery(
return STATUS_MEMORY_NOT_ALLOCATED;
}
report = QueuePop(&report_queue_config.head);
if (report == NULL)
{
DEBUG_LOG("callback report queue is empty, returning");

View file

@ -71,9 +71,7 @@ KPRCBThreadValidationProcessCallback(
*
*/
VOID
ValidateKPCRBThreads(
_Inout_ PIRP Irp
)
ValidateKPCRBThreads()
{
PAGED_CODE();

View file

@ -25,9 +25,7 @@ typedef struct _ATTACH_PROCESS_REPORT
}ATTACH_PROCESS_REPORT, * PATTACH_PROCESS_REPORT;
VOID
ValidateKPCRBThreads(
_Inout_ PIRP Irp
);
ValidateKPCRBThreads();
VOID
DetectThreadsAttachedToProtectedProcess();