New issue
Advanced search Search tips
Starred by 2 users
Status: Fixed
Owner:
Closed: Nov 15
Cc:



Sign in to add a comment
Windows Kernel multiple stack and pool memory disclosures into NTFS file system metadata
Project Member Reported by mjurczyk@google.com, Jul 7 Back to list
We have discovered that the NTFS.sys driver writes uninitialized kernel stack and pool memory into the internal structures of the file system, while mounting and operating on it. This may be of a security concern in cases where, for example, users share some files with each other via USB sticks or other storage media with NTFS-formatted volumes on them. While the victim would assume that they're only sharing intended data explicitly copied to the disk, they could also unknowingly share bits and pieces of sensitive/confidential information stored in the kernel, that just happened to reside in the memory area used by NTFS.sys while constructing internal file system structures.

Even more scary are leaks which don't require any human interaction and take place immediately when the volume is mounted. In this scenario, it could be possible to disclose kernel memory of a locked machine with physical access to a USB port, by repeatedly plugging in a flash drive (or a device which emulates one), waiting for the uninitialized memory to be written by the system, reading it back and re-mounting the disk.

We have implemented some dedicated logic in our Bochspwn system instrumentation to detect instances of such info leaks, only to find out that they do indeed take place in Windows. The table below summarizes our findings so far:

+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Symbol name                                | Memory type | Drive type       | System (Windows 7, 10) | Requires interaction |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Ntfs!NtfsInitializeReservedBuffer+0x20     | Pool        | System, external | Windows 7              | Yes                  |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Ntfs!LfsRestartLogFile+0x61e               | Pool        | System, external | Windows 7              | No                   |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Ntfs!NtfsAddAttributeAllocation+0xb16      | Pool        | System           | Windows 7, 10          | Yes                  |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| NTFS!NtfsDeleteAttributeAllocation+0x2f0   | Pool        | System           | Windows 7, 10          | Yes                  |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Ntfs!NtfsCheckpointVolume+0xdcd            | Pool        | System           | Windows 7, 10          | Yes                  |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Ntfs!NtfsWriteLog+0xf                      | Stack       | System, external | Windows 7, 10          | No                   |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Ntfs!NtfsCreateAttributeWithAllocation+0xf | Stack       | System, external | Windows 7, 10          | No                   |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Ntfs!NtfsAddAttributeAllocation+0xf        | Stack       | System, external | Windows 7, 10          | Yes                  |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+
| Ntfs!NtfsDeleteAttributeAllocation+0xf     | Stack       | System, external | Windows 7, 10          | Yes                  |
+--------------------------------------------+-------------+------------------+------------------------+----------------------+

The symbols and offsets correspond to the latest build of Ntfs.sys on Windows 7 32-bit, and indicate the location in code of where the leaked allocation is requested (or the function prologue in case of stack-based disclosures). We examined data written to both the system drive (C:\) and an external, slave drive. We tested Windows 7 and 10, but didn't look into Windows 8. As part of testing, we booted up the OS and performed some simple operations on the NTFS volumes: created files and directories, removed them, created symbolic links, directory links, junction points, compressed files, encrypted files and changed their security attributes.

Please note that the table contains information that we were able to observe, but it's not necessarily complete and accurate. For example, it's possible that some of the leaks can also be triggered on a non-system drives, or without user interaction.

As clearly seen, uninitialized memory from a number of origins in Ntfs.sys is saved to disk. From a security perspective, the leak of pool memory allocated from Ntfs!LfsRestartLogFile is the most severe one, as it is triggered with no user interaction (just by mounting an NTFS volume), and it discloses ~7.5kB of kernel memory in 2 chunks of ~3800 consecutive bytes. This data is saved in the first 8192 bytes of the special $LogFile file, as part of the two "RSTR" restart area records. As we understand it, this means that every NTFS-formatted USB stick last accessed by Windows 7 now includes more than 7kB of junk kernel memory from that computer. However, since $LogFile is an internal file, reading from it requires raw disk access which significantly reduces the impact of the bug.

Interestingly, Windows 8 and 10 are not affected by the LfsRestartLogFile flaw, as these newer systems include an extra memset() call immediately following the allocation request:

--- cut ---
PAGE:000D3D2A                 push    'rsfL'          ; Tag
PAGE:000D3D2F                 imul    eax, [edi+18h], 22h
PAGE:000D3D33                 shr     eax, 1
PAGE:000D3D35                 push    eax             ; NumberOfBytes
PAGE:000D3D36                 push    210h            ; PoolType
PAGE:000D3D3B                 mov     esi, ds:__imp__ExAllocatePoolWithTag@12 ; ExAllocatePoolWithTag(x,x,x)
PAGE:000D3D41                 call    esi ; ExAllocatePoolWithTag(x,x,x)
PAGE:000D3D43                 mov     [edi+198h], eax
PAGE:000D3D49                 push    dword ptr [edi+18h] ; size_t
PAGE:000D3D4C                 push    ebx             ; int
PAGE:000D3D4D                 push    eax             ; void *
PAGE:000D3D4E                 call    _memset
--- cut ---

This appears to be another instance of a bug fixed by Microsoft in recent system versions, but not backported to the older, yet still supported ones.

This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available, the bug report will become visible to the public.
 
Project Member Comment 1 by mjurczyk@google.com, Jul 8
Labels: MSRC-39518
Project Member Comment 2 by mjurczyk@google.com, Sep 4
Labels: -Reported-2017-Jul-7 Reported-2017-Aug-25
A summary of communications with MSRC regarding this case so far is as follows:

7/7/2017  We send the bug report to secure@microsoft.com.
7/7/2017  MSRC acknowledge the receipt of the report.
7/18/2017 MSRC request more information, preferably full call stacks of both where the leaked memory is allocated and where it is leaked to disk.
7/19/2017 We reply that we don't have this information available at hand and will be out of office until August 7th. We also ask if detailed information about all leaks is needed, or just a few specific ones.
7/28/2017 MSRC inform us that they would like to use the grace period extension to align with the October Patch Tuesday on 10/10.
8/10/2017 We acknowledge the grace period extension.
8/17/2017 MSRC request call stacks of where the leaks are detected for all reported leaks.
8/18/2017 We reply that the algorithm we're using to detect the leaks doesn't make it easy to reveal full stack traces by design, but we'll look into ways of acquiring this information.
8/25/2017 We send an update informing MSRC that we're unable to obtain the call stacks of where leaked data is written to disk, but we managed to get layers #1 and #2 of the allocation call stacks (i.e. the ancestors of functions allocating leaked memory, and their ancestors). The new information is shown below.
8/31/2017 MSRC ask if they'll have more time beyond the 10/10 Patch Tuesday considering the delay in receiving the more detailed information.
8/31/2017 We agree that the update may be essential for identifying all instances of the leaks and fixing them properly, and we decide to reset the Reported date the 8/25/2017, when the new data was sent. There is one exception for which we're upholding the original deadline (the pool leak from Ntfs!LfsRestartLogFile into $LogFile), because it was discussed in detail in the first report, and the update didn't really add any new information with regards to it (the function only has one direct ancestor and pre-ancestor).
9/2/2017  MSRC acknowledges the above.
9/4/2017  A separate  issue #1352  is created for the Ntfs!LfsRestartLogFile leak, and updates are applied here accordingly.

The more detailed tables of callers provided on 8/25/2017 are shown below (specific to Windows 7 32-bit):

+--------------------------------------------+------------------------------------------+
|                Stack leaks                 |                Pool leaks                |
+--------------------------------------------+------------------------------------------+
| Ntfs!NtfsDeleteAttributeAllocation+0xf     | Ntfs!LfsRestartLogFile+0x61e             |
| Ntfs!NtfsWriteLog+0xf                      | Ntfs!NtfsAddAttributeAllocation+0xb16    |
| Ntfs!NtfsAddAttributeAllocation+0xf        | Ntfs!NtfsCheckpointVolume+0xdcd          |
| Ntfs!NtfsCreateAttributeWithAllocation+0xf | Ntfs!NtfsDeleteAttributeAllocation+0x12d |
|                                            | Ntfs!CreateAttributeList+0x1c            |
|                                            | Ntfs!NtfsCreateMdlAndBuffer+0x95         |
|                                            | Ntfs!NtfsInitializeReservedBuffer+0x20   |
+--------------------------------------------+------------------------------------------+

+----------------------------------------+---------------------------------------------------+
|              #0 functions              |     Corresponding callers (#1 in stack trace)     |
+----------------------------------------+---------------------------------------------------+
| Ntfs!NtfsDeleteAttributeAllocation     | Ntfs!NtfsDeleteAllocationInternal+0x39f           |
|                                        | Ntfs!TxfMoveAttrToDifferentFile+0x40d             |
| Ntfs!NtfsWriteLog                      | Ntfs!NtfsCommitCurrentTransaction+0x51            |
|                                        | Ntfs!NtfsWriteLog+0x33e                           |
|                                        | Ntfs!NtfsWriteLog+0x420                           |
|                                        | Ntfs!NtfsCheckpointVolume+0xed4                   |
|                                        | Ntfs!NtfsCheckpointVolume+0xf2f                   |
|                                        | Ntfs!NtfsCheckpointVolume+0xf89                   |
|                                        | Ntfs!NtfsCheckpointVolume+0x1002                  |
|                                        | Ntfs!NtfsAddPreallocatedStructToRollbackList+0x2c |
| Ntfs!NtfsAddAttributeAllocation        | Ntfs!NtfsAddAllocation+0x3f5                      |
|                                        | Ntfs!NtfsDeleteAllocationInternal+0x549           |
|                                        | Ntfs!NtfsReallocateRange+0x24d                    |
| Ntfs!NtfsCreateAttributeWithAllocation | Ntfs!NtfsAllocateAttribute+0x283                  |
| Ntfs!LfsRestartLogFile                 | Ntfs!LfsOpenLogFile+0x91                          |
| Ntfs!NtfsCheckpointVolume              | Ntfs!NtfsCheckpointAllVolumesWorker+0x3b          |
| Ntfs!CreateAttributeList               | Ntfs!SplitFileRecord+0x416                        |
|                                        | Ntfs!MoveAttributeToOwnRecord+0x59e               |
| Ntfs!NtfsCreateMdlAndBuffer            | Ntfs!NtfsAllocateCompressionBuffer+0x71           |
| Ntfs!NtfsInitializeReservedBuffer      | Ntfs!DriverEntry+0xd8                             |
+----------------------------------------+---------------------------------------------------+

+----------------------------------------------+-------------------------------------------------+
|                 #1 functions                 |    Corresponding callers (#2 in stack trace)    |
+----------------------------------------------+-------------------------------------------------+
| Ntfs!NtfsDeleteAllocationInternal            | Ntfs!NtfsDeleteAllocation+0x3f4                 |
| Ntfs!TxfMoveAttrToDifferentFile              | Ntfs!TxfMoveAttrInternal+0x88                   |
| Ntfs!NtfsCommitCurrentTransaction            | Ntfs!NtfsExtendedCompleteRequestInternal+0x29   |
|                                              | Ntfs!NtfsCleanupTransaction+0x5a                |
|                                              | Ntfs!NtfsCheckpointCurrentTransaction+0x21      |
|                                              | Ntfs!NtfsAbortTransaction+0x2f6                 |
| Ntfs!NtfsWriteLog                            | Ntfs!PushIndexRoot+0x27f                        |
|                                              | Ntfs!DeleteSimple+0x1fe                         |
|                                              | Ntfs!NtOfsPutData+0x404                         |
|                                              | Ntfs!NtfsAllocateRecord+0x59a                   |
|                                              | Ntfs!NtfsUpdateFileNameInIndex+0x11a            |
|                                              | Ntfs!NtfsUpdateFileNameInIndex+0x36e            |
|                                              | Ntfs!InsertSimpleAllocation+0xd0                |
|                                              | Ntfs!NtfsChangeAttributeValue+0xc20             |
|                                              | Ntfs!NtfsAllocateBitmapRun+0xde                 |
|                                              | Ntfs!InsertWithBufferSplit+0x32b                |
|                                              | Ntfs!NtfsWriteFileSizes+0x3ec                   |
| Ntfs!NtfsCheckpointVolume                    | Ntfs!NtfsCheckpointAllVolumesWorker+0x3b        |
| Ntfs!NtfsAddPreallocatedStructToRollbackList | Ntfs!NtfsAddStructToRollbackList+0x5f           |
| Ntfs!NtfsAddAllocation                       | Ntfs!NtfsAddAllocationForNonResidentWrite+0x133 |
|                                              | Ntfs!NtfsExtendDataStream+0x19e                 |
|                                              | Ntfs!NtfsSetEndOfFileInfo+0x509                 |
|                                              | Ntfs!NtfsChangeAttributeCompression+0x183       |
|                                              | Ntfs!NtfsExtendDataStream+0x19e                 |
| Ntfs!NtfsReallocateRange                     | Ntfs!NtfsPrepareComplexBuffers+0x5c7            |
| Ntfs!NtfsAllocateAttribute                   | Ntfs!NtfsCreateNonresidentWithValue+0x139       |
| Ntfs!LfsOpenLogFile                          | Ntfs!NtfsStartLogFile+0xa3                      |
| Ntfs!NtfsCheckpointAllVolumesWorker          | Ntfs!NtfsForEachVcb+0x10c                       |
| Ntfs!SplitFileRecord                         | Ntfs!MakeRoomForAttribute+0x1a1                 |
| Ntfs!MoveAttributeToOwnRecord                | ?                                               |
| Ntfs!NtfsAllocateCompressionBuffer           | Ntfs!NtfsPrepareComplexBuffers+0xeb             |
| Ntfs!NtfsAllocateRecord                      | Ntfs!NtfsAllocateMftRecord+0xe5                 |
+----------------------------------------------+-------------------------------------------------+
Project Member Comment 3 by mjurczyk@google.com, Oct 3
Labels: -MSRC-39518 MSRC-40537
Project Member Comment 4 by mjurczyk@google.com, Nov 15
Labels: Fixed-2017-Nov-14 CVE-2017-11880
Status: Fixed
Fixed in yesterday's Patch Tuesday.
Project Member Comment 5 by mjurczyk@google.com, Nov 21
Labels: -Restrict-View-Commit
Sign in to add a comment