Vulnerability: IDRIX, Truecrypt Veracrypt, Truecrypt Prior to 1.23-Hotfix-1 (Veracrypt), all versions (Truecrypt) is affected by: Buffer Overflow. The impact is: Minor information disclosure of kernel stack. The component is: Veracrypt NT Driver (veracrypt.sys). The attack vector is: Locally executed code, IOCTL request to driver. The fixed version is: 1.23-Hotfix-1.
Vulnerability ID: CVE-2019-1010208
CWE: CWE-119
Severity score: 2.9
Project: https://github.com/veracrypt/VeraCrypt
Published: 2019-07-23
File: src/Driver/Ntdriver.c
Commit message: Windows: fix low severity vulnerability in driver that allowed reading 3 bytes of kernel stack memory (with a rare possibility of 25 additional bytes). Reported by Tim Harrison.
Commit author: Mounir IDRASSI

Vulnerable version (before fix):
 NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex 			UNICODE_STRING FullFileName; 			IO_STATUS_BLOCK IoStatus; 			LARGE_INTEGER offset; 			byte readBuffer [TC_SECTOR_SIZE_BIOS];  			if (!ValidateIOBufferSize (Irp, sizeof (GetSystemDriveConfigurationRequest), ValidateInputOutput)) 				break;  			EnsureNullTerminatedString (request->DevicePath, sizeof (request->DevicePath)); 			RtlInitUnicodeString (&FullFileName, request->DevicePath);  NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex  			if (NT_SUCCESS (ntStatus)) 			{ 				// Determine if the first sector contains a portion of the VeraCrypt Boot Loader 				offset.QuadPart = 0;	// MBR  				ntStatus = ZwReadFile (NtFileHandle, 					NULL, 					NULL, 					NULL, 					&IoStatus, 					readBuffer, 					sizeof(readBuffer), 					&offset, 					NULL);  				if (NT_SUCCESS (ntStatus)) 				{ 					size_t i;  					// Check for dynamic drive 					request->DriveIsDynamic = FALSE;  					if (readBuffer[510] == 0x55 && readBuffer[511] == 0xaa) 					{ 						int i; 						for (i = 0; i < 4; ++i) 						{ 							if (readBuffer[446 + i * 16 + 4] == PARTITION_LDM) 							{ 								request->DriveIsDynamic = TRUE; 								break; 							} 						} 					}  					request->BootLoaderVersion = 0; 					request->Configuration = 0; 					request->UserConfiguration = 0; 					request->CustomUserMessage[0] = 0;  					// Search for the string "VeraCrypt" 					for (i = 0; i < sizeof (readBuffer) - strlen (TC_APP_NAME); ++i) 					{ 						if (memcmp (readBuffer + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0) 						{ 							request->BootLoaderVersion = BE16 (*(uint16 *) (readBuffer + TC_BOOT_SECTOR_VERSION_OFFSET)); 							request->Configuration = readBuffer[TC_BOOT_SECTOR_CONFIG_OFFSET];  							if (request->BootLoaderVersion != 0 && request->BootLoaderVersion <= VERSION_NUM) 							{ 								request->UserConfiguration = readBuffer[TC_BOOT_SECTOR_USER_CONFIG_OFFSET]; 								memcpy (request->CustomUserMessage, readBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH); 							} 							break; 						} 					}  					Irp->IoStatus.Status = STATUS_SUCCESS; 					Irp->IoStatus.Information = sizeof (*request); 				} 				else 				{ 					Irp->IoStatus.Status = ntStatus; 					Irp->IoStatus.Information = 0; 				}  				ZwClose (NtFileHandle);

Fixed version (after fix):
 NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex 			UNICODE_STRING FullFileName; 			IO_STATUS_BLOCK IoStatus; 			LARGE_INTEGER offset; 			size_t devicePathLen = 0;  			if (!ValidateIOBufferSize (Irp, sizeof (GetSystemDriveConfigurationRequest), ValidateInputOutput)) 				break;  			// check that request->DevicePath has the expected format "\\Device\\HarddiskXXX\\Partition0" 			if (	!NT_SUCCESS (RtlUnalignedStringCchLengthW (request->DevicePath, TC_MAX_PATH, &devicePathLen)) 				||	(devicePathLen < 28) // 28 is the length of "\\Device\\Harddisk0\\Partition0" which is the minimum 				||	(devicePathLen > 30) // 30 is the length of "\\Device\\Harddisk255\\Partition0" which is the maximum 				||	(memcmp (request->DevicePath, L"\\Device\\Harddisk", 16 * sizeof (WCHAR))) 				||	(memcmp (&request->DevicePath[devicePathLen - 11], L"\\Partition0", 11 * sizeof (WCHAR))) 				) 			{ 				Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; 				Irp->IoStatus.Information = 0; 				break; 			}  			EnsureNullTerminatedString (request->DevicePath, sizeof (request->DevicePath)); 			RtlInitUnicodeString (&FullFileName, request->DevicePath);  NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex  			if (NT_SUCCESS (ntStatus)) 			{ 				byte *readBuffer = TCalloc (TC_MAX_VOLUME_SECTOR_SIZE); 				if (!readBuffer) 				{ 					Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 					Irp->IoStatus.Information = 0; 				} 				else 				{ 					// Determine if the first sector contains a portion of the VeraCrypt Boot Loader 					offset.QuadPart = 0;	// MBR  					ntStatus = ZwReadFile (NtFileHandle, 						NULL, 						NULL, 						NULL, 						&IoStatus, 						readBuffer, 						TC_MAX_VOLUME_SECTOR_SIZE, 						&offset, 						NULL);  					if (NT_SUCCESS (ntStatus)) 					{ 						// check that we could read all needed data 						if (IoStatus.Information >= TC_SECTOR_SIZE_BIOS) 						{ 							size_t i;  							// Check for dynamic drive 							request->DriveIsDynamic = FALSE;  							if (readBuffer[510] == 0x55 && readBuffer[511] == 0xaa) 							{ 								int i; 								for (i = 0; i < 4; ++i) 								{ 									if (readBuffer[446 + i * 16 + 4] == PARTITION_LDM) 									{ 										request->DriveIsDynamic = TRUE; 										break; 									} 								} 							}  							request->BootLoaderVersion = 0; 							request->Configuration = 0; 							request->UserConfiguration = 0; 							request->CustomUserMessage[0] = 0;  							// Search for the string "VeraCrypt" 							for (i = 0; i < sizeof (readBuffer) - strlen (TC_APP_NAME); ++i) 							{ 								if (memcmp (readBuffer + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0) 								{ 									request->BootLoaderVersion = BE16 (*(uint16 *) (readBuffer + TC_BOOT_SECTOR_VERSION_OFFSET)); 									request->Configuration = readBuffer[TC_BOOT_SECTOR_CONFIG_OFFSET];  									if (request->BootLoaderVersion != 0 && request->BootLoaderVersion <= VERSION_NUM) 									{ 										request->UserConfiguration = readBuffer[TC_BOOT_SECTOR_USER_CONFIG_OFFSET]; 										memcpy (request->CustomUserMessage, readBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH); 									} 									break; 								} 							}  							Irp->IoStatus.Status = STATUS_SUCCESS; 							Irp->IoStatus.Information = sizeof (*request); 						} 						else 						{ 							Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; 							Irp->IoStatus.Information = 0; 						} 					} 					else 					{ 						Irp->IoStatus.Status = ntStatus; 						Irp->IoStatus.Information = 0; 					}  					TCfree (readBuffer); 				}  				ZwClose (NtFileHandle);

Patch:
patch: @@ -1902,11 +1902,24 @@ NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex
 			UNICODE_STRING FullFileName;
 			IO_STATUS_BLOCK IoStatus;
 			LARGE_INTEGER offset;
-			byte readBuffer [TC_SECTOR_SIZE_BIOS];
+			size_t devicePathLen = 0;
 
 			if (!ValidateIOBufferSize (Irp, sizeof (GetSystemDriveConfigurationRequest), ValidateInputOutput))
 				break;
 
+			// check that request->DevicePath has the expected format "\\Device\\HarddiskXXX\\Partition0"
+			if (	!NT_SUCCESS (RtlUnalignedStringCchLengthW (request->DevicePath, TC_MAX_PATH, &devicePathLen))
+				||	(devicePathLen < 28) // 28 is the length of "\\Device\\Harddisk0\\Partition0" which is the minimum
+				||	(devicePathLen > 30) // 30 is the length of "\\Device\\Harddisk255\\Partition0" which is the maximum
+				||	(memcmp (request->DevicePath, L"\\Device\\Harddisk", 16 * sizeof (WCHAR)))
+				||	(memcmp (&request->DevicePath[devicePathLen - 11], L"\\Partition0", 11 * sizeof (WCHAR)))
+				)
+			{
+				Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+				Irp->IoStatus.Information = 0;
+				break;
+			}
+
 			EnsureNullTerminatedString (request->DevicePath, sizeof (request->DevicePath));
 			RtlInitUnicodeString (&FullFileName, request->DevicePath);
 
@@ -1918,68 +1931,88 @@ NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex
 
 			if (NT_SUCCESS (ntStatus))
 			{
-				// Determine if the first sector contains a portion of the VeraCrypt Boot Loader
-				offset.QuadPart = 0;	// MBR
-
-				ntStatus = ZwReadFile (NtFileHandle,
-					NULL,
-					NULL,
-					NULL,
-					&IoStatus,
-					readBuffer,
-					sizeof(readBuffer),
-					&offset,
-					NULL);
-
-				if (NT_SUCCESS (ntStatus))
+				byte *readBuffer = TCalloc (TC_MAX_VOLUME_SECTOR_SIZE);
+				if (!readBuffer)
 				{
-					size_t i;
+					Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+					Irp->IoStatus.Information = 0;
+				}
+				else
+				{
+					// Determine if the first sector contains a portion of the VeraCrypt Boot Loader
+					offset.QuadPart = 0;	// MBR
 
-					// Check for dynamic drive
-					request->DriveIsDynamic = FALSE;
+					ntStatus = ZwReadFile (NtFileHandle,
+						NULL,
+						NULL,
+						NULL,
+						&IoStatus,
+						readBuffer,
+						TC_MAX_VOLUME_SECTOR_SIZE,
+						&offset,
+						NULL);
 
-					if (readBuffer[510] == 0x55 && readBuffer[511] == 0xaa)
+					if (NT_SUCCESS (ntStatus))
 					{
-						int i;
-						for (i = 0; i < 4; ++i)
+						// check that we could read all needed data
+						if (IoStatus.Information >= TC_SECTOR_SIZE_BIOS)
 						{
-							if (readBuffer[446 + i * 16 + 4] == PARTITION_LDM)
+							size_t i;
+
+							// Check for dynamic drive
+							request->DriveIsDynamic = FALSE;
+
+							if (readBuffer[510] == 0x55 && readBuffer[511] == 0xaa)
 							{
-								request->DriveIsDynamic = TRUE;
-								break;
+								int i;
+								for (i = 0; i < 4; ++i)
+								{
+									if (readBuffer[446 + i * 16 + 4] == PARTITION_LDM)
+									{
+										request->DriveIsDynamic = TRUE;
+										break;
+									}
+								}
 							}
-						}
-					}
 
-					request->BootLoaderVersion = 0;
-					request->Configuration = 0;
-					request->UserConfiguration = 0;
-					request->CustomUserMessage[0] = 0;
+							request->BootLoaderVersion = 0;
+							request->Configuration = 0;
+							request->UserConfiguration = 0;
+							request->CustomUserMessage[0] = 0;
 
-					// Search for the string "VeraCrypt"
-					for (i = 0; i < sizeof (readBuffer) - strlen (TC_APP_NAME); ++i)
-					{
-						if (memcmp (readBuffer + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0)
-						{
-							request->BootLoaderVersion = BE16 (*(uint16 *) (readBuffer + TC_BOOT_SECTOR_VERSION_OFFSET));
-							request->Configuration = readBuffer[TC_BOOT_SECTOR_CONFIG_OFFSET];
-
-							if (request->BootLoaderVersion != 0 && request->BootLoaderVersion <= VERSION_NUM)
+							// Search for the string "VeraCrypt"
+							for (i = 0; i < sizeof (readBuffer) - strlen (TC_APP_NAME); ++i)
 							{
-								request->UserConfiguration = readBuffer[TC_BOOT_SECTOR_USER_CONFIG_OFFSET];
-								memcpy (request->CustomUserMessage, readBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH);
+								if (memcmp (readBuffer + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0)
+								{
+									request->BootLoaderVersion = BE16 (*(uint16 *) (readBuffer + TC_BOOT_SECTOR_VERSION_OFFSET));
+									request->Configuration = readBuffer[TC_BOOT_SECTOR_CONFIG_OFFSET];
+
+									if (request->BootLoaderVersion != 0 && request->BootLoaderVersion <= VERSION_NUM)
+									{
+										request->UserConfiguration = readBuffer[TC_BOOT_SECTOR_USER_CONFIG_OFFSET];
+										memcpy (request->CustomUserMessage, readBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH);
+									}
+									break;
+								}
 							}
-							break;
+
+							Irp->IoStatus.Status = STATUS_SUCCESS;
+							Irp->IoStatus.Information = sizeof (*request);
+						}
+						else
+						{
+							Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+							Irp->IoStatus.Information = 0;
 						}
 					}
+					else
+					{
+						Irp->IoStatus.Status = ntStatus;
+						Irp->IoStatus.Information = 0;
+					}
 
-					Irp->IoStatus.Status = STATUS_SUCCESS;
-					Irp->IoStatus.Information = sizeof (*request);
-				}
-				else
-				{
-					Irp->IoStatus.Status = ntStatus;
-					Irp->IoStatus.Information = 0;
+					TCfree (readBuffer);
 				}
 
 				ZwClose (NtFileHandle);
path: src/Driver/Ntdriver.c