Every patch Tuesday, we, like many in the security industry, love to analyze the released patches and see if any of them can lead to the development of a working exploit. Recently, the MS11-080 advisory caught our attention as it afforded us the opportunity to play in the kernel and try to get a working privilege escalation exploit out of it.
After downloading the patch from the Microsoft website, we extracted it, decompiled the afd.sys driver, and ran a diff on an unpatched version. The results of our diff showed that only the AfdJoinLeaf function was changed:
In analyzing the vulnerable AfdJoinLeaf function, we determined that it could be triggered by sending a specific IOCTL (0x120bb) to the afd driver:
From the patch diff, we saw that only one code chunk was changed at 00016CFC but in order to reach the block, a few branches in the code need to be taken by precisely crafting the DeviceIOControlFile input buffer beginning with the Input/Output buffer size:
Our next requirement is that the 4th DWORD in the input buffer must be 0×00000001:
The value pointed to by (input buffer + 0×10) increased by 0×8 must be less or equal to (input buffer size – 0xC):
At this point, we have bypassed the necessary checks and have made it to the vulnerable code chunk:
Now, by setting the size of the output buffer to 0, we can bypass the code that checks if the output buffer is a writable address residing in user space. By circumventing this check, we will be able to specify any kernel address we choose as the address for the output buffer.
However, in the AfdJoinLeaf function, there doesn’t seem to be an obvious way to trigger the pointer overwrite. One possible path is to craft our attack to reach the call to AfdRestartJoin in the basic block at 00016F54:
This function, once reached, will eventually call AfdConnectApcKernelRoutine:
AfdConnectApcKernelRoutine will try to write a NTSTATUS code in the IRP output buffer. In order to reach AfdRestartJoin, we once again need to take a couple of branches in the right direction, beginning with the branch at 0x00016DBD. The stack variable input+8 must be equal to 0×00000000:
In the branch at 0x00016FEA, the socket state is checked. We need to have a TCP socket in the CONNECTING state (0×2) in order to take the jump and get to AfdRestartJoin:
Now, with the various checks successfully bypassed, we reach the AfdRestartJoin function:
Following along, AfdRestartJoin calls AfdConnectApcKernelRoutine as expected:
We are getting closer to the end at this point but we are still passing an invalid kernel address for the output buffer:
Not surprisingly, this invalid address causes AfdConnectApcKernelRoutine to crash while trying to write an NTSTATUS code (STATUS_INVALID_ADDRESS_COMPONENT) to the provided address:
The hard work is all done up to this point although we don’t entirely control the DWORD that will be written to the output buffer address as the address will a NTSTATUS code (in our case, 0xC0000207). This bug then, is not quite a “write what, where” but all is not lost as we can still write wherever we want to in kernel space. Furthermore, by writing the 0xC0000207 value in a misaligned way, we will be able to call our shellcode in user space at 0x000207XX:
We decided to use a token-stealing shellcode in order to escalate to SYSTEM privileges on our vulnerable machine. To view the full code, check out our MS11-080 Privilege Escalation Exploit that works on 32-bit Win XPSP3 and Win 2K3SP2 Standard/Enterprise.