The comments table has been repopulated! I’m currently working on restoring their hierarchy. For ease, I’m working backwards, from the newest to the oldest posts. Please bear with any site instability/sluggishness you might encounter. Cheers! ☺

On Apple’s recent SSL/TLS bug

Written by Raymond Santos Estrella on Sunday, 23 February 2014. Posted in 2014

On Apple’s recent SSL/TLS bug

A few days ago, Apple issued a security update for iOS, bringing the OS up to level 7.0.6. That update cryptically stated that there was a bug in the way iOS was handling SSL/TLS but, in true Apple fashion, remained too vague to be fully understood. Since then, I’ve had the misfortune of reading quite a lot of fear, misinformation and distrust (FUD) being spread about the net. Apple, of course, could have just documented everything and prevented this sort of thing from happening but, well, whatever. In any case, I’ll take a shot at explaining what went wrong in previous iOS versions and what they fixed, such as I can gather with my own efforts.

The bug in question concerns this piece of code:

static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
                                 uint8_t *signature, UInt16 signatureLen)
{
	OSStatus        err;
	...

	if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
		goto fail;
	if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
		goto fail;
		goto fail;
	if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
		goto fail;
	...

fail:
	SSLFreeBuffer(&signedHashes);
	SSLFreeBuffer(&hashCtx);
	return err;
}

(Quoted from Apple’s published source code.)

See anything out of the ordinary? How about those two subsequent goto fail lines. While the first goto fail command is correctly bound to the if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) statement, the second, despite the proper code indentation, isn’t bound to any condition at all. The code will always jump to the end from that second goto, err will contain a successful value because the SHA1 update operation was successful and so the signature verification will never fail.

This signature verification is checking the signature in a ServerKeyExchange message. This is used in Diffie–Hellman (DHE) and Elliptic curve Diffie–Hellman (ECDHE) ciphersuites to communicate the ephemeral key for the connection. We know that in an encrypted server-client exchange, it’s as if the server is saying “here’s the ephemeral key and here’s a signature, from my SSL certificate, so you know that it’s really from me”. If the link between the ephemeral key and the certificate chain is broken, then everything absolutely falls apart. It’s possible to send a correct certificate chain to the client, but sign the handshake with the wrong private key, or not sign it at all! There’s no proof that the server possesses the private key matching the public key in its certificate.

Since this is in SecureTransport, it affects iOS from some point prior to 7.0.6 (I have confirmed on 7.0.4) and also OSX (also confirmed on 10.9.1). It affects anything that uses SecureTransport, which is most software on those platforms without their own built-in key exchange. Luckily, Chrome and Firefox are not affected, as both use NSS for SSL/TLS. However, that doesn’t mean very much if, say, the software update systems on your machine might be using SecureTransport.

Since I was bored, I tried duplicating this issue on a local webserver-capable machine that was opened up to the internet sans router. Visiting something like https://125.123.82.129:1266. Note that under normal circumstances, loading a secure site happens on port 443. However, if you’re able to load the site on port 1266, the server is sending the same certificates but signing it with a completely different key. If you can load an HTTPS site on port 1266 with your device, then you have this bug.

In previous versions of iOS and OSX, loading that site will show that everything is in order in the certificate chain. Rather, it’s the link between the handshake to that certificate chain which is broken. I personally don’t believe any sort of certificate pinning would have stopped such a bug like this. Also, this doesn’t only affect sites using DHE or ECDHE ciphersuites—the attacker gets to choose the ciphersuite in this case and will choose the one that works for them.

Note, however, that this doesn’t affect TLS 1.2 because there’s a different function for verifying the different ServerKeyExchange message in TLS 1.2. But, again, the attacker can choose any version that the client will accept. But if the client only enables TLS 1.2 to the exclusion of any other previous version, then it logically seems that this issue wouldn’t surface. Likewise, if the client only enabled the plain RSA ciphersuites then there’s no ServerKeyExchange and that should also work around this issue. (Of the two, the former workaround is much more preferable.)

Based on my test site, iOS 7.0.6 does fix the issue but OSX 10.9.1 is still affected. (Update: reading up on others’ experience and confirmations, it looks like the bug was introduced in 10.9 for OSX but existed in at least some versions of iOS 6. iOS 6.1.6 for older devices was released yesterday to fix it.)

This is a very subtle bug buried deep in the code. While the developer might not be absolutely blameless for introducing the double goto statements, I personally believe that any punishment due should be mitigated by the obscure and easily-missed nature of this bug. It’s one line of code!

Here’s a stripped down piece of code with the same issue:

extern int f();

int g() {
	int ret = 1;

	goto out;
	ret = f();

out:
	return ret;
}

If I compile this code with the -Wall (enable all warnings) switch, neither GCC 4.8.2 or Clang 3.3 from Xcode display any messages about the dead code. While I’m not absolutely surprised, I would have thought that such an occurence in any codebase would give at least some semblance of warning. Peter Nelson points out that compiling with the -Wunreachable-code switch in Clang would warn about this, but it’s definitely not in -Wall.)

Conclusion

I’ve seen some comments blaming the coding style on this bug by allowing ifs wtihout braces. Well, while it’s a not too unreasonable conclusion, one can have incorrect indentation with braces too, so that doesn’t seem terribly convincing to me.

A test case could have caught this, but it seems overly difficult to write one because the bug is so deep within the handshake. One would need to write a completely separate TLS stack with lots of options for sending invalid handshakes. The folks at Apple probably (hopefully) have a test like this in place to prevent future bugs like this.

Code review can be effective against these sorts of bug. Not just auditing, but review of each change as it goes in. Of course, this adds some pecuniary cost to coding in terms of more people and man-hours expended but I’m reasonably confident that such a glaring bug could have been spotted at some point in time relatively close to it’s introduction into the codebase. Since I’m not familiar with Apple’s policies with regard to code audits and reviews so I’d rather not say any more without further information.

Penultimately, there was a lot of discussion these past few days that Apple missed checking the hostname in the certificate. It’s true that curl on the OSX command line oddly accepts HTTPS connections to IP addresses when the IP address isn’t in the certificate, but I can’t find that there’s anything more than that and Safari itself doesn’t have that problem.

Finally, please update your devices as soon as you can to prevent any kind of exploits that are sure to pop up now that the existence of the bug has become public.

Share This Article

About the Author

Raymond

Raymond Santos Estrella

I guess I should really make a proper writeup here. Something witty or maybe a joke to add some levity. I’ll come back to this when I have time. If you have any suggested copy that I can insert here, drop me a line.

Comments (4)

  • daikama

    daikama

    12 February 2014 at 07:54 |

    Woohoo! Code!

    reply

  • Raymond

    Raymond

    12 February 2014 at 08:07 |

    haha Yeah, haven't written any coding-related thing in a while. Here you go!

    reply

  • KTNO

    KTNO

    12 February 2014 at 10:12 |

    Judging from the quick uptake of firmware updates, this thing is virtually sure to be eliminated from a vast majority of iDevices.

    reply

  • reagal

    reagal

    12 February 2014 at 11:26 |

    In hindsight, Apple’s SSL bug seems less disastrous as OpenSSL’s heartbeat exploit, doesn’t it?

    reply

Leave a comment

You are commenting as guest. Optional login below.