Triggering MS14-066

BeyondTrust Research Team, November 17th, 2014

Microsoft addressed CVE-2014-6321 this Patch Tuesday, which has been hyped as the next Heartbleed.  This vulnerability (actually at least 2 vulnerabilities) promises remote code execution in applications that use the SChannel Security Service Provider, such as Microsoft Internet Information Services (IIS).

The details have been scarce.  Lets fix that.

1

Looking at the bindiff of schannel.dll, we see a few changed functions, several touching the DTLSCookieManager class and various others.  There is at least one bug addressed in DTLSCookieManager, but that one is for a different time.  The one everyone is worried about in seems to be in schannel!DecodeSigAndReverse(…).

A high level view of the changes to DecodeSigAndReverse(…) are presented below.

2

Here we can see that there is some new logic  (grey blocks) added to the middle of the patched function (right side).  Added branches are always a good sign.  If we zoom in on the patched version, the situation looks even more promising.

asdfasdf

 

 

We can now see that the added logic controls a path to a memcpy (actually two memcpys — they wouldn’t both fit in the screenshot).  This is an indication that we are looking in the right place.

So how do we get here?  Lets look at the paths to this function in the unpatched version of schannel.dll

4

 

So, it appears as though we need to hit ‘ProcessHandshake’ and then craft a ‘ClientVerifyMessage’ in order to hit the changed code.  To accomplish this, we should probably check out the TLS/SSL documentation at MSDN.

5

Given the names of the function in the codepath, it would make sense that we are dealing with a  Certificate Verify message which is involved in certificate authentication.  If we take a closer look at the unpatched function, we can get a key clue from the lpszStructType parameter in the call to CryptDecodeObject.

 

6

With a quick trip to MSDN, we can see that this parameter is telling us what kind of structure to expect.  In this case we have X509_ECC_SIGNATURE and X509_DSS_SIGNATURE.  Picking on the ECC_SIGNATURE, the expected structure is defined on MSDN

7

It appears as though there could be an issue with the size parameter to one of the memcpys, probably related to encoding the certificate.

At this point with what we know, the fastest way for us to proceed is to look at this function dynamically (with a debugger).  So, we created an ECDSA signed certificate with OpenSSL and setup Microsoft IIS with certificate authentication enabled. We then attached a remote debugger to the LSASS process on the IIS box and breakpointed the ECC_SIGNATURE comparison (cmp ebx, 2F).

Surprisingly the breakpoint fired when attempting to authenticate using openssl s_client on the first try!

Now that we can hit the bad code, the next step is making something cool happen here.  Again, to speed up analysis, we decided to modify OpenSSL to fuzz this code path.

In OpenSSL, ECDSA signatures for ‘client verify messages’ are handled in the source file s3_clnt.c.  The encoded signatures from the client which end up hitting the CryptDecodeObject(…) call in schannel!DecodeSigAndReverse(…) come from a function called ECDSA_sign(…).  If we wander down the function ssl3_send_client_verify(…) which eventually calls ECDSA_sign(…), we get to this block which actually handles the ECDSA signing for  our client verify message:

8

To clarify this call, the function prototype for ECDSA_sign is as follows:

 int         ECDSA_sign(int type, const unsigned char *dgst,
                        int dgstlen, unsigned char *sig,
                        unsigned int *siglen, EC_KEY *eckey);

Reading the documentation for that function we learn that  “…The DER encoded signatures is stored in sig and it’s length is returned in sig_len.”

Therefore, if we were to use openssl s_client to authenticate to our IIS box and then were to single step through schannel!DecodeSigAndReverse(…), we would see the contents of ‘p’ from the above call to ECDSA_sign(…) being handed to CryptDecodeObject(…) in schannel, which would then be translated and handed off to our bad memcpy block.

So, all we really need to do is to edit s3_clnt.c to randomly change one byte in ‘p’ to a random value before sending our Certificate Verify message back to IIS over and over again and wait until something cool happens.

And if we wait long enough, it will – we will get a crash in memcpy.

Capture

Further analysis and exploitation are left as an exercise to the reader.