-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[dev] fix a side-loading vulnerability with cfgmgr32.dll
* Current Rufus and earlier versions (when compiled with MinGW) suffer from a side-loading vulnerability due to cfgmgr32.dll being attempted to be loaded from the same directory as the executable. This may result in someone being able to execute elevated malicious code if they already have gained user-level access to the platform and were able to drop an arbitrary cfgmgr32.dll in the same directory as rufus. * While we were able to address similar vulnerabilities using delay-loading, this method does not appear to work for MinGW with this specific DLL, so we remove all the implicit CM_ function calls, that result in automated DLL loading that cannot be mitigated, to replace them with direct DLL hooks, which are not subject to Windows' default (vulnerable) DLL lookup behaviour. We still add the def for the delay loading in case we manage to find how to delay load cfgmgr32 with MinGW in the future... * Fixes CVE-2025-26624 (GHSA-p8p5-r296-g2jv). * This vulnerability was discovered by @EmperialX working with @Shauryae1337 and reported by @EmperialX.
- Loading branch information
Showing
12 changed files
with
99 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
EXPORTS | ||
CM_Get_Device_IDA@16 | ||
CM_Get_Device_ID_List_SizeA@12 | ||
CM_Get_Device_ID_ListA@16 | ||
CM_Locate_DevNodeA@12 | ||
CM_Get_Child@12 | ||
CM_Get_Sibling@12 | ||
CM_Get_Parent@12 | ||
CM_Get_DevNode_Status@16 | ||
CM_Get_DevNode_Registry_PropertyA@24 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
/* | ||
* Rufus: The Reliable USB Formatting Utility | ||
* Device detection and enumeration | ||
* Copyright © 2014-2024 Pete Batard <[email protected]> | ||
* Copyright © 2014-2025 Pete Batard <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
|
@@ -49,6 +49,21 @@ extern RUFUS_DRIVE rufus_drive[MAX_DRIVES]; | |
extern BOOL enable_HDDs, enable_VHDs, use_fake_units, enable_vmdk, usb_debug; | ||
extern BOOL list_non_usb_removable_drives, its_a_me_mario; | ||
|
||
/* | ||
* CfgMgr32.dll interface. | ||
* Note that, unlike what is the case with other DLLs, delay-loading of cfgmgr32 | ||
* does *not* work with MinGW, so we have to go through direct hooking yet again... | ||
*/ | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, CHAR*, ULONG, ULONG)); | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_Device_ID_List_SizeA, (PULONG, PCSTR, ULONG)); | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_Device_ID_ListA, (PCSTR, PCHAR, ULONG, ULONG)); | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Locate_DevNodeA, (PDEVINST, DEVINSTID_A, ULONG)); | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_DevNode_Status, (PULONG, PULONG, DEVINST, ULONG)); | ||
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_DevNode_Registry_PropertyA, (DEVINST, ULONG, PULONG, PVOID, PULONG, ULONG)); | ||
|
||
/* | ||
* Get the VID, PID and current device speed | ||
*/ | ||
|
@@ -65,15 +80,18 @@ static BOOL GetUSBProperties(char* parent_path, char* device_id, usb_device_prop | |
if ((parent_path == NULL) || (device_id == NULL) || (props == NULL)) | ||
goto out; | ||
|
||
cr = CM_Locate_DevNodeA(&device_inst, device_id, 0); | ||
PF_INIT_OR_OUT(CM_Locate_DevNodeA, CfgMgr32); | ||
PF_INIT_OR_OUT(CM_Get_DevNode_Registry_PropertyA, CfgMgr32); | ||
|
||
cr = pfCM_Locate_DevNodeA(&device_inst, device_id, 0); | ||
if (cr != CR_SUCCESS) { | ||
uprintf("Could not get device instance handle for '%s': CR error %d", device_id, cr); | ||
goto out; | ||
} | ||
|
||
props->port = 0; | ||
size = sizeof(props->port); | ||
cr = CM_Get_DevNode_Registry_PropertyA(device_inst, CM_DRP_ADDRESS, NULL, (PVOID)&props->port, &size, 0); | ||
cr = pfCM_Get_DevNode_Registry_PropertyA(device_inst, CM_DRP_ADDRESS, NULL, (PVOID)&props->port, &size, 0); | ||
if (cr != CR_SUCCESS) { | ||
uprintf("Could not get port for '%s': CR error %d", device_id, cr); | ||
goto out; | ||
|
@@ -196,6 +214,8 @@ int CycleDevice(int index) | |
if ((index < 0) || (safe_strlen(rufus_drive[index].id) < 8)) | ||
return ERROR_INVALID_PARAMETER; | ||
|
||
PF_INIT_OR_OUT(CM_Get_DevNode_Status, CfgMgr32); | ||
|
||
// Need DIGCF_ALLCLASSES else disabled devices won't be listed. | ||
dev_info = SetupDiGetClassDevsA(&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES); | ||
if (dev_info == INVALID_HANDLE_VALUE) { | ||
|
@@ -218,7 +238,7 @@ int CycleDevice(int index) | |
found = TRUE; | ||
|
||
// Detect if the device is already disabled | ||
if (CM_Get_DevNode_Status(&dev_status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS) | ||
if (pfCM_Get_DevNode_Status(&dev_status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS) | ||
disabled = (dev_status & DN_HAS_PROBLEM) && (problem_code == CM_PROB_DISABLED); | ||
|
||
// Disable the device | ||
|
@@ -267,7 +287,7 @@ int CycleDevice(int index) | |
// successful, but leave the device in an actual disabled state... So we can end up | ||
// with zombie devices, that are effectively disabled, but that Windows still sees | ||
// as enabled... So we need to detect this. | ||
if (CM_Get_DevNode_Status(&dev_status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS) { | ||
if (pfCM_Get_DevNode_Status(&dev_status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS) { | ||
disabled = (dev_status & DN_HAS_PROBLEM) && (problem_code == CM_PROB_DISABLED); | ||
if (disabled) | ||
ret = ERROR_DEVICE_REINITIALIZATION_NEEDED; | ||
|
@@ -278,6 +298,7 @@ int CycleDevice(int index) | |
SetupDiDestroyDeviceInfoList(dev_info); | ||
if (!found) | ||
uprintf("Could not find a device to cycle!"); | ||
out: | ||
return ret; | ||
} | ||
|
||
|
@@ -473,7 +494,7 @@ BOOL GetDevices(DWORD devnum) | |
const char* windows_sandbox_vhd_label = "PortableBaseLayer"; | ||
// Hash table and String Array used to match a Device ID with the parent hub's Device Interface Path | ||
htab_table htab_devid = HTAB_EMPTY; | ||
StrArray dev_if_path; | ||
StrArray dev_if_path = STRARRAY_EMPTY; | ||
char letter_name[] = " (?:)"; | ||
char drive_name[] = "?:\\"; | ||
char setting_name[32]; | ||
|
@@ -497,6 +518,14 @@ BOOL GetDevices(DWORD devnum) | |
uint64_t drive_size = 0; | ||
usb_device_props props; | ||
|
||
PF_INIT_OR_OUT(CM_Get_Child, CfgMgr32); | ||
PF_INIT_OR_OUT(CM_Get_Parent, CfgMgr32); | ||
PF_INIT_OR_OUT(CM_Get_Sibling, CfgMgr32); | ||
PF_INIT_OR_OUT(CM_Get_Device_IDA, CfgMgr32); | ||
PF_INIT_OR_OUT(CM_Get_Device_ID_ListA, CfgMgr32); | ||
PF_INIT_OR_OUT(CM_Get_Device_ID_List_SizeA, CfgMgr32); | ||
PF_INIT_OR_OUT(CM_Locate_DevNodeA, CfgMgr32); | ||
|
||
IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList)); | ||
ClearDrives(); | ||
StrArrayCreate(&dev_if_path, 128); | ||
|
@@ -526,19 +555,19 @@ BOOL GetDevices(DWORD devnum) | |
if (SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) { | ||
|
||
// Find the Device IDs for all the children of this hub | ||
if (CM_Get_Child(&device_inst, dev_info_data.DevInst, 0) == CR_SUCCESS) { | ||
if (pfCM_Get_Child(&device_inst, dev_info_data.DevInst, 0) == CR_SUCCESS) { | ||
device_id[0] = 0; | ||
s = StrArrayAdd(&dev_if_path, devint_detail_data->DevicePath, TRUE); | ||
uuprintf(" Hub[%d] = '%s'", s, devint_detail_data->DevicePath); | ||
if ((s>= 0) && (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS)) { | ||
if ((s>= 0) && (pfCM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS)) { | ||
ToUpper(device_id); | ||
if ((k = htab_hash(device_id, &htab_devid)) != 0) { | ||
htab_devid.table[k].data = (void*)(uintptr_t)s; | ||
} | ||
uuprintf(" Found ID[%03d]: %s", k, device_id); | ||
while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) { | ||
while (pfCM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) { | ||
device_id[0] = 0; | ||
if (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS) { | ||
if (pfCM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS) { | ||
ToUpper(device_id); | ||
if ((k = htab_hash(device_id, &htab_devid)) != 0) { | ||
htab_devid.table[k].data = (void*)(uintptr_t)s; | ||
|
@@ -566,7 +595,7 @@ BOOL GetDevices(DWORD devnum) | |
// Also compute the uasp_start index | ||
if (strcmp(usbstor_name[s], "UASPSTOR") == 0) | ||
uasp_start = s; | ||
if (CM_Get_Device_ID_List_SizeA(&list_size[s], usbstor_name[s], ulFlags) != CR_SUCCESS) | ||
if (pfCM_Get_Device_ID_List_SizeA(&list_size[s], usbstor_name[s], ulFlags) != CR_SUCCESS) | ||
list_size[s] = 0; | ||
if (list_size[s] != 0) | ||
full_list_size += list_size[s]-1; // remove extra NUL terminator | ||
|
@@ -601,7 +630,7 @@ BOOL GetDevices(DWORD devnum) | |
for (s = 0, i = 0; s < ARRAYSIZE(usbstor_name); s++) { | ||
list_start[s] = i; | ||
if (list_size[s] > 1) { | ||
if (CM_Get_Device_ID_ListA(usbstor_name[s], &devid_list[i], list_size[s], ulFlags) != CR_SUCCESS) | ||
if (pfCM_Get_Device_ID_ListA(usbstor_name[s], &devid_list[i], list_size[s], ulFlags) != CR_SUCCESS) | ||
continue; | ||
if (usb_debug) { | ||
uprintf("Processing IDs belonging to '%s':", usbstor_name[s]); | ||
|
@@ -708,17 +737,17 @@ BOOL GetDevices(DWORD devnum) | |
// a lookup table, but there shouldn't be that many USB storage devices connected... | ||
// NB: Each of these Device IDs should have a child, from which we get the Device Instance match. | ||
for (device_id = devid_list; *device_id != 0; device_id += strlen(device_id) + 1) { | ||
if (CM_Locate_DevNodeA(&parent_inst, device_id, 0) != CR_SUCCESS) { | ||
if (pfCM_Locate_DevNodeA(&parent_inst, device_id, 0) != CR_SUCCESS) { | ||
uuprintf("Could not locate device node for '%s'", device_id); | ||
continue; | ||
} | ||
if (CM_Get_Child(&device_inst, parent_inst, 0) != CR_SUCCESS) { | ||
if (pfCM_Get_Child(&device_inst, parent_inst, 0) != CR_SUCCESS) { | ||
uuprintf("Could not get children of '%s'", device_id); | ||
continue; | ||
} | ||
if (device_inst != dev_info_data.DevInst) { | ||
// Try the siblings | ||
while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) { | ||
while (pfCM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) { | ||
if (device_inst == dev_info_data.DevInst) { | ||
uuprintf("NOTE: Matched instance from sibling for '%s'", device_id); | ||
break; | ||
|
@@ -759,8 +788,8 @@ BOOL GetDevices(DWORD devnum) | |
// for UASP devices in ASUS "Turbo Mode" or "Apple Mobile Device USB Driver" for iPods) | ||
// so try to see if we can match the grandparent. | ||
if ( ((uintptr_t)htab_devid.table[j].data == 0) | ||
&& (CM_Get_Parent(&grandparent_inst, parent_inst, 0) == CR_SUCCESS) | ||
&& (CM_Get_Device_IDA(grandparent_inst, str, MAX_PATH, 0) == CR_SUCCESS) ) { | ||
&& (pfCM_Get_Parent(&grandparent_inst, parent_inst, 0) == CR_SUCCESS) | ||
&& (pfCM_Get_Device_IDA(grandparent_inst, str, MAX_PATH, 0) == CR_SUCCESS) ) { | ||
device_id = str; | ||
method_str = "[GP]"; | ||
ToUpper(device_id); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
/* | ||
* Rufus: The Reliable USB Formatting Utility | ||
* Device detection and enumeration | ||
* Copyright © 2014-2019 Pete Batard <[email protected]> | ||
* Copyright © 2014-2025 Pete Batard <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
|
@@ -46,20 +46,6 @@ typedef struct usb_device_props { | |
/* | ||
* Windows DDK API definitions. Most of it copied from MinGW's includes | ||
*/ | ||
typedef DWORD DEVNODE, DEVINST; | ||
typedef DEVNODE *PDEVNODE, *PDEVINST; | ||
typedef DWORD RETURN_TYPE; | ||
typedef RETURN_TYPE CONFIGRET; | ||
typedef CHAR *DEVINSTID_A; | ||
|
||
#ifndef CM_GETIDLIST_FILTER_PRESENT | ||
#define CM_GETIDLIST_FILTER_PRESENT 0x00000100 | ||
#endif | ||
|
||
#ifndef FILE_DEVICE_USB | ||
#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN | ||
#endif | ||
|
||
typedef enum USB_CONNECTION_STATUS { | ||
NoDeviceConnected, | ||
DeviceConnected, | ||
|
@@ -77,26 +63,16 @@ typedef enum USB_HUB_NODE { | |
UsbMIParent | ||
} USB_HUB_NODE; | ||
|
||
/* Cfgmgr32.dll interface */ | ||
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Device_IDA(DEVINST dnDevInst, CHAR* Buffer, ULONG BufferLen, ULONG ulFlags); | ||
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Device_ID_List_SizeA(PULONG pulLen, PCSTR pszFilter, ULONG ulFlags); | ||
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Device_ID_ListA(PCSTR pszFilter, PCHAR Buffer, ULONG BufferLen, ULONG ulFlags); | ||
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Locate_DevNodeA(PDEVINST pdnDevInst, DEVINSTID_A pDeviceID, ULONG ulFlags); | ||
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Child(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags); | ||
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Parent(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags); | ||
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Sibling(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags); | ||
DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_DevNode_Status(PULONG pulStatus, PULONG pulProblemNumber, DEVINST dnDevInst, ULONG ulFlags); | ||
|
||
#define USB_HUB_CYCLE_PORT 273 | ||
#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 | ||
#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279 | ||
|
||
#define IOCTL_USB_HUB_CYCLE_PORT \ | ||
CTL_CODE(FILE_DEVICE_USB, USB_HUB_CYCLE_PORT, METHOD_BUFFERED, FILE_ANY_ACCESS) | ||
CTL_CODE(FILE_DEVICE_UNKNOWN, USB_HUB_CYCLE_PORT, METHOD_BUFFERED, FILE_ANY_ACCESS) | ||
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ | ||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) | ||
CTL_CODE(FILE_DEVICE_UNKNOWN, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) | ||
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \ | ||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS) | ||
CTL_CODE(FILE_DEVICE_UNKNOWN, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS) | ||
|
||
/* Most of the structures below need to be packed */ | ||
#pragma pack(push, 1) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.