Skip to content
GitLab
Next
    • GitLab: the DevOps platform
    • Explore GitLab
    • Install GitLab
    • How GitLab compares
    • Get started
    • GitLab docs
    • GitLab Learn
  • Pricing
  • Talk to an expert
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
    Projects Groups Topics Snippets
  • Register
  • Sign in
  • GnuTLS GnuTLS
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributor statistics
    • Graph
    • Compare revisions
    • Locked files
  • Issues 274
    • Issues 274
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
    • Requirements
  • Merge requests 22
    • Merge requests 22
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Artifacts
    • Schedules
    • Test cases
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Model experiments
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Wiki
    • Wiki
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • gnutlsgnutls
  • GnuTLSGnuTLS
  • Issues
  • #694
Closed
Open
Issue created Feb 05, 2019 by Tavis Ormandy@taviso

SECURITY: CVE-2019-3829: critical use after free vulnerability in verify_crt()

Description of problem:

This is a critical memory corruption vulnerability in any API backed by verify_crt(), including gnutls_x509_trust_list_verify_crt() and related routines. I suspect any client or server that verifies X.509 certificates with GnuTLS is likely affected and can be compromised by a malicious server or active network attacker.

In multi-threaded-clients this is a use-after-free vulnerability, and a double-free vulnerability in single-threaded clients.

The core bug is that _gnutls_x509_get_signature does not clear signature->data in the cleanup path:

lib/x509/common.c

 cleanup:
	gnutls_free(signature->data); // <- pointer in datum parameter freed, but not cleared
	return result;
}

Callers like check_if_ca assume that if _gnutls_x509_get_signature ever sets that parameter, then it can be safely freed, but that is not true:

lib/x509/verify.c

	ret =
	    _gnutls_x509_get_signature(cert->cert, "signature",
				       &cert_signature);
	if (ret < 0) {
		gnutls_assert();
		goto fail;
	}
        // ...
 fail:
	result = 0;

 cleanup:
	_gnutls_free_datum(&cert_signed_data);
	_gnutls_free_datum(&issuer_signed_data);
	_gnutls_free_datum(&cert_signature);   // <--- freed again
	_gnutls_free_datum(&issuer_signature);
	return result;
}

Version of gnutls used:

gnutls-3.6.6.tar.xz

Distributor of gnutls (e.g., Ubuntu, Fedora, RHEL)

Built from source.

How reproducible: 100%

Steps to Reproduce:

  • Download the attached PEM bundle and save it as _gnutls_x509_get_signature.pem
  • Run certtool --verify-chain --infile _gnutls_x509_get_signature.pem

Actual results:

        Subject: CN=VeriSign Class 3 Code Signing 2010 CA,OU=Terms of use at https://www.verisign.com/rpa (c)10,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
        Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU=(c) 2006 VeriSign\, Inc. - For authorized use only,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
        Signature algorithm: RSA-SHA1
        Output: Not verified. The certificate is NOT trusted. The certificate issuer is unknown. The certificate chain uses insecure algorithm.

        Subject: CN=VeriSign Class 3 Code Signing 2010 CA,OU=Terms of use at https://www.verisign.com/rpa (c)10,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
        Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU=(c) 2006 VeriSign\, Inc. - For authorized use only,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
        Checked against: CN=VeriSign Class 3 Code Signing 2010 CA,OU=Terms of use at https://www.verisign.com/rpa (c)10,OU=VeriSign Trust Network,O=VeriSign\, Inc.,C=US
        Signature algorithm: RSA-SHA1
        Output: Verified. The certificate is trusted.

*** Error in `certtool': double free or corruption (!prev): 0x000056069d657ef0 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bcb)[0x7fc502b8ebcb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76f96)[0x7fc502b94f96]
/lib/x86_64-linux-gnu/libc.so.6(+0x777de)[0x7fc502b957de]
/usr/lib/x86_64-linux-gnu/libgnutls.so.30(+0xcfe50)[0x7fc50437ee50]
/usr/lib/x86_64-linux-gnu/libgnutls.so.30(+0xd0f76)[0x7fc50437ff76]
/usr/lib/x86_64-linux-gnu/libgnutls.so.30(gnutls_x509_trust_list_verify_crt2+0x44c)[0x7fc50438fe8c]
/usr/lib/x86_64-linux-gnu/libgnutls.so.30(gnutls_x509_trust_list_verify_crt+0x15)[0x7fc504390385]
certtool(+0xdff0)[0x56069cda2ff0]
certtool(+0x13570)[0x56069cda8570]
certtool(+0xc5c9)[0x56069cda15c9]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7fc502b3e2b1]
certtool(+0xc60a)[0x56069cda160a]

Expected results:

No memory corruption.

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff65a83fa in __GI_abort () at abort.c:89
#2  0x00007ffff65e4bd0 in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7ffff66d9d58 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007ffff65eaf96 in malloc_printerr (action=3, str=0x7ffff66d9dd0 "double free or corruption (!prev)", ptr=<optimized out>, ar_ptr=<optimized out>) at malloc.c:5049
#4  0x00007ffff65eb7de in _int_free (av=0x7ffff690db00 <main_arena>, p=0x5555557ddf00, have_lock=0) at malloc.c:3905
#5  0x00007ffff7aa14e4 in _gnutls_free_datum (dat=0x7fffffffcf70) at ./../datum.h:47
#6  0x00007ffff7aa1dab in check_if_ca (cert=0x5555557ce9d0, issuer=0x5555557c7d50, max_path=0x7fffffffd0a8, flags=4) at verify.c:244
#7  0x00007ffff7aa56c4 in verify_crt (cert=0x5555557ce9d0, trusted_cas=0x555555797880, tcas_size=1, flags=4, output=0x7fffffffd0d0, vparams=0x7fffffffd0a0, end_cert=1) at verify.c:732
#8  0x00007ffff7aa604c in _gnutls_verify_crt_status (certificate_list=0x7fffffffd160, clist_size=1, trusted_cas=0x555555797880, tcas_size=1, flags=4, purpose=0x0, func=0x55555556536a <detailed_verification>) at verify.c:975
#9  0x00007ffff7abcd97 in gnutls_x509_trust_list_verify_crt2 (list=0x5555557c2ab0, cert_list=0x7fffffffd160, cert_list_size=2, data=0x0, elements=0, flags=4, voutput=0x7fffffffd350, func=0x55555556536a <detailed_verification>) at verify-high.c:1366
#10 0x00007ffff7abc44b in gnutls_x509_trust_list_verify_crt (list=0x5555557c2ab0, cert_list=0x5555557c42e0, cert_list_size=2, flags=4, voutput=0x7fffffffd350, func=0x55555556536a <detailed_verification>) at verify-high.c:1197
#11 0x0000555555565f91 in _verify_x509_mem (cert=0x5555557bfeb0, cert_size=7141, cinfo=0x7fffffffd400, use_system_trust=0, purpose=0x0, hostname=0x0, email=0x0) at certtool.c:2396
#12 0x0000555555566245 in verify_chain (cinfo=0x7fffffffd400) at certtool.c:2466
#13 0x0000555555563867 in cmd_parser (argc=4, argv=0x7fffffffd5e8) at certtool.c:1406
#14 0x00005555555605ff in main (argc=4, argv=0x7fffffffd5e8) at certtool.c:126
(gdb) frame 6
#6  0x00007ffff7aa1dab in check_if_ca (cert=0x5555557ce9d0, issuer=0x5555557c7d50, max_path=0x7fffffffd0a8, flags=4) at verify.c:244
244             _gnutls_free_datum(&cert_signature);
(gdb) p cert_signature 
$1 = {data = 0x5555557ddf10 "Xې\366\377\177", size = 0}
(gdb) 

I have verified this patch against HEAD fixes the issue:

diff --git a/lib/x509/common.c b/lib/x509/common.c
index 9ce427522..3f9e04202 100644
--- a/lib/x509/common.c
+++ b/lib/x509/common.c
@@ -1366,6 +1366,8 @@ _gnutls_x509_get_signature(ASN1_TYPE src, const char *src_name,
 
  cleanup:
        gnutls_free(signature->data);
+       signature->data = NULL;
+       signature->size = 0;
        return result;
 }

This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public.

Edited Feb 14, 2019 by Nikos Mavrogiannopoulos
Assignee
Assign to
Time tracking