Today we released several fixes on MS10-048 affecting the win32k.sys kernel component. The most severe vulnerability allows a local user to perform an authenticated elevation of privileges, with no possible remote vector.

 

This update also includes several “Defense in Depth” measures that correct potential integer overflows in unrealistic scenarios. In this blog post we are going to walk you through these vulnerabilities to help explain the technical reasoning behind the DiD rating.

 

In order to understand these issues we first need to understand the source code involved. The win32k.sys component, among other things, is responsible for handling and forwarding windows messages. Before handling or forwarding them, win32k.sys will validate the messages and their parameters: probing, fetching from user mode, validating sizes, pointers, etc…

 

While handling a certain type of request, and before forwarding it to the top windows on the desktop, several potential integer overflows can happen that would bypass the before mentioned checks. All these are similar to the one that we will focus on:

 

             case DBT_DEVTYP_PORT:

                pPortW = (PDEV_BROADCAST_PORT_W)lParam;

                if ((1+wcslen(pPortW->dbcp_name))*sizeof(WCHAR) + FIELD_OFFSET(DEV_BROADCAST_PORT_W, dbcp_name) > cbSize) {

                    MSGERRORCLEANUP(0);

                }

                break;

 

 

An attacker could control the value of pPortW->dbcp_name, which is already probed and captured locally in kernel space.

 

On 32 bit systems an integer overflow can happen if wcslen() returns more than 2^311.  This would require having a 2^321 byte string captured in kernel mode plus the same one in user mode, 2^33 in total. This is not possible on current 32bit systems since with this 32bit range you can only have 2^32 bytes of virtual memory per process shared by user mode and kernel mode

 

However, that still leaves 64 bit systems.  In order to see if this could be exploitable on 64 bit system we will go to the assembly level.

 

                0:000> u fffff97f`ff07474c

                fffff97f`ff07474c 488d7b0c        lea     rdi,[rbx+0Ch]

                fffff97f`ff074750 33c0                xor     eax,eax

                fffff97f`ff074752 4883c9ff         or      rcx,0FFFFFFFFFFFFFFFFh

                fffff97f`ff074756 66f2af             repne scas word ptr [rdi] <- inlined wcslen

                fffff97f`ff074759 48f7d1            not     rcx <- rcx contains the length

                fffff97f`ff07475c 488d4c090c   lea     rcx,[rcx+rcx+0Ch] <- this does not overflow unless you have a really big (length ~ 2^63 )

                fffff97f`ff074761 493bcc          cmp     rcx,r12 <- compare against cbSize…

                fffff97f`ff074764 7609            jbe     fffff97f`ff07476f

                0:000>

 

The comparison from the above source code is up-casted on 64 bit systems. Again, the attacker needs a really big string in user space (plus the local copy at kernel space).  That’s 2^65 bytes of data and is not possible on current 64bit systems either.

 

So, why are we fixing it? If there is a future recompilation or source change and the comparison is down-casted to 32 bits this could potentially be exploitable on 64 bit systems. Adding that to the fact that we have fixes in the close area makes this a worthy effort.

 

-Fermin J. Serna, MSRC Engineering

 

1 These two values are rounded up.  Technically the value that must be bypassed is a few bytes less.  However, this does not change the overall argument as the size of the string is too large to be allocated on the system.