This post was written by Marcin Noga with contributions by Earl Carter and Martin Lee.

New vulnerabilities for old operating systems may not seem particularly interesting, until you consider the large number of legacy machines running outdated versions of Windows. Windows XP has reached its end of life, meaning that new vulnerabilities will not be patched. In this post we will show that a recent vulnerability can be used as a platform for exploiting Windows XP.

In October, Microsoft released a bulletin for a privilege escalation vulnerability in the FASTFAT driver that was released as:

MS14-063 -- Vulnerability in FAT32 Disk Partition Driver Could Allow Elevation of Privilege (2998579),  CVE-2014-4115.

Let me present some of the most interesting parts of the advisory and add some details from my own research.

When the bug kicks in…
In the advisory, Microsoft indicates that the following OS’s are vulnerable:

  • Microsoft Windows Server 2003 SP2
  • Vista SP2
  • Server 2008 SP2
    The Microsoft bulletin does not mention Windows XP, since Windows XP is no longer supported. According to my research, however, this vulnerability is also present in the Windows XP FASTFAT driver.

See the following video.

This vulnerability can be exploited on Windows XP SP3 using a malicious usb stick with a malformed FAT32 partition. Let’s examine the reaction when the USB is inserted into the system.

General Description
Vulnerable code existing in the FastFAT.sys driver can be exploited via an intentionally malformed FAT32 Boot sector delivered through a USB flash drive. If the field “Number of FATs” contains a value greater than 2, the driver will allocate insufficient memory. Further actions made by FastFAT.sys driver will then cause a pool overflow.

How it happens -- code analysis
The following is an example of a malformed FAT32 Boot Sector. The “Number of FATs” field is set to 119 (0x77 in hex), instead of the normal value of 2, making the boot sector malformed.

               Member         "Value (dec)" "Value (hex)" Size
" 00000000 struct BOOTSECTOR_FAT32"     {...}           00000200
" 00000000 int8 jmp[00000003]"                          00000003
" 00000003 char OemName[00000008]"     MSDOS5.0         00000008
" 0000000B struct BPB_FAT32" {...}                      00000035
" 0000000B uint16 BytesPerSector"    512     0200       00000002
" 0000000D int8 SectorsPerCluster"     1     01         00000001
" 0000000E uint16 ReservedSectors"    36     0024       00000002
" 00000010 int8 NumberOfFATs"        119     77         00000001
" 00000011 uint16 RootEntries"         0     0000       00000002
" 00000013 uint16 TotalSectors"        0     0000       00000002
" 00000015 int8 Media"                -8     F8         00000001
" 00000016 uint16 SectorsPerFAT"       0     0000       00000002
" 00000018 uint16 SectorsPerTrack"    63     003F       00000002
" 0000001A uint16 HeadsPerCylinder"  255     00FF       00000002
" 0000001C uint32 HiddenSectors"      63     0000003F   00000004
" 00000020 uint32 TotalSectorsBig" 80262     00013986   00000004
"++ FAT32 Section
" 00000024 uint32 SectorsPerFAT"     618     0000026A   00000004
" 00000028 uint16 Flags"               0     0000       00000002
" 0000002A uint16 Version"             0     0000       00000002
" 0000002C uint32 RootCluster"         2     00000002   00000004
" 00000030 uint16 InfoSector"          1     0001       00000002
" 00000032 uint16 BootBackupStart"     6     0006       00000002
" 00000034 int8 Reserved[0000000C]"                     0000000C
" 00000040 int8 DriveNumber"        -128     80         00000001
" 00000041 int8 Unused"                1     01         00000001
" 00000042 int8 ExtBootSignature"     41     29         00000001
" 00000043 uint32 SerialNumber"  3896654535  E8423AC7   00000004
" 00000047 char VolumeLabel[0000000B]"       "NO NAME " 0000000B
" 00000052 char FileSystem[00000008]"        "FAT32 "   00000008
" 0000005A blob BootCode[000001A6]"                     000001A6

Memory corruption is a common driver problem. Activation of a special pool will help us to locate the moment when a pool overflow starts to appear

verifier /volatile /flags 0x1 /adddriver fastfat.sys

After insertion of USB flash drive with a malformed FAT32 we can examine the crash dump that occurs due to insufficient memory allocation.

Crash dump info
kd> !analyze -v
DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION (d6)
N bytes of memory was allocated and more than N bytes are being referenced.
This cannot be protected by try-except.
When possible, the guilty driver's name (Unicode string) is printed on
the bugcheck screen and saved in KiBugCheckDriver.
Arguments:
Arg1: 8a011008, memory referenced
Arg2: 00000001, value 0 = read operation, 1 = write operation
Arg3: b7ae63da, if non-zero, the address which referenced memory.
Arg4: 00000000, (reserved)

Debugging Details:
 ------------------
 WRITE_ADDRESS:  8a011008 Special pool
FAULTING_IP:
 Fastfat!FatCommonWrite+444
 b7ae63da 8978fc          mov     dword ptr [eax-4],edi
MM_INTERNAL_CODE:  0
 IMAGE_NAME:  Fastfat.SYS
 DEBUG_FLR_IMAGE_TIMESTAMP:  48025b94
 MODULE_NAME: Fastfat
 FAULTING_MODULE: b7ada000 Fastfat
 DEFAULT_BUCKET_ID:  DRIVER_FAULT
 BUGCHECK_STR:  0xD6
 PROCESS_NAME:  System
TRAP_FRAME:  bacdf8c0 -- (.trap 0xffffffffbacdf8c0)
 ErrCode = 00000002
 eax=8a01100c ebx=8a7ece90 ecx=00186c00 edx=00000800 esi=89a14b18 edi=00004800
 eip=b7ae63da esp=bacdf934 ebp=bacdfab4 iopl=0         nv up ei ng nz ac pe cy
 cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010297
 Fastfat!FatCommonWrite+0x444:
 b7ae63da 8978fc          mov     dword ptr [eax-4],edi ds:0023:8a011008=????????
 Resetting default scope
LAST_CONTROL_TRANSFER:  from 8051cc4f to 804f8cb5
STACK_TEXT:
 bacdf848 8051cc4f 00000050 8a011008 00000001 nt!KeBugCheckEx+0x1b
 bacdf8a8 8054051c 00000001 8a011008 00000000 nt!MmAccessFault+0x8e7
 bacdf8a8 b7ae63da 00000001 8a011008 00000000 nt!KiTrap0E+0xcc
 bacdfab4 b7adab9a 89a14b18 8a7ece90 89c59020 Fastfat!FatCommonWrite+0x444
 bacdfaf8 804ee119 89c59020 8a7ece90 806d12a4 Fastfat!FatFsdWrite+0xad
 bacdfb08 8064d628 89ada170 00004000 89c59020 nt!IopfCallDriver+0x31
 bacdfb2c 804ef411 bacdfb68 bacdfd40 00000000 nt!IovCallDriver+0xa0
 bacdfb40 8050c497 89ada107 bacdfb68 bacdfbfc nt!IoSynchronousPageWrite+0xaf
 bacdfc24 8050ce3d e10ec820 e10ec828 e10ec828 nt!MiFlushSectionInternal+0x3bf
 bacdfc60 804e38a2 89c33d70 e10ec820 00000004 nt!MmFlushSection+0x1b5
 bacdfce8 804e3bc4 00001000 00000000 00000001 nt!CcFlushCache+0x386
 bacdfd2c 804e61ee 89da0290 8055b0c0 89da1da8 nt!CcWriteBehind+0xdc
 bacdfd74 80534c02 89da0290 00000000 89da1da8 nt!CcWorkerThread+0x126
 bacdfdac 805c6160 89da0290 00000000 00000000 nt!ExpWorkerThread+0x100
 bacdfddc 80541dd2 80534b02 00000000 00000000 nt!PspSystemThreadStartup+0x34
 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
STACK_COMMAND:  kb
FOLLOWUP_IP:
 Fastfat!FatCommonWrite+444
 b7ae63da 8978fc          mov     dword ptr [eax-4],edi
SYMBOL_STACK_INDEX:  3
 SYMBOL_NAME:  Fastfat!FatCommonWrite+444
 FOLLOWUP_NAME:  MachineOwner
 FAILURE_BUCKET_ID:  0xD6_VRF_Fastfat!FatCommonWrite+444
 BUCKET_ID:  0xD6_VRF_Fastfat!FatCommonWrite+444
 Followup: MachineOwner


Detailed analysis
To help debug driver memory issues, you can enable the special pool feature. This feature causes each memory allocation to be placed on a separate page. With the special pool active for fastfat.sys, you should begin executing the following when the overflow occurs,:

PAGE:0001C37A     movzx edx, word ptr [eax+90h]
PAGE:0001C381     movzx ecx, cx
PAGE:0001C384     imul    edx, ecx
PAGE:0001C387     mov     [ebp+BytesPerFat], edx
PAGE:0001C38D
PAGE:0001C38D loc_1C38D:
PAGE:0001C38D     mov     cl, [eax+96h]
PAGE:0001C393     cmp     cl, 2     ; cl = Number of FATs
PAGE:0001C396     jbe     short RegularFATsAmount
PAGE:0001C398     push    'itaF'     ; Tag
PAGE:0001C39D     movzx eax, cl
PAGE:0001C3A0     push    eax     ; Number of FATs used as pool size
PAGE:0001C3A1     push    11h     ; PoolType
PAGE:0001C3A3     call ds:__imp__ExAllocatePoolWithTag
PAGE:0001C3A9     mov     edi, eax
PAGE:0001C3AB     mov     eax, [ebp+VCB]
PAGE:0001C3AE     jmp     short loc_1C3B6
PAGE:0001C3B0 ; ------------------------------------------------------------------
PAGE:0001C3B0
PAGE:0001C3B0 RegularFATsAmount:
PAGE:0001C3B0     lea     edi, [ebp+var_174]
PAGE:0001C3B6
PAGE:0001C3B6 loc_1C3B6:
PAGE:0001C3B6     mov [ebp+unknow], edi
PAGE:0001C3BC     and     [ebp+counter], 0
PAGE:0001C3C3     cmp     byte ptr [eax+96h], 0
PAGE:0001C3CA     jbe     short loc_1C410
PAGE:0001C3CC     mov     ecx, [ebp+offsetToFirstFAT]
PAGE:0001C3CF     mov     edx, ecx
PAGE:0001C3D1     sub     edx, [ebp+StartingVbo]
PAGE:0001C3D4     lea     eax, [edi+0Ch]
PAGE:0001C3D7
PAGE:0001C3D7 fillArrayLoop:
PAGE:0001C3D7     mov edi, [ebp+offsetToFirstFAT]
PAGE:0001C3DA     mov     [eax-4], edi
PAGE:0001C3DD     mov     [eax-0Ch], ecx
PAGE:0001C3E0     and     dword ptr [eax-8], 0
PAGE:0001C3E4     mov     [eax], edx
PAGE:0001C3E6     mov     edi, [ebp+unknow3]
PAGE:0001C3EC     mov     [eax+4], edi
PAGE:0001C3EF     inc     [ebp+counter]
PAGE:0001C3F5     add     ecx, [ebp+BytesPerFat]
PAGE:0001C3FB     add     eax, 18h
PAGE:0001C3FE     mov     edi, [ebp+VCB]
PAGE:0001C401     movzx edi, byte ptr [edi+96h] ; to EDI goes Number of FATs
PAGE:0001C408     cmp     [ebp+counter], edi
PAGE:0001C40E     jb short fillArrayLoop


Note: When the number of FATs is bigger than 2, the pool allocated is simply the number of FATs (instead of the number of FATs times size of a FAT entry)

PAGE:0001C39D     movzx eax, cl
PAGE:0001C3A0     push    eax     ; Number of FATs used as pool size
PAGE:0001C3A1     push    11h     ; PoolType
PAGE:0001C3A3     call    ds:__imp__ExAllocatePoolWithTag

The Next loop appears and it iterates the number of times indicated by the number of FATs. In this loop, labeled as fillArrayLoop, the pool allocated by the code describe above is filled by an array of structures where the size of the structure equals 24 bytes (each structure consists of 6 DWORD’s). It Is easy to deduce that the iteration will lead to a pool overflow because during the pool allocation, the number of FATs was taken into account without including the size of structure element. It’s even easier to understand on pseudo code:

NumberOfFATs = *(_BYTE *)(VCB + 150);
 if ( NumberOfFATs <= 2u )
 {
 ptrPool = &v100;
 }
 else
 {
 ptrPool = ExAllocatePoolWithTag((POOL_TYPE)17, NumberOfFATs, 'itaF'); 
[BUG]//NumberOfFATs !!! instead of NumberOfFATs * sizeof(SomeStructure)=24
 v14 = VCB;
 }
 counter = 0;
 if ( *(_BYTE *)(v14 + 0x96) )
 {
 v18 = offsetToFirstFAT;
 unknow1 = offsetToFirstFAT - unknow2;
 v20 = (int)((char *)ptrPool + 12);
 do
 {
 *(_DWORD *)(v20 - 4) = offsetToFirstFAT;
 *(_DWORD *)(v20 - 12) = v18;
 *(_DWORD *)(v20 - 8) = 0;
 *(_DWORD *)v20 = unknow1;
 *(_DWORD *)(v20 + 4) = unknow3;
 ++counter;
 v18 += BytesPerFat;
 v20 += 24;
 }
 while ( counter < NumberOfFATs );
 }


Exploitability
After three iterations of the loop cycle, here is how this array looks in memory:

#1
00 48 00 00
00 00 00 00
00 48 00 00
00 08 00 00
00 02 00 00
ef ef ef ef <- not initialized
#2
00 1c 05 00
00 00 00 00
00 48 00 00
00 08 00 00
00 02 00 00
ef ef ef ef <- not initialized
#3
00 f0 09 00
00 00 00 00
00 48 00 00
00 08 00 00
00 02 00 00
ef ef ef ef <- not initialized

Which values at a particular offset are we able to control ?

Offset +0: (v20 - 12)

For the first iteration it’s value equals offsetToFirstFAT and is the result of adding:

(v20 - 12) = (v20 - 12) + BytesPerFat.

Note: BytesPerFAT equals : Bytes per sector(= 512) * Sectors per fat(= 618)  == 0x4D400

(316416)

Offset +4: (v20 - 8)

Constant 0

Offset + 8: (v20 - 4)
The value of this field always equals offsetToFirstFAT.

Note: offsetToFirstFAT : Bytes per sector * Reserved sectors == (0x4800)

Taking into account the information about how this bug is triggered and what portion of data we are able to control during page overflow this vulnerability appears to be hard to exploit. The most lucrative scenario for attacker would be to create a malformed FAT32 partition (that does not cause crashes after usb stick insertion) which contains an executable file with the possibility for the victim to run it.

When run, this executable, for example, would be responsible for kernel pool spraying. After that it would write any random data to the malformed partition, triggering at the same time the bug in FatCommonWrite. My observations show that when you create a nearly empty malformed partition containing a couple files, the bug only triggers when you open one of these files and try to save (write) modifications.

Even though this vulnerability seems to be hard to exploit, we should not disregard this vulnerability because history and recently Chris Evans’ write-up about off-by-one shows that any kind of scenario can become real in some circumstances.

Conclusion
This vulnerability is obviously a serious issue for users who are still running Windows XP. Unfortunately, due to Windows XP being no longer being supported, the vulnerability is not going to be patched. Therefore, users will need to upgrade to a newer version of Windows to be safe from this particular vulnerability.