Service Desk (from debian.axhn@manchmal.in-ulm.de): gnutls: Missing authority check in the certificate revocation check routines
Greetings,
Summary
When validating a certificate against a given CRL (certificate revocation list), the GnuTLS library routines do not validate whether the presented CRL was issued by the same CA.
Exploiting this, possibly by modifying or replacing the CRL content during download which usually happens in the plain, a certificate can be made appear revoked although it was not. Or vice versa, a revocation of a certificate can be hidden.
How to repeat
Create two CAs, and for each one certificate with the same serial number. For the sake of this example - see attachment "CAs.tar" - where among other the following files are around:
goodCA/
root-ca.crt # CA certificate
crl.pem # CRL, empty
server1.crt # one created certificate, serial #1
badCA/
root-ca.crt # CA certificate
crl.pem # CRL, containing a revocation for the
# below certificate
server2.crt # one created certificate, serial #1, revoked
Scenario I
Verify the server1 certificate, signed by goodCA, using the badCA CRL:
certtool --verify \
--load-ca-certificate goodCA/root-ca.crt \
--load-crl badCA/crl.pem \
<goodCA/server1.crt
Expected:
The request is rejected for a CRL signature failure, non-zero exit.
Observed:
| Chain verification output: Not verified. The certificate is NOT trusted. The certificate chain is revoked. exit code 1
The server1 certificate is reported as revoked although it is not.
As this still reports an error, applications will probably refuse to continue. However, the error message is wrong and therefore misleading.
Comparing to openssl:
openssl verify -crl_check \
-CAfile goodCA/root-ca.crt \
-CRLfile badCA/crl.pem \
goodCA/server1.crt
Observed: | error 8 at 0 depth lookup: CRL signature failure | error goodCA/server1.crt: verification failed exit code: 2
Scenario II
The opposite constellation: Now verify the server2 certificate, signed by badCA, using the goodCA CRL:
certtool --verify \
--load-ca-certificate badCA/root-ca.crt \
--load-crl goodCA/crl.pem \
<badCA/server2.crt
Expected:
The request is rejected for using an untrusted CRL, exit non-zero.
Observed: | Chain verification output: Verified. The certificate is trusted. exit code: 0
Hence the revocation is effectively void.
Again, comparing to openSSL:
openssl verify -crl_check \
-CAfile badCA/root-ca.crt \
-CRLfile goodCA/crl.pem \
badCA/server2.crt
| error 8 at 0 depth lookup: CRL signature failure | error badCA/server2.crt: verification failed exit code: 2
How to repeat, helper script
For your convenience: In the attached "helper.tar", there is a script to re-generate the CA-related files. Unpack to an arbitrary directory, then run
./gen-x509-stuff goodCA server1
./gen-x509-stuff --revoke badCA server2
The script uses the openssl command but I'm quite confident using the respective gnutls command line tools wouldn't change a bit.
Versions used
Tests were made using the Debian unstable/sid packages of gnutls-bin 3.6.10-4 and openssl 1.1.1d-2:
$ gnutls-cli --version
gnutls-cli 3.6.10
$ openssl version
OpenSSL 1.1.1d 10 Sep 2019
Remark
This bug is old, perhaps existed since ever. I found while fixing a client with a broken TLS implementation in Debian, this was in October 2015 the latest. Nobody else encountered this problem and so far I bothered to report it - which could as well be seen as how little CRLs are used.
Regards,
Christoph