Talos Vulnerability Report


Microsoft Remote Desktop Services (RDP7) Windows XP Multiple Information Leak Vulnerabilities

December 10, 2019
CVE Number



Exploitable information leak vulnerabilities exists in the RDP7 implementation of Microsoft's Remote Desktop Services on Windows XP. Various aspects of the T.128 protocol, such as capability negotiation, can cause an information leak, which can provide an attacker information about the target's address-space. An attacker can simply negotiate capabilities with the target via T.128 in order to trigger these vulnerabilities.

Tested Versions

Microsoft's Remote Desktop Services -- Windows XP (only):

RDPWD.sys 5.1.2600.5512
termdd.sys 5.1.2600.5512

Product URLs


CVSSv3 Score

5.3 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N


CWE-201: Information Exposure Through Sent Data


Remote Desktop Services allow a user or administrator to take control of a remote computer over a network connection. This allows the user to utilize a familiar graphical user interface to interact with said remote machine that provides a thin-client architecture on the Windows platform. These capabilities are accomplished using the Remote Desktop Protocol which is built on top of the X.224, T.124, T.125, T.128 protocols. Remote Desktop Services is a common service within the enterprise and is commonly used as a workaround on a network with otherwise minimalistic remote administration capabilities.

When a client initializes a connection via the RDP protocol, the client must first go through a number of stages in order to setup each of the individual layers that are used by the RDS platform. After performing all of the required negotiations, the client will then perform a security exchange which is required to complete the initialization of the T.128 protocol which will provide the Multipoint Application Sharing as mentioned in the T.128 specification. After performing this security exchange, the client will then need to negotiate capabilities with the remote server. This is done by first receiving a packet from the server containing the supported capabilities, followed by a response from the client with the capabilities that it will use. During the server's construction of the individual capabilities, the server does not properly initialize the related structures prior to sending them to the client. Thus when announcing these capabilities to a client, the server will then mistakenly leak information with these packets.

After completing the requirements of the T.125 protocol, the service will begin to setup its state in order to communicate over the T.128 protocol. This is done by calling the WDWNewShareClass function from the WDWConnect function. This will then instantiate a ShareClass object by first allocating memory for it at [1] and then calling its constructor at [2]. Upon returning to its caller, it will complete preparation of the object by calling the SM_Init function [3]. Once these functions have properly setup the ShareClass object, the driver may then send encrypted data to a client over the T.128 layer.

f1b2ad66 53              push    ebx
f1b2ad67 e8861a0000      call    RDPWD!WDWNewShareClass (f1b2c7f2)  ; \
f1b2ad6c 8bf0            mov     esi,eax
f1b2ad6e 85f6            test    esi,esi
f1b2ad70 0f8cd4fdffff    jl      RDPWD!WDWConnect+0x56 (f1b2ab4a)
f1b2c7f2 8bff            mov     edi,edi
f1b2c7f4 55              push    ebp
f1b2c7f5 8bec            mov     ebp,esp
f1b2c7f7 56              push    esi
f1b2c7f8 57              push    edi
f1b2c7f9 68e0040000      push    4E0h                                       ; size
f1b2c7fe 33ff            xor     edi,edi
f1b2c800 e8390a0000      call    RDPWD!operator new (f1b2d23e)              ; [1] Allocate 0x4e0 for ShareClass
f1b2c805 85c0            test    eax,eax
f1b2c807 8b7508          mov     esi,dword ptr [ebp+8]
f1b2c80a 59              pop     ecx
f1b2c80b 7416            je      RDPWD!WDWNewShareClass+0x31 (f1b2c823)
f1b2c80d ff765c          push    dword ptr [esi+5Ch]
f1b2c810 8bc8            mov     ecx,eax
f1b2c812 ff7630          push    dword ptr [esi+30h]
f1b2c815 ff762c          push    dword ptr [esi+2Ch]
f1b2c818 ff7628          push    dword ptr [esi+28h]
f1b2c81b 56              push    esi
f1b2c81c e811ffffff      call    RDPWD!ShareClass::ShareClass (f1b2c732)    ; [2] Instantiate ShareClass object
f1b2ad76 ff7520          push    dword ptr [ebp+20h]
f1b2ad79 53              push    ebx
f1b2ad7a ff735c          push    dword ptr [ebx+5Ch]
f1b2ad7d e88edaffff      call    RDPWD!SM_Init (f1b28810)                   ; [3]
f1b2ad82 8bf0            mov     esi,eax
f1b2ad84 85f6            test    esi,esi
f1b2ad86 0f8cbefdffff    jl      RDPWD!WDWConnect+0x56 (f1b2ab4a)
f1b2ad8c 8b451c          mov     eax,dword ptr [ebp+1Ch]

After the ShareClass object has been setup, any component of the driver can then use the ShareClass::SC_SendData method from the ShareClass object in order to send data over the T.128 protocol. Data that is sent over the T.128 protocol is wrapped in a type named SharePDU. A SharePDU is encrypted by Microsoft's implementation of RDP and supports two layers of types. The first layer of the SharePDU is prefixed with a structure, ShareControlHeader which contains a 4-bit field named ShareControlHeader.PDUType which informs the consumer of the type of the structure that the ShareControlHeader contains. This header can then be used by a server and client to either notify the client that it should deactivate a particular session, or to exchange/negotiate what capabilities will be used by the current T.128 session. On top of this T.128 layer, a server or client can also exchange even more data by encompassing a packet within a ShareDataHeader. The ShareClass object is used to represent data that is to be sent using the ShareDataHeader layer, whereas the SM_* functions are used to represent data that as sent at the ShareControlHeader layer.

ShareDataHeader.pad1octet Info Leak

As prior mentioned, the driver implements a method for the ShareClass object called ShareClass::SC_SendData. This method will take packet data provided by the caller, and prefix it with a ShareControlHeader before handing the packet off to the SM_SendData function for encrypting the packet. Upon entry into the ShareClass::SC_SendData method, the method will first set the ShareControlHeader.PDUType field to Data(7), and itsShareControlHeader.ProtocolVersionfield to 1 at [4]. Afterwards, theShareControlHeader.PDUSourcewill then be set to the server channel identifier which is always 1002 (0x3ea). After completing theShareControlHeader, the method will then initialize theShareDataHeader. The 16-bit length of theSharePDUwill be written to the beginning of the packet, and at [6] the rest of the fields will be initialized. The resulting buffer after initialization will then be passed to theSMSendDatafunction with theSECENCRYPTflag set as one of its parameters. While initializing theShareDataHeaderstructure, two of the 8-bit fields at offset 0xa and offset 0xe are left uninitialized. The field at offset 0xe represents theShareDataHeader.PDUType2` field and is intended to be initialized by the caller. The field at offset 0xa, however, is left uninitialized and when the packet is sent will leak 8-bits to the client.

f1b34f46 8bff            mov     edi,edi
f1b34f48 55              push    ebp
f1b34f49 8bec            mov     ebp,esp
f1b34f4b 8b550c          mov     edx,dword ptr [ebp+0Ch]        ; Packet data
f1b34f4e 8b4508          mov     eax,dword ptr [ebp+8]          ; ShareClass*
f1b34f51 56              push    esi
f1b34f52 8b7514          mov     esi,dword ptr [ebp+14h]        ; SharePDU length
f1b34f55 85f6            test    esi,esi
f1b34f57 66c742021700    mov     word ptr [edx+2],17h           ; [4] ShareControlHeader.protocolVersion := 1 | ShareControlHeader.pduType := Data(7)
f1b34f5d 668b8874040000  mov     cx,word ptr [eax+474h]         ; Server Channel Id (1002)
f1b34f64 66894a04        mov     word ptr [edx+4],cx            ; ShareControlHeader.PDUSource
f1b34f68 7427            je      RDPWD!ShareClass::SC_SendData+0x4b (f1b34f91))

f1b34f6a 668932          mov     word ptr [edx],si              ; [5] SharePDU.totalLength
f1b34f6d 8b8878040000    mov     ecx,dword ptr [eax+478h]
f1b34f73 6683621000      and     word ptr [edx+10h],0           ; [6] ShareDataHeader.compressedLength
f1b34f78 894a06          mov     dword ptr [edx+6],ecx          ; ShareDataHeader.ShareId
f1b34f7b 8a4d18          mov     cl,byte ptr [ebp+18h]
f1b34f7e 884a0b          mov     byte ptr [edx+0Bh],cl          ; ShareDataHeader.StreamId
f1b34f81 6689720c        mov     word ptr [edx+0Ch],si          ; ShareDataHeader.uncompressedLength
f1b34f85 c6420f00        mov     byte ptr [edx+0Fh],0           ; ShareDataHaeder.compressedType

f1b34f91 8b8880040000    mov     ecx,dword ptr [eax+480h]
f1b34f97 6a08            push    8                              ; TS_SECURITY_PACKET.basicSecurityHeader.flags := SEC_ENCRYPT(8)
f1b34f99 6a00            push    0                              ; NM_ packet type
f1b34f9b 6a00            push    0                              ; Network Channel Id
f1b34f9d 6a01            push    1                              ; SendData.dataPriority := high(1)
f1b34f9f ff7510          push    dword ptr [ebp+10h]            ; Packet data length
f1b34fa2 e8c73affff      call    RDPWD!SM_SendData (f1b28a6e)   ; [7]
f1b34fa7 5e              pop     esi
f1b34fa8 5d              pop     ebp
f1b34fa9 c21800          ret     18h

The SharePDU begins with a ShareControlHeader which has the following definition. As prior mentioned, the first field in the header is a 16-bit length. When the ShareControlHeader.pduType is set to Data(7), the header of the SharePDU will then be followed by a ShareDataHeader. In the implementation of ShareClass::SC_SendData the byte at offset 0xa is not initialized which references the ShareDataHeader.pad1octet field at [8]. Thus the 8-bits being leaked is via the ShareDataHeader.pad1octet field.

UserID ::= Integer16

ShareControlHeader ::= SEQUENCE {
    totalLength Integer16(0..32767),    // 0x0 : +2
    protocolVersion Integer4(1),        // 0x2.0 : +0.5
    pduType PDUType,                    // 0x2.5 : +1.5
    pduSource UserID                    // 0x4 : +2

ShareID ::= Integer32 
StreamID ::= INTEGER {
    streamLowPriority(1), streamMediumPriority(2), streamHighPriority(4)

ShareDataHeader ::= SEQUENCE {
    shareControlHeader ShareControlHeader,  // 0x0 : +6
    shareID ShareID,                        // 0x6 : +4
    pad1octet Integer8(0),                  // [8] 0xa : +1
    streamID StreamID,                      // 0xb : +1
    uncompressedLength Integer16,           // 0xc : +2
    pduType2 PDUType2,                      // 0xe : +1
    generalCompressedType Integer8,         // 0xf : +1
    generalCompressedLength Integer16       // 0x10 : +2

SynchronizePDU.targetUser Info Leak

As prior mentioned, all T.128 data is prefixed with a ShareControlHeader by the ShareClass::SC_SendData method before being passed to the SM_SendData function which will then wrap the packet in a SharePDU. The SM_SendData method will then read flags from its parameter in order to send to the client. The ShareClass::SC_SendData method thus expects the caller to properly allocate space for its data along with some space at the beginning for both the ShareControlHeader and the ShareDataHeader. When the server wishes to send a SynchronizePDU(31) to a client, the following method ShareClass::SCInitiateSync is called. At [9], the method will first allocate 0x16 bytes for the buffer which sets aside 0x12 for the SharePDU headers, and leaves 4 for the rest of its packet data. After allocating 0x16 bytes and storing it over the first parameter on the stack, the length will be stored at ShareControlHeader.totalLength offset 0x0 followed by synchronize(31) being stored to ShareDataHeader.PDUType2 at offset 0xe. As only 0x4 extra bytes were allocated for the entire SynchronizePDU (0x12 for headers), the 16-bit value of synchronize(1) is stored at offset 0x12 for SynchronizePDU.messageType. This only initializes the first 16-bits of the SynchronizePDU at offset 0x12 which leaves the 16-bits at offset 0x14 uninitialized. Thus when sending this packet, 16-bits will be leaked to the client.

f1b3583c 8bff            mov     edi,edi
f1b3583e 55              push    ebp
f1b3583f 8bec            mov     ebp,esp
f1b35841 56              push    esi
f1b35842 8b7508          mov     esi,dword ptr [ebp+8]                      ; ShareClass*
f1b35845 8b86bc030000    mov     eax,dword ptr [esi+3BCh]
f1b3584b 8a80e012b4f1    mov     al,byte ptr RDPWD!ShareClass::scStateTable+0x18 (f1b412e0)[eax]
f1b35851 84c0            test    al,al
f1b35853 7404            je      RDPWD!ShareClass::SCInitiateSync+0x1d (f1b35859)
f1b35855 3c03            cmp     al,3
f1b35857 7558            jne     RDPWD!ShareClass::SCInitiateSync+0x75 (f1b358b1)

f1b35859 8b8e80040000    mov     ecx,dword ptr [esi+480h]
f1b3585f 6a01            push    1
f1b35861 6a16            push    16h
f1b35863 8d5508          lea     edx,[ebp+8]                                ; Result pointer
f1b35866 e8d923ffff      call    RDPWD!SM_AllocBuffer (f1b27c44)            ; [9] Allocate 0x16 bytes of space
f1b3586b 85c0            test    eax,eax
f1b3586d 7542            jne     RDPWD!ShareClass::SCInitiateSync+0x75 (f1b358b1)

f1b3586f 8b4508          mov     eax,dword ptr [ebp+8]
f1b35872 6a00            push    0
f1b35874 66c7001600      mov     word ptr [eax],16h                         ; ShareControlHeader.totalLength
f1b35879 8b4508          mov     eax,dword ptr [ebp+8]
f1b3587c 6a00            push    0                                          ; data stream id
f1b3587e c6400e1f        mov     byte ptr [eax+0Eh],1Fh                     ; ShareDataHeader.PDUType2 := synchronize(31)
f1b35882 8b4508          mov     eax,dword ptr [ebp+8]
f1b35885 6a16            push    16h                                        ; SharePDU length
f1b35887 6a16            push    16h                                        ; packet length
f1b35889 66c740120100    mov     word ptr [eax+12h],1                       ; SynchronizePDU.messageType := synchronize(1)
f1b3588f ff7508          push    dword ptr [ebp+8]                          ; packet data
f1b35892 56              push    esi                                        ; ShareClass*
f1b35893 e8aef6ffff      call    RDPWD!ShareClass::SC_SendData (f1b34f46)   ; [10] Send packet with PDUType = Data(7)
f1b35898 85c0            test    eax,eax
f1b3589a 7415            je      RDPWD!ShareClass::SCInitiateSync+0x75 (f1b358b1)

The SynchronizePDU has the following definition. In this structure the SynchronizePDU.messageType field is at offset 0x12 which is initialized to synchronize(1) by the ShareClass::SCInitiateSync method. The field at offset 0x14, SynchronizePDU.targetUser, is not initialized which is then leaked to the client when the packet is sent.

SynchronizeMessageType ::= INTEGER {synchronize(1)}(0..65535)

SynchronizePDU ::= SEQUENCE {
    shareDataHeader ShareDataHeader,        // 0x0 : +18
    messageType SynchronizeMessageType,     // 0x12 : +2
    targetUser UserID                       // 0x14 : +2

DeactivateOtherPDU.lengthSourceDescriptor Info Leak

The following code shows a disassembly of the ShareClass::SCDeactivateOther method. This method is called to send the DeactivateOtherPDU which is used to deactivate another client that is joined to a T.128 session. The DeactivateOtherPDU is unique in that it uses a field with a variable size called DeactivateOtherPDU.sourceDescriptor. Thus in order to allocate a buffer for the packet, some sizes will need to be determined before allocating. At [11], the method will calculate the string length of the field which will be added to the static length of the DeactivateOtherPDU. After summing the size of the static part of the DeactivateOtherPDU (0xe bytes) with the length of DeactivateOtherPDU.sourceDescriptor, the method will pass the result to the SM_AllocBuffer function at [12]. Once allocating a buffer with it, the method will continue initializing the ShareControlHeader fields. As 0xe bytes was allocated for the header and fields belonging to the DeactivateOtherPDU, only 0x6 bytes is allocated towards the ShareControlHeader. This leaves 0x8 bytes for the DeactivateOtherPDU. After initializing the fields for the DeactivateOtherPDU, its sourceDescriptor field is then copied into the buffer at offset 0xe [13]. The last field that is written to this buffer is the 16-bit deactivateId at offset 0xa, which leaves a 16-bit field at offset 0xc uninitialized. After finishing initializing the packet, it is then sent at [14] with SM_SendData.

f1b358bc 8bff            mov     edi,edi
f1b358be 55              push    ebp
f1b358bf 8bec            mov     ebp,esp
f1b358c1 51              push    ecx
f1b358c2 53              push    ebx
f1b358c3 8b5d08          mov     ebx,dword ptr [ebp+8]
f1b358c6 56              push    esi
f1b358c7 8d83c4030000    lea     eax,[ebx+3C4h]                     ; [11] sourceDescriptor fetched to calculate its strlen()
f1b358cd 57              push    edi
f1b358ce 8d7001          lea     esi,[eax+1]

f1b358d1 8a08            mov     cl,byte ptr [eax]
f1b358d3 40              inc     eax
f1b358d4 84c9            test    cl,cl
f1b358d6 75f9            jne     RDPWD!ShareClass::SCDeactivateOther+0x15 (f1b358d1)
f1b358d8 8b8b80040000    mov     ecx,dword ptr [ebx+480h]
f1b358de 2bc6            sub     eax,esi
f1b358e0 8d7803          lea     edi,[eax+3]                        ; string length + 3
f1b358e3 83e7fc          and     edi,0FFFFFFFCh                     ; align string length to dword
f1b358e6 8d770e          lea     esi,[edi+0Eh]                      ; string length + 0xe
f1b358e9 6a01            push    1
f1b358eb 56              push    esi                                ; buffer size
f1b358ec 8d5508          lea     edx,[ebp+8]                        ; result pointer
f1b358ef 8975fc          mov     dword ptr [ebp-4],esi
f1b358f2 e84d23ffff      call    RDPWD!SM_AllocBuffer (f1b27c44)    ; [12] allocate space for pdu using strlen() of sourceDescriptor
f1b358f7 85c0            test    eax,eax
f1b358f9 7569            jne     RDPWD!ShareClass::SCDeactivateOther+0xa8 (f1b35964)
f1b358fb 8b4508          mov     eax,dword ptr [ebp+8]
f1b358fe 668930          mov     word ptr [eax],si                  ; ShareControlHeader.totalLength
f1b35901 8b4508          mov     eax,dword ptr [ebp+8]
f1b35904 66c740021400    mov     word ptr [eax+2],14h               ; ShareControlHeader.protocolVersion := 1 | ShareControlHeader.PDUType := deactivateOther(4)
f1b3590a 668b8374040000  mov     ax,word ptr [ebx+474h]             ; Server Channel Id (1002)
f1b35911 8b4d08          mov     ecx,dword ptr [ebp+8]
f1b35914 66894104        mov     word ptr [ecx+4],ax                ; ShareControlHeader.PDUSource
f1b35918 8b4d08          mov     ecx,dword ptr [ebp+8]
f1b3591b 8b8378040000    mov     eax,dword ptr [ebx+478h]
f1b35921 894106          mov     dword ptr [ecx+6],eax              ; DeactivateOtherPDU.shareId
f1b35924 8b4508          mov     eax,dword ptr [ebp+8]
f1b35927 668b4d0c        mov     cx,word ptr [ebp+0Ch]
f1b3592b 6689480a        mov     word ptr [eax+0Ah],cx              ; DeactivateOtherPDU.deactivateId
f1b3592f 8bcf            mov     ecx,edi
f1b35931 8b7d08          mov     edi,dword ptr [ebp+8]
f1b35934 8bc1            mov     eax,ecx
f1b35936 c1e902          shr     ecx,2
f1b35939 83c70e          add     edi,0Eh                            ; [13] copy 
f1b3593c 8db3c4030000    lea     esi,[ebx+3C4h]                     ; Source Descriptor
f1b35942 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
f1b35944 6a08            push    8                                  ; TS_SECURITY_PACKET.basicSecurityHeader.flags := SEC_ENCRYPT(8)
f1b35946 6a00            push    0                                  ; NM_ type
f1b35948 6a00            push    0                                  ; ChannelId key
f1b3594a 8bc8            mov     ecx,eax
f1b3594c 83e103          and     ecx,3
f1b3594f 6a01            push    1                                  ; SendData.dataPriority := high(1)
f1b35951 ff75fc          push    dword ptr [ebp-4]                  ; packet length
f1b35954 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
f1b35956 8b5508          mov     edx,dword ptr [ebp+8]
f1b35959 8b8b80040000    mov     ecx,dword ptr [ebx+480h]
f1b3595f e80a31ffff      call    RDPWD!SM_SendData (f1b28a6e)       ; [14] send packet

The following structure is the definition of the DeactivateOtherPDU. Comparing this with the previous code snippet, the lengthSourceDescriptor field does not appear to be populated as the length that was calculated is only used for copying the sourceDescriptor into the packet. The sending of the DeactivateOtherPDU will thus leak the 16-bit 'lengthSourceDescriptor` field to the client.

DeactivateOtherPDU ::= SEQUENCE {
    shareControlHeader ShareControlHeader,                      ; 0x0 : +6
    shareID ShareID,                                            ; 0x6 : +4
    deactivateID UserID,                                        ; 0xa : +2
    lengthSourceDescriptor Integer16 (1..maxSourceDescriptor),  ; 0xc : +2
    sourceDescriptor T50String (SIZE(1..maxSourceDescriptor))   ; 0xe : +lengthSourceDescriptor


In order for RDS to set up the T.128 layer of the RDP protocol and enable Multipoint Application Sharing, it was prior mentioned that the driver must negotiate capabilities with the client. This is done by the server preparing a DemandActivatePDU to announce the available capabilities in order to allow the client to confirm them by responding with a ConfirmActivePDU. In order to send this PDU, the following code is executed from the WDWDDConnect function. Once clearing an event, the ShareClass::SC_CreateShare method is called at [15]. This method performs a number of actions related to fetching the capabilities that are supported by the server and preparing them prior to submission to the client for negotiation. After preparations, the method will first allocate a buffer for the DemandActivePDU at [16]. The DemandActivePDU contains two variable-length fields and as a result of this, their lengths are summed in order to produce the total length that gets passed to the SM_AllocBuffer function. The first variable-length field is named DemandActivePDU.sourceDescriptor and is a null-terminated string. The next variable-length field is an array of all the supported capabilities for the client to confirm. After allocating space for the packet which includes the size of these two fields, the method will then initialize the ShareControlHeader fields that are prefixed at the beginning of the packet. Similar to all packets that are sent with the SM_SendData function, the ShareControlHeader.PDUType field is set at [17]. As this PDU is a DemandActivePDU, the ShareConotrolHeader.PDUType field is set to demandActive(1). After assigning the server channel identifier (1002) to ShareControlHeader.PDUSource, the length of the two variable-length fields are then assigned at [18]. Once that has been accomplished, the variable-length fields will then be copied into the allocated buffer. The first variable-length field in the packet is the DemandActivePDU.sourceDescriptor which is copied at [19]. Immediately following this is the array of capabilities which are copied at [20]. After everything has been copied into the allocated buffer, the DemandActivePDU.SessionId field is written before the entire packet is finally sent at [21].

f1b2d098 ff7644          push    dword ptr [esi+44h]
f1b2d09b ff15fc0fb4f1    call    dword ptr [RDPWD!_imp__KeClearEvent (f1b40ffc)]
f1b2d0a1 53              push    ebx                                            ; ShareClass*
f1b2d0a2 e89d700000      call    RDPWD!ShareClass::SC_CreateShare (f1b34144)    ; [15] \
f1b2d0a7 85c0            test    eax,eax
f1b2d0a9 0f840ffeffff    je      RDPWD!WDWDDConnect+0x20 (f1b2cebe)
f1b2d0af 6860ea0000      push    0EA60h                                         ; 60000ms (1 minute)
f1b2d0b4 ff7644          push    dword ptr [esi+44h]
f1b2d0b7 56              push    esi
f1b2d0b8 e8d1d6ffff      call    RDPWD!WDW_WaitForConnectionEvent (f1b2a78e)    ; wait for event
f1b2d0bd 837e5800        cmp     dword ptr [esi+58h],0
f1b2d0c1 0f84f7fdffff    je      RDPWD!WDWDDConnect+0x20 (f1b2cebe)
f1b2d0c7 3d02010000      cmp     eax,102h
f1b2d0cc 7539            jne     RDPWD!WDWDDConnect+0x269 (f1b2d107)
f1b2d0ce 8b4710          mov     eax,dword ptr [edi+10h]
f1b2d0d1 6a00            push    0
f1b2d0d3 53              push    ebx
f1b2d0d4 894314          mov     dword ptr [ebx+14h],eax
f1b2d0d7 e8ac7d0000      call    RDPWD!ShareClass::SC_EndShare (f1b34e88)       ; send DeactivateAllPDU
f1b348b9 8b853cfeffff    mov     eax,dword ptr [ebp-1C4h]               ; length of combined capabilities
f1b348bf 83e6fc          and     esi,0FFFFFFFCh
f1b348c2 8d440612        lea     eax,[esi+eax+12h]                      ; sum source descriptor and combined capabilities
f1b348c6 6a01            push    1
f1b348c8 50              push    eax
f1b348c9 8d9548feffff    lea     edx,[ebp-1B8h]                         ; result pointer
f1b348cf 89b538feffff    mov     dword ptr [ebp-1C8h],esi
f1b348d5 898544feffff    mov     dword ptr [ebp-1BCh],eax
f1b348db e86433ffff      call    RDPWD!SM_AllocBuffer (f1b27c44)        ; [16] allocate buffer for DemandActivePDU
f1b348e0 85c0            test    eax,eax
f1b348e2 0f85fe000000    jne     RDPWD!ShareClass::SC_CreateShare+0x8a2 (f1b349e6)
f1b34904 6a08            push    8                                      ; TS_SECURITY_PACKET.basicSecurityHeader.flags := SEC_ENCRYPT(8)
f1b34906 898778040000    mov     dword ptr [edi+478h],eax
f1b3490c 8b8548feffff    mov     eax,dword ptr [ebp-1B8h]
f1b34912 668910          mov     word ptr [eax],dx                      ; SharePDU.totalLength
f1b34915 8b8548feffff    mov     eax,dword ptr [ebp-1B8h]
f1b3491b 66c740021100    mov     word ptr [eax+2],11h                   ; [17] ShareControlHeader.protocolVersion := 1 | ShareControlHeader.PDUType := demandActive(1)
f1b34921 668b01          mov     ax,word ptr [ecx]                      ; Server Channel Id (1002)
f1b34924 8b8d48feffff    mov     ecx,dword ptr [ebp-1B8h]
f1b3492a 66894104        mov     word ptr [ecx+4],ax                    ; ShareControlHeader.PDUSource
f1b3492e 8b8778040000    mov     eax,dword ptr [edi+478h]
f1b34934 8b8d48feffff    mov     ecx,dword ptr [ebp-1B8h]
f1b3493a 894106          mov     dword ptr [ecx+6],eax                  ; DemandActivePDU.ShareId
f1b3493d 8b8548feffff    mov     eax,dword ptr [ebp-1B8h]
f1b34943 6689700a        mov     word ptr [eax+0Ah],si                  ; [18] DemandActivePDU.lengthSourceDescriptor
f1b34947 8b8548feffff    mov     eax,dword ptr [ebp-1B8h]
f1b3494d 668b8d3cfeffff  mov     cx,word ptr [ebp-1C4h]
f1b34954 6689480c        mov     word ptr [eax+0Ch],cx                  ; [18] DemandActivePDU.lengthCombinedCapabilities
f1b34958 8bbd48feffff    mov     edi,dword ptr [ebp-1B8h]
f1b3495e 8bce            mov     ecx,esi
f1b34960 8bb534feffff    mov     esi,dword ptr [ebp-1CCh]               ; Source Descriptor
f1b34966 8bc1            mov     eax,ecx
f1b34968 c1e902          shr     ecx,2
f1b3496b 83c70e          add     edi,0Eh                                ; DemandActivePDU.sourceDescriptor
f1b3496e f3a5            rep movs dword ptr es:[edi],dword ptr [esi]    ; [19] copy source descriptor
f1b34970 8bc8            mov     ecx,eax
f1b34972 83e103          and     ecx,3
f1b34975 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
f1b34977 8b9548feffff    mov     edx,dword ptr [ebp-1B8h]
f1b3497d 8b8d3cfeffff    mov     ecx,dword ptr [ebp-1C4h]
f1b34983 8bb530feffff    mov     esi,dword ptr [ebp-1D0h]               ; Combined Capabilities
f1b34989 8d7c100e        lea     edi,[eax+edx+0Eh]                      ; DemandActivePDU.combinedCapabilities
f1b3498d 8bd1            mov     edx,ecx
f1b3498f c1e902          shr     ecx,2
f1b34992 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]    ; [20] copy capabilities
f1b34994 8bca            mov     ecx,edx
f1b34996 8b9540feffff    mov     edx,dword ptr [ebp-1C0h]
f1b3499c 83e103          and     ecx,3
f1b3499f f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
f1b349a1 8b8d3cfeffff    mov     ecx,dword ptr [ebp-1C4h]               ; Combined Capabilites Length
f1b349a7 8bb52cfeffff    mov     esi,dword ptr [ebp-1D4h]               ; ShareClass*
f1b349ad 53              push    ebx                                    ; NM_ type
f1b349ae 03c1            add     eax,ecx
f1b349b0 8b8d48feffff    mov     ecx,dword ptr [ebp-1B8h]
f1b349b6 53              push    ebx                                    ; Network Channel Id
f1b349b7 6a01            push    1                                      ; SendData.dataPriority := high(1)
f1b349b9 ffb544feffff    push    dword ptr [ebp-1BCh]                   ; Packet length
f1b349bf 8954080e        mov     dword ptr [eax+ecx+0Eh],edx            ; DemandActivePDU.SessionId
f1b349c3 8b9548feffff    mov     edx,dword ptr [ebp-1B8h]               ; Packet data
f1b349c9 8b8e80040000    mov     ecx,dword ptr [esi+480h]
f1b349cf e89a40ffff      call    RDPWD!SM_SendData (f1b28a6e)           ; [21] send packet
f1b349d4 898544feffff    mov     dword ptr [ebp-1BCh],eax

In the prior disassembly, the capabilities and their length were copied from a pointer that was located within the function's frame which is initialized by the ShareClass::CPC_GetCombinedCapabilities method. This is shown in the following disassembly with the pointer being initialized near the beginning of the method at 1d0(%ebp) with its length at 1c4(%ebp). Both of the described locations within the frame are passed to the ShareClass::CPC_GetCombinedCapabilities method at [22]. Depending on its second parameter, this method will grab a property out of the ShareClass instance beginning at 58(%eax) [23]. Once this property containing the capabilities has been fetched, the capabilities and their length will then be written to the method's parameters at [24] before returning to the caller.

f1b34837 8d8530feffff    lea     eax,[ebp-1D0h]                                             ; Combined Capabilities
f1b3483d 50              push    eax
f1b3483e 8d853cfeffff    lea     eax,[ebp-1C4h]                                             ; Combined Capabilities Length
f1b34844 50              push    eax
f1b34845 53              push    ebx                                                        ; Index
f1b34846 57              push    edi                                                        ; ShareClass*
f1b34847 e84cd4ffff      call    RDPWD!ShareClass::CPC_GetCombinedCapabilities (f1b31c98)   ; [22] \
f1b31c98 8bff            mov     edi,edi
f1b31c9a 55              push    ebp
f1b31c9b 8bec            mov     ebp,esp
f1b31c9d 8b450c          mov     eax,dword ptr [ebp+0Ch]    ; Index
f1b31ca0 56              push    esi
f1b31ca1 33f6            xor     esi,esi
f1b31ca3 3bc6            cmp     eax,esi
f1b31ca5 7508            jne     RDPWD!ShareClass::CPC_GetCombinedCapabilities+0x17 (f1b31caf)
f1b31ca7 8b4508          mov     eax,dword ptr [ebp+8]      ; ShareClass*
f1b31caa 8b4058          mov     eax,dword ptr [eax+58h]    ; [23] pointer to capabilities
f1b31cad eb0d            jmp     RDPWD!ShareClass::CPC_GetCombinedCapabilities+0x24 (f1b31cbc)
f1b31caf 8b4d08          mov     ecx,dword ptr [ebp+8]
f1b31cb2 8d448158        lea     eax,[ecx+eax*4+58h]
f1b31cb6 3930            cmp     dword ptr [eax],esi
f1b31cb8 7423            je      RDPWD!ShareClass::CPC_GetCombinedCapabilities+0x45 (f1b31cdd)
f1b31cba 8b00            mov     eax,dword ptr [eax]
f1b31cbc 0fb710          movzx   edx,word ptr [eax]
f1b31cbf 3bd6            cmp     edx,esi
f1b31cc1 8d4804          lea     ecx,[eax+4]
f1b31cc4 7609            jbe     RDPWD!ShareClass::CPC_GetCombinedCapabilities+0x37 (f1b31ccf)
f1b31ccf 8b5510          mov     edx,dword ptr [ebp+10h]    ; [24] result length
f1b31cd2 2bc8            sub     ecx,eax
f1b31cd4 890a            mov     dword ptr [edx],ecx
f1b31cd6 8b4d14          mov     ecx,dword ptr [ebp+14h]    ; [24] result capabilities
f1b31cd9 8901            mov     dword ptr [ecx],eax
f1b31cdb eb0a            jmp     RDPWD!ShareClass::CPC_GetCombinedCapabilities+0x4f (f1b31ce7)

f1b31cdd 8b4510          mov     eax,dword ptr [ebp+10h]    ; [24] result length
f1b31ce0 8930            mov     dword ptr [eax],esi
f1b31ce2 8b4514          mov     eax,dword ptr [ebp+14h]    ; [24] result capabilities
f1b31ce5 8930            mov     dword ptr [eax],esi

The previous disassembly has shown that the ShareClass object contains its capabilities at offset +0x58 of the instance. This property is initialized by the WDWDDConnect function which is responsible for calling the ShareClass::SC_CreateShare method as was described. In order to initialize the capabilities that are used later, the WDWDDConnect function will call upon the ShareClass::DCS_Init method after instantiating the ShareClass at [25]. Firstly, the ShareClass::DCS_Init method will call ShareClass::SC_Init at [26] which will populate a number of fields that are used when sending some of the PDU types. At [27], the method will copy the `sourceDescriptor from a global and store it at offset +0x3c4 of the class.

f1b2cf61 8b4710          mov     eax,dword ptr [edi+10h]
f1b2cf64 894314          mov     dword ptr [ebx+14h],eax
f1b2cf67 ff765c          push    dword ptr [esi+5Ch]
f1b2cf6a 56              push    esi
f1b2cf6b 53              push    ebx                                    ; ShareClass*
f1b2cf6c e809500000      call    RDPWD!ShareClass::DCS_Init (f1b31f7a)  ; [25] \
f1b2cf71 83631400        and     dword ptr [ebx+14h],0
f1b2cf75 85c0            test    eax,eax
f1b2cf77 0f848f000000    je      RDPWD!WDWDDConnect+0x16e (f1b2d00c)
f1b31fd5 ff7510          push    dword ptr [ebp+10h]
f1b31fd8 c7030068c461    mov     dword ptr [ebx],61C46800h
f1b31fde 56              push    esi
f1b31fdf 897b04          mov     dword ptr [ebx+4],edi
f1b31fe2 e80d200000      call    RDPWD!ShareClass::SC_Init (f1b33ff4)   ; [26] \
f1b31fe7 85c0            test    eax,eax
f1b31fe9 0f84a0000000    je      RDPWD!ShareClass::DCS_Init+0x115 (f1b3208f)
f1b33ff4 8bff            mov     edi,edi
f1b33ff6 55              push    ebp
f1b33ff7 8bec            mov     ebp,esp
f1b33ff9 51              push    ecx
f1b33ffa 53              push    ebx
f1b33ffb 33db            xor     ebx,ebx
f1b33ffd 56              push    esi
f1b33ffe 8b7508          mov     esi,dword ptr [ebp+8]                  ; ShareClass*
f1b340a1 8b07            mov     eax,dword ptr [edi]
f1b340a3 8986c0030000    mov     dword ptr [esi+3C0h],eax
f1b340a9 8b450c          mov     eax,dword ptr [ebp+0Ch]
f1b340ac 898680040000    mov     dword ptr [esi+480h],eax
f1b340b2 a1ea3fb3f1      mov     eax,dword ptr [RDPWD!ShareClass::UP_UpdateHeaderSize+0x28 (f1b33fea)]
f1b340b7 6800200000      push    2000h
f1b340bc 8986c4030000    mov     dword ptr [esi+3C4h],eax               ; [27] Store source descriptor
f1b340c2 e873340000      call    RDPWD!IcaBufferGetUsableSpace (f1b3753a)
f1b340c7 6800400000      push    4000h
f1b340cc 898698040000    mov     dword ptr [esi+498h],eax
f1b340d2 e863340000      call    RDPWD!IcaBufferGetUsableSpace (f1b3753a)
f1b340d7 89869c040000    mov     dword ptr [esi+49Ch],eax

Returning from the ShareClass::SC_Init method, the caller will then call two more methods one of which is displayed in the following assembly. The call to ShareClass::CPC_Init at [28] shows that the address of the properties at 68(%ecx) are written to 58(%ecx) which is where the capabilities that are being written to the DemandActivePDU are coming from. After writing the address at [29], the method returns and then begins to register the very first capability by calling the ShareClass::SC_SetCapabilities method.

f1b31fef 56              push    esi
f1b31ff0 e877fbffff      call    RDPWD!ShareClass::CPC_Init (f1b31b6c)                  ; [28] \
f1b31ff5 56              push    esi
f1b31ff6 e8e1300000      call    RDPWD!ShareClass::SC_SetCapabilities (f1b350dc)
f1b31ffb 33c0            xor     eax,eax
f1b31b6c 8bff            mov     edi,edi
f1b31b6e 55              push    ebp
f1b31b6f 8bec            mov     ebp,esp
f1b31b71 8b4d08          mov     ecx,dword ptr [ebp+8]
f1b31b74 57              push    edi
f1b31b75 33c0            xor     eax,eax
f1b31b77 8d795c          lea     edi,[ecx+5Ch]                                          ; initialize properties
f1b31b7a ab              stos    dword ptr es:[edi]
f1b31b7b ab              stos    dword ptr es:[edi]
f1b31b7c ab              stos    dword ptr es:[edi]
f1b31b7d 8d4168          lea     eax,[ecx+68h]                                          ; capabilities pointer
f1b31b80 894158          mov     dword ptr [ecx+58h],eax                                ; [29] Write capabilities pointer to +58
f1b31b83 66832000        and     word ptr [eax],0
f1b31b87 5f              pop     edi
f1b31b88 5d              pop     ebp
f1b31b89 c20400          ret     4

The first capability that is registered is done by ShareClass::SC_SetCapabilities. The actual registration of a capability requires a caller to initialize a structure for the capability's data and pass a pointer to said buffer along with its length to the ShareClass::CPC_RegisterCapabilities method. The following disassembly shows a TS_SHARE_CAPABILITYSET with a size of 0x8 bytes being initialized on the stack and then registered. The server's channel identifier (1002) is first fetched from a property belonging to the ShareClass, and then written to offset 0x4 of the stack buffer. The next value that is written is the capability set's 16-bit type which is set to shareCapabilitySet(9). Once these actions have been performed, the buffer will then be passed to ShareClass::CPC_RegisterCapabilities in order to register the capability. Prior to real capability registration, the ShareClass::CPC_RegisterCapabilities method will take the length passed by the caller via one of its parameters at [32] and then write the length to offset 0x2 of the capability data. Thus this is the only field that the method expects the caller to leave uninitialized. Looking back at the capability data prior to being passed to the ShareClass::CPC_RegisterCapabilities method, only two fields, the 16-bit TS_SHARE_CAPABILITYSET.capabilitySetType at offset 0x0 and the 16-bit TS_SHARE_CAPABILITYSET.nodeId at offset 0x4 are initialized. As the ShareClass::CPC_RegisterCapabilities method initializes the 16-bit length at offset 0x2, this leaves the 16-bits at offset 0x6 uninitialized.

f1b350dc 8bff            mov     edi,edi
f1b350de 55              push    ebp
f1b350df 8bec            mov     ebp,esp
f1b350e1 51              push    ecx
f1b350e2 51              push    ecx
f1b350e3 8b4508          mov     eax,dword ptr [ebp+8]  ; ShareClass*
f1b350e6 668b8874040000  mov     cx,word ptr [eax+474h] ; Server Channel Id (1002)
f1b350ed 66894dfc        mov     word ptr [ebp-4],cx    ; [30] TS_SHARE_CAPABILITYSET.nodeId
f1b350f1 6a08            push    8                      ; capability data length
f1b350f3 8d4df8          lea     ecx,[ebp-8]            ; capability buffer to use
f1b350f6 51              push    ecx                    ; capability data
f1b350f7 50              push    eax
f1b350f8 66c745f80900    mov     word ptr [ebp-8],9     ; [31] TS_SHARE_CAPABILITYSET.capabilitySetType := shareCapabilitySet(9)
f1b350fe e8bdcaffff      call    RDPWD!ShareClass::CPC_RegisterCapabilities (f1b31bc0)
f1b35103 c9              leave
f1b35104 c20400          ret     4
f1b31bc0 8bff            mov     edi,edi
f1b31bc2 55              push    ebp
f1b31bc3 8bec            mov     ebp,esp
f1b31bc5 53              push    ebx
f1b31bc6 668b5d10        mov     bx,word ptr [ebp+10h]      ; [32] capability length
f1b31bca 6685db          test    bx,bx
f1b31bcd 744e            je      RDPWD!ShareClass::CPC_RegisterCapabilities+0x5d (f1b31c1d)
f1b31bcf 8b5508          mov     edx,dword ptr [ebp+8]      ; ShareClass*
f1b31bd2 56              push    esi
f1b31bd3 8b750c          mov     esi,dword ptr [ebp+0Ch]    ; capability data
f1b31bd6 66895e02        mov     word ptr [esi+2],bx        ; [32] CapabilitySet.lengthCapability
f1b31c1d 5b              pop     ebx
f1b31c1e 5d              pop     ebp
f1b31c1f c20c00          ret     0Ch

The TS_SHARE_CAPABILITYSET has the following structure. As prior mentioned, it is the 16-bits at offset 0x6 of this structure that was mistakenly uninitialized by the caller. This offset references the TS_SHARE_CAPABILITYSET.pad2octets field. As a result in the capabilities that are sent to the client via the DemandActivePDU, the 16-bits in this field will reference uninitialized data which will leak these 16-bits to the remote client.

    uint16_t capabilitySetType;     // 0x0 : +2
    uint16_t lengthCapability;      // 0x2 : +2
    uint16_t nodeId;                // 0x4 : +2
    uint16_t pad2octets;            // 0x6 : +2


After initializing a number of other capabilities, the ShareClass::DCS_Init method will eventually execute the following code. This will call the ShareClass::IM_Init method at [33]. This method is responsible for registering the TS_INPUT_CAPABILITYSET capability into the ShareClass. At [34], the method will pass the capability's length (0x58) and its buffer for the capability data to the ShareClass::CPC_RegisterCapabilities method. Prior to calling the method, a few fields in the buffer will be initialized. This includes the TS_INPUTCAPABILITYSET.capabilitySetType being set to inputCapabilitySet(13), followed by its length and some flags. As the capability's length is 0x58 and only 6 bytes are initialized, this can leak 0x52 bytes of data to the client.

f1b3207a 56              push    esi
f1b3207b e8f8080000      call    RDPWD!ShareClass::IM_Init (f1b32978)   ; [33] \
f1b32978 8bff            mov     edi,edi
f1b3297a 55              push    ebp
f1b3297b 8bec            mov     ebp,esp
f1b3297d 83ec5c          sub     esp,5Ch
f1b329ac 6a58            push    58h                        ; [34] capability data length
f1b329ae 8d45a4          lea     eax,[ebp-5Ch]              ; capability data on stack
f1b329b1 50              push    eax                        ; capability data
f1b329b2 52              push    edx
f1b329b3 66c745a40d00    mov     word ptr [ebp-5Ch],0Dh     ; TS_INPUT_CAPABILITYSET.capabilitySetType := inputCapabilitySet(13)
f1b329b9 66c745a65800    mov     word ptr [ebp-5Ah],58h     ; TS_INPUT_CAPABILITYSET.lengthCapability
f1b329bf 66c745a83500    mov     word ptr [ebp-58h],35h     ; TS_INPUT_CAPABILITYSET.inputFlags := FASTPATH_INPUT2 | UNICODE | MOUSEX | SCANCODES
f1b329c5 e8f6f1ffff      call    RDPWD!ShareClass::CPC_RegisterCapabilities (f1b31bc0)

The TS_INPUT_CAPABILITYSET can be defined with the following structure. In the prior disassembly, only the TS_INPUT_CAPABILITYSET.capabilitySetType, TS_INPUT_CAPABILITYSET.lengthCapability, and the TS_INPUT_CAPABILITYSET.inputFlags fields are set. This leaves the other fields to be uninitialized which will leak 0x52 bytes of data to the client when the packet is sent.

    uint16_t capabilitySetType;     // 0x0 : +2
    uint16_t lengthCapability;      // 0x2 : +2
    uint16_t inputFlags;            // 0x4 : +2
    uint16_t pad2octetsA;           // 0x6 : +2
    uint32_t keyboardLayout;        // 0x8 : +2
    uint32_t keyboardType;          // 0xc : +2
    uint32_t keyboardSubType;       // 0x10 : +2
    uint32_t keyboardFunctionKey;   // 0x14 : +2
    char[64] imeFileName;           // 0x18 : +64

Due to the expectations of the SM_SendData function, and both the ShareClass::SC_SendData and ShareClass::CPC_RegisterCapabilities methods, each of these can allow leakage of stack information to a remote client. These functions/methods expect their caller to assign fields within a structure that they use which can make it easy for one to forget to clear the packet. As demonstrated, this can leak information to a client which can provide information to an attacker when trying to predict the state of the driver.

Crash Information

The base addresses of the functions described in the details are as follows:

kd> lm vm termdd
Browse full module list
start    end        module name
f350a000 f3513f00   termdd     (pdb symbols)          c:\mss\termdd.pdb\C04E4855F20641ECB654BB1AD575B8611\termdd.pdb
    Loaded symbol image file: termdd.sys
    Image path: termdd.sys
    Image name: termdd.sys
    Browse all global symbols  functions  data
    Timestamp:        Sun Apr 13 13:38:36 2008 (4802532C)
    CheckSum:         0000DEB5
    ImageSize:        00009F00
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
    Information from resource tables:

kd> lm vm rdpwd
Browse full module list
start    end        module name
f1b23000 f1b45100   RDPWD      (pdb symbols)          c:\mss\RDPWD.pdb\19F9DADE1E5E42CD8A2AA9DB4F26060B1\RDPWD.pdb
    Loaded symbol image file: RDPWD.SYS
    Image path: RDPWD.SYS
    Image name: RDPWD.SYS
    Browse all global symbols  functions  data
    Timestamp:        Sun Apr 13 13:38:40 2008 (48025330)
    CheckSum:         0002585F
    ImageSize:        00022100
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
    Information from resource tables:

Exploit Proof of Concept

To run the provided proof-of-concept, Python2 and the six module must be installed. To install the six module, one can simply use pip as follows:

$ pip install six

After installing the required modules, the proof-of-concept can then be run with the following command against an XP machine. For furhter help, the --help parameter has also been implemented.

$ python poc.zip $host

After running the proof-of-concept, a number of structures will then be emitted to the screen after negotiating capabilities. These structures contain information that was leaked with the vulnerabilities described in this document.


These information leaks exist only on the RDP implementation provided by the Windows XP platform. Other implementations for platforms such as Windows 7 (and newer) properly initialize these structures with memset() and therefore it is recommended to use versions newer than Windows XP if one does not want to be exposed to these vulnerabilities.


2019-09-19 - Vendor Disclosure
2019-10-29 - 30 day follow up
2019-10-30 - Vendor advised no fix planned due to product end of life
2019-12-04 - CVE assigned
2019-12-10 - Public Release


Discovered by a member of Cisco Talos.