Please read our main article, Anatomy of a "goto fail" - Apple's SSL bug explained, plus an unofficial patch! before going any further.
Note that you must have Apple's developer tools (Xcode) installed before you proceed, because you need to use the codesign utility to authorise your patched version of the SecureTransport system library.
The patched file will be signed "ad-hoc," so it can ony be used on your Mac.
So if you try it, and it works, please don't redistribute your modified file, for licensing and safety reasons.
What we're patching is the double-goto-fail bug in sslKeyExchange.c.
This bug is also known as CVE-2014-1266:
. . hashOut.data = hashes + SSL_MD5_DIGEST_LEN; hashOut.length = SSL_SHA1_DIGEST_LEN; if ((err = SSLFreeBuffer(&hashCtx)) != 0) goto fail; if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; /* MISTAKE! THIS LINE SHOULD NOT BE HERE */ if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; err = sslRawVerify(...); . . .
The double-goto-fail code in Apple's library compiles to the following assembler code:
086c39 7561 jnz fail 086c3b 4c8d3d860f1e00 lea r15, [rip+_SSLHashSHA1] 086c42 488db538ffffff lea rsi, [rbp-0xc8] 086c49 4c89ff mov rdi, r15 086c4c e833a1fcff call _ReadyHash 086c51 89c3 mov ebx, eax 086c53 85db test ebx, ebx 086c55 7545 jnz fail 086c57 4d8b7718 mov r14, [r15+0x18] 086c5b 488dbd38ffffff lea rdi, [rbp-0xc8] 086c62 488db528ffffff lea rsi, [rbp-0xd8] 086c69 41ffd6 call r14 086c6c 89c3 mov ebx, eax 086c6e 85db test ebx, ebx 086c70 752a jnz fail 086c72 488dbd38ffffff lea rdi, [rbp-0xc8] 086c79 488db518ffffff lea rsi, [rbp-0xe8] 086c80 41ffd6 call r14 086c83 89c3 mov ebx, eax 086c85 85db test ebx, ebx 086c87 7513 jnz fail 086c89 488dbd38ffffff lea rdi, [rbp-0xc8] 086c90 488db558ffffff lea rsi, [rbp-0xa8] 086c97 41ffd6 call r14 086c9a 89c3 mov ebx, eax 086c9c 488dbd08ffffff lea rdi, [rbp-0xf8] 086ca3 e8d875fcff call _SSLFreeBuffer
The snippets that do this:
call r14 ; Call HashSHA1.update() mov ebx, eax ; Put return value in EBX test ebx, ebx ; Test for EBX non zero (i.e. error) jnz fail ; If so, goto fail
are followed by the buggy one, which you might have thought would compile something like this:
call r14 ; Call HashSHA1.update() mov ebx, eax ; Put return value in EBX test ebx, ebx ; Test for EBX non zero (i.e. error) jnz fail ; If so, goto fail jmp fail ; And then wrongly goto fail anyway
If that were the case, we could just patch away the incorrect jump, and life would be good.
But compiler optimisation has given us:
call r14 ; Call HashSHA1.update() mov ebx, eax ; Get return value - no check needed lea rdi, [rbp-0xf8] ; And prepare to exit regardless call _SSLFreeBuffer
The code that the incorrect goto fail skipped over has been optimised away, so there is no call to the HashSHA1.final() function, and so no easy way to call the sslRawVerify() code at all.
In short, the code can't be patched to work properly, but it can be patched to fail safely all the time, in contrast with its current tendency to fail wrongly.
We'll change the buggy code by "borrowing" the call r14 instruction to produce:
mov ebx, 0x1 lea rdi, [rbp-0xf8] call _SSLFreeBuffer
The function now always fails with a non-zero result (I chose the value 1), whether there was a real error or merely the error caused by the bug.
So, entirely for academic interest (please don't try this at home!), you could do this:
Now change the following bytes in the copy of the library file you just made (I used the free Hex Fiend binary editor):
That should change the code in the library as follows:
Now add your own digital signature to the patched file so OS X will accept it, and copy the patched-and-signed file into place in the /System directory:
Reboot, and you should have the new, hackily-patched library in place.
The codesign step above signs the hacked library with an "ad-hoc" signature, meaning that it will work on your Mac only.
That's just as well.
You can't spread this untested fix to others: they have to take the steps for themselves.
To undo the change if it doesn't work, copy the Security.original.in-case back over the patched version of Security and reboot once more.
For a "before-and-after" test of the patch, visit SSL expert Adam Langley's excellent test site https://www.imperialviolet.org:1266/ before you patch:
Then try again after you patch; Safari should be stopped short by the error that is now correctly reported by SecureTransport:
Remember, however: this patch is unvalidated, unwarranted and not recommended for actual use.
- For fun.
- To suggest that emergency "fixes" don't always fix, but often can only work around problems.
- To show what C code looks like when compiled to assembler.
- To give some insight into how unauthorised hacks, for good and bad, can be achieved.
- To introduce the OS X codesign utility and Apple's code signing protection.