0-day Windows XP SP3 Denial of Service (CSRSS Crash)

A rather short blog post today, as I am currently on my vacations. After publishing two, quite extensive write-ups regarding vulnerabilities in the Windows “CSRSS” component at Microsoft July Patch Tuesday:

I would like to shortly discuss the details about another bug in the Windows Subsystem, which was NOT patched due to low severity, and can be used to force a reboot of a Windows-driven machine. The result can be accomplished by exploiting a flaw in the winsrv!SrvGetConsoleTitle routine – a member of the Console Management services’ group. All Windows NT-family system editions up to Windows XP / 2003 are affected; on Windows 7, making use of the bug would crash the corresponding CONHOST.EXE process, at most. Even though it is also theoretically possible to turn the issue into an “Information Disclosure” class, we consider it highly unlikely to avoid an unhandled exception during the exploitation process.

Note: Upon publishing the CVE-2011-1281 (AllocConsole EOP) article, a few people contacted me asking for some minor advices related to the vulnerability exploitation. One of them – a French security researcher Mysterie – managed to create a fully functional exploit and even made it available to the public audience. If you’re interested, see his post and PoC source code.

Vulnerability Details

As previously mentioned, the considered security issue resides in winsrv!SrvGetConsoleTitle, a CSRSS operation handler associated with the official kernel32!GetConsoleTitle API. The routine’s functionality is implied by its name: it should be used to retrieve the current console title (a single string, displayed on the console window title bar). The function takes two parameters: a pointer to the output buffer, and the size of the buffer. Simple enough, so far :-)

After a short investigation of the flawed function, we can see the following assembly snippet:

.text:75B328DF                 push    edi
.text:75B328E0                 push    1
.text:75B328E2                 push    dword ptr [esi+LPC_MSG.nSize]
.text:75B328E5                 lea     edi, [esi+LPC_MSG.lpConsoleTitle]
.text:75B328E8                 push   edi
.text:75B328E9                 push    esi
.text:75B328EA                 call    ds:__imp__CsrValidateMessageBuffer@16; CsrValidateMessageBuffer(x,x,x,x)
.text:75B328F0                 test    al, al
.text:75B328F2                 jz      loc_75B3F50D
.text:75B328F8                 cmp     byte ptr [esi+34h], 0
.text:75B328FC                 jz      loc_75B3F517
.text:75B32902                 mov     edx, [ebp+msg]
.text:75B32905                 mov     ax, [edx+CONSOLE.TitleLength]
.text:75B3290C                 cmp     [esi+LPC_MSG.nSize], ax
.text:75B32910                 jbe     short outputLengthLessThanActual
.text:75B32912                 movzx   eax, ax
.text:75B32915                 mov     [esi+LPC_MSG.nSize], eax
.text:75B32918
.text:75B32918 outputLengthLessThanActual:             ; CODE XREF:SrvGetConsoleTitle(x,x)+4Aj
.text:75B32918                 mov     ecx, [esi+LPC_MSG.nSize]
.text:75B3291B                 mov     esi, [edx+CONSOLE.Title]
.text:75B32921                 mov     edi,[edi]
.text:75B32923                 mov     eax, ecx
.text:75B32925                 shr     ecx, 2
.text:75B32928                 rep movsd
.text:75B3292A                 mov     ecx, eax
.text:75B3292C                 and     ecx,3
.text:75B3292F                 rep movsb

The above listing is equivalent to the following C-like pseudocode:

  if(!CsrValidateMessageBuffer(msg, &msg->lpConsoleTitle, msg->nSize, sizeof(BYTE))
  {
    // Bail out with STATUS_INVALID_PARAMETER;
  }

  if(msg->UnicodeFlag)
  {
    if((USHORT)msg->nSize > Console->TitleLength)
    {
      msg->nSize = Console->TitleLength;
    }
    memcpy(msg->lpConsoleTitle, Console->Title, msg->nSize);
  }

A single glimpse is enough to spot the apparent error – a 16-bit truncated output buffer size is used during the comparison with the actual title length; after that, a full 32-bit number is used as the “memcpy” function operand. Therefore, it should be possible to get CSRSS to copy more bytes than desired, by setting the nSize parameter to a value larger than the current title, but with the lower 16 bits below the string length (e.g. nSize = 0x10002). However, if you simply try to call:

#define CONSOLE_TITLE_LENGTH (0x10002)
CHAR ConsoleTitle[CONSOLE_TITLE_LENGTH];

GetConsoleTitle(lpConsoleTitle, CONSOLE_TITLE_LENGTH);

the operating system will definitely not crash. Why?

The reason of this specific behavior, is the fact that the “msg->nSize” value is used to validate the correctness of the msg->lpConsoleTitle pointer. Since the output of the funtion can be as large as tens of kilobytes, the output buffer is expected to be stored inside the shared client-csrss section. The section – created during the process initialization – has a constant size of 0x10000, hard-coded in the ntdll!CsrpConnectToServer routine:

.text:7C92291F mov [ebp+var_38], 2
.text:7C922926 mov [ebp+var_34], 1
.text:7C92292A mov [ebp+var_33], 1
.text:7C92292E mov [ebp+var_24], 10000h
.text:7C922935 mov [ebp+var_20], ebx
.text:7C922938 call _NtCreateSection@28 ; NtCreateSection(x,x,x,x,x,x,x)

Consequently, it is not possible to use the shared heap for a valid allocation of 0x10000 or more bytes. In order to address the limitation, one can close the CSRSS connection at run-time (primarily through closing the (A)LPC port handle, established during PE loading), and re-connect to the server, specifying a custom-sized shared section. The above is made possible thanks to the fact that CSRSS performs no validation regarding the size of the shared section, whatsoever.

After following the above steps, a potential attacker can specify an overlong output buffer as the kernel32!GetConsoleTitle parameter, and either read an excessive amount of CSRSS heap bytes following the original console title string, or generate an unhandled READ Access Violation exception.

An example crash log from Windows XP SP3 32-bit is as follows:

 *** An Access Violation occurred in C:\WINDOWS\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16:

The instruction at 75B62928 tried to read from an invalid address, 01148000

 *** enter .exr 006AFBE0 for the exception record
 ***  enter .cxr 006AFBFC for the context
 *** then kb to get the faulting stack

Break instruction exception - code 80000003 (first chance)
001b:7c90120e cc              int     3

kd> kb
ChildEBP RetAddr  Args to Child              
006afa38 7c9652ae 006afaec 00000000 00000001 ntdll!DbgBreakPoint
006afa78 7c9659c1 006afaec 7c9659c6 006afac4 ntdll!RtlUnhandledExceptionFilter2+0x27b
006afa88 75b4324c 006afaec 00000000 00000000 ntdll!RtlUnhandledExceptionFilter+0x12
006afac4 75b44aea 006afaec 75b468b1 006afaf4 CSRSRV!CsrUnhandledExceptionFilter+0x48
006afacc 75b468b1 006afaf4 00000001 006afaf4 CSRSRV!CsrApiRequestThread+0x4d4
006afaf4 7c9032a8 006afbe0 006affe4 006afbfc CSRSRV!_except_handler3+0x61
006afb18 7c90327a 006afbe0 006affe4 006afbfc ntdll!ExecuteHandler2+0x26
006afbc8 7c90e46a 00000000 006afbfc 006afbe0 ntdll!ExecuteHandler+0x24
006afbcc 00000000 006afbfc 006afbe0 006afbfc ntdll!KiUserExceptionDispatcher+0xe

kd> .exr 006AFBE0
ExceptionAddress: 75b62928 (winsrv!SrvGetConsoleTitle+0x00000065)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 01148000
Attempt to read from address 01148000

kd> .cxr 006AFBFC
eax=00040002 ebx=00000026 ecx=0000ed22 edx=004f23b0 esi=01148000 edi=0018b134
eip=75b62928 esp=006afec8 ebp=006afed0 iopl=3         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00013217
winsrv!SrvGetConsoleTitle+0x65:
001b:75b62928 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

And… that’s pretty much it! To those who are spending their week on BlackHat / DEFCON – have fun! I am going to drop some details about one or more CSRSS issues, as long as you’re not completely fed up with the Windows Subsystem posts :-)

Take care,

2 thoughts on “0-day Windows XP SP3 Denial of Service (CSRSS Crash)”

  1. “All Windows NT-family system editions up to Windows XP / 2003 are affected; on Windows 7, making use of the bug would crash the corresponding CONHOST.EXE process, at most. ”
    What about Vista?

Comments are closed.