Sunday, 18 March 2012

MS12-020 Vulnerability for Breakfast

A few days ago Microsoft has released an important update that fixes vulnerabilities in the Remote Desktop Protocol.

The update affects a number of the system files such as Rdpcore.dll and RdpWD.sys. Comparison of the files in the disassembled form before and after update reveals the code changes.

Let's have a look at the code changes that took place in the driver file RdpWD.sys. But first, what's the role of this file in the Remote Desktop Protocol?

When an RDP client connects to the server component of Remote Desktop Services on TCP port 3389, the login and GDI graphics subsystem are initiated in a newly created session. Within that session, the GDI graphics subsystem relies on RDP-specific driver RdpWD.sys - the keyboard and mouse driver that transfers keyboard/mouse events over TCP connection. The driver also creates virtual channels that set up redirection of other hardware devices (disc, audio, printers) - this allows transferring the requested data over the TCP connection.

The code modification took place in the function HandleAttachUserReq(). Here is the snippet of the original driver's source code (v6.1.7600.16385 found on 32-bit Windows 7):

Replace the old value of maxChannelIds (0x22) with 0 - this byte is located at the packet offset of 0x2C.

The final packet dump will look as shown below (selections in yellow reflect the aforementioned changes 1-4):

All these modifications of the packet are perfectly legitimate, excepting one: setting maxChannelIds value of the targetParameters to 0. That's probably the only "evil" byte in the whole packet. Can there be a signature reliably constructed to catch such packet (a rhetorical question)?

Given the flexibility of BER format (you can BER-encode 0 as 02 01 00, or 02 02 0000, or 02 04 00000000 - that is, using 1, 2, or 4 bytes), is there a reasonably small number of combinations that can potentially assemble a malformed packet with the "evil" byte in it, a number so small that it can be covered with the signatures (another rhetorical question)?

Saving the packet dump into packet.bin and running the Python script below will cause BSoD for the target server at %TARGET_IP_ADDRESS%:

The crush dump can then be loaded into WinDbg debugger to locate the place of the exception. As seen in the crush dump, the error happens in the following instruction that belongs to the function termdd!IcaBufferAllocEx:

8d02c987 mov eax,dword ptr [esi+18h]

The instruction at the address 0x8d02c987 attempts to read a pointer (the next instruction references a DWORD pointed by it, and compares its value with 0) from the address 0x0a400620:

The call stack reveals that the IcaBufferAllocEx() function within termdd.sys is called from RdpWD.sys driver and can be traced back to its NM_Disconnect() call:termdd!IcaBufferAllocEx+0x1bRDPWD!WDICART_IcaBufferAllocEx+0x24RDPWD!StackBufferAllocEx+0x5cRDPWD!MCSDetachUserRequest+0x29RDPWD!NMDetachUserReq+0x14RDPWD!NM_Disconnect+0x16RDPWD!SM_Disconnect+0x27RDPWD!SM_OnConnected+0x70RDPWD!NMAbortConnect+0x23RDPWD!NM_Connect+0x68RDPWD!SM_Connect+0x11dRDPWD!WDWConnect+0x557RDPWD!WDLIB_TShareConfConnect+0xa0RDPWD!WDSYS_Ioctl+0x6c9termdd!_IcaCallSd+0x37termdd!_IcaCallStack+0x57termdd!IcaDeviceControlStack+0x466termdd!IcaDeviceControl+0x59termdd!IcaDispatch+0x13fnt!IofCallDriver+0x63nt!IopSynchronousServiceTail+0x1f8nt!IopXxxControlFile+0x6aant!NtDeviceIoControlFile+0x2ant!KiFastCallEntry+0x12a

As first reported by Luigi Auriemma, the use-after-free vulnerability is located in the handling of the maxChannelIds field of the T.125 ConnectMCSPDU packet when set to a value less or equal to 5. That field was patched in the example above with 0.

According to Luigi, the problem happens during the disconnection of the user started with RDPWD!NM_Disconnect while the effect of the possible code execution is visible in termdd!IcaBufferAlloc.

The exploit is currently at the DoS (denial-of-service) stage of its evolution. This is bad enough, considering how many web sites are managed remotely via RDP and thus, could potentially be knocked down with a single packet of 443 bytes submitted by an attacker.

Unless someone has a crystal ball, it's hard to predict if this bug will ever evolve into a much more thrilling "remote code execution" stage - only time will tell that.