The recent GnuTLS certificate verification bug made it possible to craft an arbitrary certificate in a way that GnuTLS would validate correctly against a given CA root certificate store.
Since we started our SSL Notary service about a year ago, whenever a big SSL or TLS security incident happens, we scan all the certificates we have seen so far to see if they could have been used for an attack. For this bug, we dived into the GnuTLS source, examined the conditions that trigger the bug, and figured how an attacker would exploit the bug. Based on our analysis, we believe that there exist multiple methods to trigger the bug. In the following, we describe one that worked.
Looking at the patch that fixes this bug, we observe that the return codes for error cases were not set properly in a few examples. For example, the function
_gnutls_verify_certificate2 performs the actual certificate verification starting at line 465. GnuTLS first attempts to extract the part of the certificate that is signed, the signature, and the signature algorithm. If any of these lookups fail, the verification still succeeds due to missing adjustments of the variable
result, which denotes the function return value. On success, the function should return a non-zero value, but the code lacked checks to set the value to zero.
From an attacker perspective, making the signature lookup fail is probably the easiest method to exploit the bug. According to the function
_gnutls_x509_get_signature, the signature verification fails if the number of bits in the certificate signature is not divisible by 8. Certificate signatures have an ASN.1 bit string encoding, which theoretically may exhibit an arbitrary number of zeros and ones, and may not necessarily be divisible by 8. But the GnuTLS function assumes a byte-aligned signature, hence the check. In the ASN.1 DER encoding, used in X509 certificates, a bit string begins with a single byte which denotes the number of missing bits in the last byte. For all normal certificates, this value is always zero, and the function continues. But if we change this byte to a non-zero value, the function fails the divisibility check and returns with wrong error code, causing the certificate verification to succeed.
Moreover, we also looked at a few other entry points with the goal to make the signature algorithm verification fail. However, none of them seemed to be as easy to exploit as the signature algorithm.
To trigger the bug, we patched the certificate for bro.org, exchanged the private key, and tried to validate it with GnuTLS. Indeed, the verification succeeds with
gnutls-certtool and we could establish a secure connection to the server using
You can test the vulnerability of your GnuTLS installation by trying to connect to our test server:
gnutls-cli -p 443 gnutls.notary.icsi.berkeley.edu --x509cafile [root store]
You also can test it by downloading the following certificate chain and verifying it using
gnutls-certtool --verify --infile exploit.pem --load-ca-certificate [root store]
Usually, you can find your operating system root store in
/etc/ssl, or you can use our root-store copy. Note that the output of
gnutls-certtool is slightly truncated when using a chain that exploits the bug, nevertheless certificate verification (including hostname verification) works.
At the moment we are scanning all the certificates contained in the ICSI SSL Notary to see if there is any certificate that specifies a non 8-bit-divisible signature. Due to the size of our data set - at the moment it consists or more than 1.8 million certificates extracted from more than 50 billion connections - the scan is still running; so far there have been no hits.