Recently, Apple released a patch for a bug in its SSL handshake implementation on iOS and Mac OS X that allowed attackers to intercept SSL traffic originating from vulnerable devices. It turns out that the GnuTLS library also contained a bug that was patched on February 27, 2014; this bug also allows attackers to intercept SSL traffic. Let’s take a look at the GnuTLS issue.
The short story is that the function that performs certificate signature verification (among other things) in GnuTLS sometimes returns a non-zero value if an error occurs. The caller assumes that only a return value of zero indicates an error. So, the caller assumes that certificate signature verification passed when in reality it did not.
To exploit this vulnerability, the attacker would typically need to be on the same LAN as the victim with an application that uses a vulnerable version of GnuTLS. The attacker would perform a SSL man-in-the-middle attack when the victim tries to connect to a server and would present a specially crafted certificate that is not signed by a trusted certificate authority, but claims to be. The certificate should erroneously get accepted by the victim, and the attacker will be able to sniff/modify traffic between the victim and the server.
This issue also impacts certificate verification on the server side as well as certificate verification in other contexts (e.g. verifying OCSP responses)
This bug seems to affect all certificate verification in GnuTLS including client/server certificate verification during SSL handshakes as well as clients verifying that OCSP responses are signed using the right certificates. Certificate verification in GnuTLS goes through roughly the following steps:
- When certificate verification is required, the _gnutls_x509_verify_certificate function is called. If the provided certificate itself is trusted, then this function simply returns.
- Otherwise, certificate chain verification is required. It starts from the top of the chain and tries to ensure that the certificate is signed by a trusted certificate authority. Then, it walks down the certificate chain and verifies signatures, certificate attributes, etc. at each step. The actual verification at each step is performed by _gnutls_verify_certificate2, which is given the certificate to verify as well as a list of potential signers. For the first check, it is given the complete set of trusted certificate authorities, and for subsequent checks in the certificate chain, it is just given the previous certificate in the chain.
- The _gnutls_verify_certificate2 function verifies that the maximum certificate chain length of 4096 has not been exceeded, ensures that the given certificate is issued by one of the potential signers provided by _gnutls_x509_verify_certificate, verifies that the signer’s certificate is actually allowed to sign other certificates, verifies the actual signature on the certificate, verifies that the certificate is currently valid, etc.
If the above checks succeed, then the certificate is considered valid. Note that other verification steps like common name verification are performed elsewhere.
The bug is in the code that checks whether the signer’s certificate is actually allowed to sign other certificates. The _gnutls_verify_certificate2 function calls check_if_ca to see if the signer’s certificate contains the CA attribute. The check_if_ca function calls _gnutls_x509_get_signed_data and _gnutls_x509_get_signature, which return negative values if errors occur. For example, if the certificate signature length is not a multiple of 8 bits, _gnutls_x509_get_signature returns the value -43 (GNUTLS_E_CERTIFICATE_ERROR). Now, the actual bug is in how check_if_ca handles this return value:
If a negative value is received, further checks (including signature verification on the certificate) are bypassed, and the ‘goto cleanup’ call is executed. Here’s what the cleanup code looks like:
Note that the negative result will be returned to the caller. The caller (_gnutls_verify_certificate2) contains:
The caller assumes that a return value of zero indicates an error and that non-zero return values mean that everything is okay.
Let’s assume that this routine is verifying a certificate that claims to be signed by a trusted certificate authority (the issuer DN in the certificate matches one of the trusted CAs’ DN). If this certificate contains errors, the check_if_ca function will skip signature verification and return a negative value. The _gnutls_verify_certificate2 function will assume that everything is okay (i.e. that the certificate is indeed signed by a trusted CA). Eventually, certificate verification will succeed, and signature verification will never have been performed.
Would Certificate/Key Pinning Solve This?
I’m not familiar with GnuTLS’s support for certificate/key pinning; I have not experimented with the options listed at http://www.gnutls.org/manual/html_node/Certificate-verification.html. So, I cannot comment on that.
Who is Affected?
Anybody using software that uses GnuTLS is affected. Typically, this will be Linux applications. Both the client side and the server side for SSL connections are affected; however, the server side vulnerability only matters if its clients are being authenticated using certificates.
What Can You Do?
End users using Linux should update GnuTLS to version 3.2.12. This will not help with applications that are statically linked against an older version of GnuTLS; you will need to update those separately.
If your application uses GnuTLS and is dynamically linked against it, encourage your users to install the latest version of GnuTLS. If your application is statically linked against GnuTLS, you’ll need to push out an application update linked against the latest version of GnuTLS.
+Amit Sethi is a Technical Manager at Cigital. His software security expertise includes architectural risk analysis, threat modeling, secure code reviews, penetration testing, and more. He has helped many multi-national corporations solve complex security problems and is a contributor to The Justice League Blog.