Mu Dynamics, Inc. Security Advisories MU-201202-01 and MU-201202-02 for GnuTLS and Libtasn1
Security
security at mudynamics.com
Wed Mar 21 00:40:02 CET 2012
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Mu Dynamics, Inc. Security Advisories MU-201202-01 and MU-201202-02 for GnuTLS and Libtasn1
TLS record handling vulnerability in GnuTLS [MU-201202-01]
ASN.1 length decoding vulnerability in Libtasn1 [MU-201202-02]
20 March 2012
http://blog.mudynamics.com/2012/03/20/gnutls-and-libtasn1-vulns/
http://labs.mudynamics.com/advisories.html
Affected Products/Versions:
* libgnutls up to 3.0.16.
* libtasn1 up to 2.11.
Product Overview:
GnuTLS is an open source implementation of SSL, TLS and DTLS, with APIs for
encrypted network communications, along with X.509, PKCS #12, OpenPGP, and
other security data types.
Analysis:
Details for TLS record handling vulnerability in GnuTLS [MU-201202-01]:
The block cipher decryption logic in GnuTLS assumed that a record containing
any data which was a multiple of the block size was valid for further
decryption processing, leading to a heap corruption vulnerability.
The bug can be reproduced in GnuTLS 3.0.14 by creating a corrupt
GenericBlockCipher struct with a valid IV, while everything else is stripped
off the end, while the handshake message length retains its original value:
struct {
opaque IV[SecurityParameters.record_iv_length];
// corrupt: below items not sent
/*
block-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[SecurityParameters.mac_length];
uint8 padding[GenericBlockCipher.padding_length];
uint8 padding_length;
};
*/
} GenericBlockCipher;
This will cause a segmentation fault, when the ciphertext_to_compressed
function tries to give decrypted data to _gnutls_auth_cipher_add_auth for HMAC
verification, even though the data length is invalid, and it should have
returned GNUTLS_E_DECRYPTION_FAILED or GNUTLS_E_UNEXPECTED_PACKET_LENGTH
instead, before _gnutls_auth_cipher_add_auth was called.
Since the error was not returned soon enough, all of the various operations
ciphertext_to_compressed performs: i.e. setting the IV, removing the padding,
setting the "true" data length with the padding stripped, checking the padding
size and padding payload and verifying HMAC could all reference undefined,
unallocated, or uninitialized memory.
There could be similar ways to reproduce this for AEAD ciphers due to the
various flows through this code, but we did not attempt to do this, and see it
as a topic for further investigation.
Below we trace the execution of the ciphertext_to_compressed function from
lib/gnutls_cipher.c. The unsafe operations and missed opportunities to return
before the heap corruption happens are marked with "***** ... *****" :
433 static int
434 ciphertext_to_compressed (gnutls_session_t session,
435 gnutls_datum_t *ciphertext,
436 uint8_t * compress_data,
437 int compress_size,
438 uint8_t type, record_parameters_st * params,
439 uint64* sequence)
440 {
...
511 case CIPHER_BLOCK:
512 if (ciphertext->size < MAX(blocksize, tag_size) || (ciphertext->size % blocksize != 0)) ***** UNSAFE *****
513 return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
514
515 /* ignore the IV in TLS 1.1+
516 */
517 if (explicit_iv)
518 {
519 _gnutls_auth_cipher_setiv(¶ms->read.cipher_state,
520 ciphertext->data, blocksize);
521
522 ciphertext->size -= blocksize;
523 ciphertext->data += blocksize;
524
525 if (ciphertext->size == 0) ***** UNSAFE *****
526 {
527 gnutls_assert ();
528 return GNUTLS_E_DECRYPTION_FAILED;
529 }
530 }
...
537 if ((ret =
538 _gnutls_cipher_decrypt (¶ms->read.cipher_state.cipher,
539 ciphertext->data, ciphertext->size)) < 0)
540 return gnutls_assert_val(ret);
541
542 pad = ciphertext->data[ciphertext->size - 1] + 1; /* pad */
543
544 if ((int) pad > (int) ciphertext->size - tag_size)
545 {
546 gnutls_assert ();
547 _gnutls_record_log
548 ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
549 session, pad, ciphertext->size, tag_size); ***** Message Appears During The Attack *****
550 /* We do not fail here. We check below for the
551 * the pad_failed. If zero means success.
552 */
553 pad_failed = GNUTLS_E_DECRYPTION_FAILED; ***** Execution Continues Anyway *****
554 pad %= blocksize;
555 }
556
557 length = ciphertext->size - tag_size - pad;
558
559 /* Check the padding bytes (TLS 1.x) */
...
577 /* Pass the type, version, length and compressed through
578 * MAC.
579 */
580 preamble_size =
581 make_preamble (UINT64DATA(*sequence), type,
582 length, ver, preamble);
583 ret = _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size);
584 if (ret < 0)
585 return gnutls_assert_val(ret);
586
587 ret = _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, ciphertext->data, length); ***** UNSAFE, crashes here *****
588 if (ret < 0)
589 return gnutls_assert_val(ret); ***** Crashes Before Error Is Returned *****
...
The segmentation fault appears as follows in GDB:
Program received signal SIGSEGV, Segmentation fault.
0x003b9946 in _nettle_sha256_compress (state=0x807f128,
input=0x808f000 <Address 0x808f000 out of bounds>, k=0x3cdb60)
at sha256-compress.c:111
111 sha256-compress.c: No such file or directory.
in sha256-compress.c
(gdb) bt
#0 0x003b9946 in _nettle_sha256_compress (state=0x807f128,
input=0x808f000 <Address 0x808f000 out of bounds>, k=0x3cdb60)
at sha256-compress.c:111
#1 0x003b961b in nettle_sha256_update (ctx=0x807f128, length=4294916861,
data=0x808effc "") at sha256.c:92
#2 0x003b336d in nettle_hmac_sha256_update (ctx=0x807f050, length=4294967280,
data=0x8082b09 '\017' <repeats 16 times>) at hmac-sha256.c:43
#3 0x0021a749 in wrap_nettle_hmac_update (_ctx=0x807f050, text=0x8082b09,
textsize=4294967280) at mac.c:231
#4 0x00158233 in _gnutls_hmac (handle=0x807ef9c, text=0x8082b09,
textlen=4294967280) at ./gnutls_hash_int.h:73
#5 0x00158b35 in _gnutls_auth_cipher_add_auth (handle=0x807ef78,
text=0x8082b09, textlen=-16) at gnutls_cipher_int.c:190
#6 0x001473de in ciphertext_to_compressed (session=0x807d810,
ciphertext=0xbfffe8a4, compress_data=0x8083da4 "", compress_size=16384,
type=22 '\026', params=0x807ed48, sequence=0x807efcc)
at gnutls_cipher.c:587
#7 0x00145cdc in _gnutls_decrypt (session=0x807d810,
ciphertext=0x8082af9 "\252\257C/7\301\362\352h|d\275#\312\027\312", '\017' <repeats 16 times>, ciphertext_size=32, data=0x8083da4 "", max_data_size=16384,
type=GNUTLS_HANDSHAKE, params=0x807ed48, sequence=0x807efcc)
at gnutls_cipher.c:159
...
(gdb)
The segmentation fault appears as follows in Valgrind Memcheck:
==29586== Invalid read of size 1
==29586== at 0x40274B9: memcpy (mc_replace_strmem.c:497)
==29586== by 0x42BC5A6: nettle_sha256_update (sha256.c:92)
==29586== by 0x42B636C: nettle_hmac_sha256_update (hmac-sha256.c:43)
==29586== by 0x411C748: wrap_nettle_hmac_update (mac.c:231)
==29586== by 0x405A232: _gnutls_hmac (gnutls_hash_int.h:73)
==29586== by 0x405AB34: _gnutls_auth_cipher_add_auth (gnutls_cipher_int.c:190)
==29586== by 0x40493DD: ciphertext_to_compressed (gnutls_cipher.c:587)
==29586== by 0x4047CDB: _gnutls_decrypt (gnutls_cipher.c:159)
...
==29586== Address 0x4464411 is 0 bytes after a block of size 89 alloc'd
==29586== at 0x4024F12: calloc (vg_replace_malloc.c:467)
==29586== by 0x4049AE4: _mbuffer_alloc (gnutls_mbuffers.c:288)
==29586== by 0x4049C49: _mbuffer_linearize (gnutls_mbuffers.c:349)
==29586== by 0x40462FB: _gnutls_recv_in_buffers (gnutls_record.c:996)
==29586== by 0x404D01C: _gnutls_handshake_io_recv_int (gnutls_buffers.c:1174)
==29586== by 0x4050383: _gnutls_recv_handshake (gnutls_handshake.c:1260)
...
==29586== Invalid read of size 1
...
==29586== Address 0x4464412 is 1 bytes after a block of size 89 alloc'd
...
==29586== Process terminating with default action of signal 11 (SIGSEGV)
==29586== Access not within mapped region at address 0x4779000
==29586== at 0x42BC946: _nettle_sha256_compress (sha256-compress.c:111)
==29586== by 0x42BC61A: nettle_sha256_update (sha256.c:92)
==29586== by 0x42B636C: nettle_hmac_sha256_update (hmac-sha256.c:43)
==29586== by 0x411C748: wrap_nettle_hmac_update (mac.c:231)
==29586== by 0x405A232: _gnutls_hmac (gnutls_hash_int.h:73)
==29586== by 0x405AB34: _gnutls_auth_cipher_add_auth (gnutls_cipher_int.c:190)
==29586== by 0x40493DD: ciphertext_to_compressed (gnutls_cipher.c:587)
...
Segmentation fault
Details for ASN.1 length decoding vulnerability in Libtasn1 [MU-201202-02]:
Various functions using the ASN.1 length decoding logic in Libtasn1 were
incorrectly assuming that the return value from asn1_get_length_der is always
less than the length of the enclosing ASN.1 structure, which is only true for
valid structures and not for intentionally corrupt or otherwise buggy
structures.
Here is an example of unsafe asn1_get_length_der usage from
lib/minitasn1/decoding.c, in the asn1_der_decoding function:
0812 asn1_retCode
0813 asn1_der_decoding (ASN1_TYPE * element, const void *ider, int len,
0814 char *errorDescription)
0815 {
...
1033 case TYPE_ENUMERATED:
1034 len2 =
1035 asn1_get_length_der (der + counter, len - counter, &len3);
1036 if (len2 < 0)
1037 return ASN1_DER_ERROR;
1038 if (len2 + len3 > len - counter)
1039 return ASN1_DER_ERROR;
1040 _asn1_set_value (p, der + counter, len3 + len2);
1041 counter += len3 + len2;
1042 move = RIGHT;
1043 break;
The above call to asn1_get_length_der was returning an impossibly large value
of 2GB when the Mu analyzer generated corrupt lengths fields for versions,
serial numbers, public key info, and signature structures in X.509 client
certificates, but this could happen in any use of Libtasn1 that is relying
upon asn1_get_length_der, not just SSL, TLS, or GnuTLS.
The asn1_der_decoding function failed to check for cases when
asn1_get_length_der returned a length larger than the enclosing structure's
(void* ider) own length (int len).
When _asn1_set_value was called anyway, it contained a memcpy operation which
assumed the arguments are valid, which tried copy 2GB of memory, leading to a
heap corruption vulnerability.
Simon Josefsson, Libtasn1 maintainer, described the patch as follows: "the
real bug was not in asn1_get_length_der() even if that is the function we
patch[ed]. The callers of that function that did not check that the return
values are sane were buggy. However, instead of fixing all callers, ... we
went for the simpler solution to let the function return an error for a
situation that is unlikely to occur without malicious interaction or data
corruption."
The asn1_der_decoding function shown above is now safe, because
asn1_get_length_der was updated to "[return] -4 when the decoded length value
plus @len would exceed @der_len," so asn1_der_decoding returns ASN1_DER_ERROR
before it can call _asn1_set_value to trigger the segmentation fault.
Abbreviated GDB Backtrace after the segmentation fault:
(gdb) bt
#0 __memcpy_ia32 () at ../sysdeps/i386/i686/multiarch/../memcpy.S:75
#1 0x00000001 in ?? ()
#2 0x0020eadc in _asn1_set_value (node=0x807ff50, value=0x807ed5c,
len=2147483652) at parser_aux.c:228
#3 0x0020a646 in asn1_der_decoding (element=0x8078000, ider=0x807ed4e,
len=687, errorDescription=0x0) at decoding.c:1036
#4 0x001bc7da in gnutls_x509_crt_import (cert=0x8078000, data=0xbfffeae8,
format=GNUTLS_X509_FMT_DER) at x509.c:226
#5 0x00176d16 in gnutls_pcert_import_x509_raw (pcert=0x807d610,
cert=0xbfffeae8, format=GNUTLS_X509_FMT_DER, flags=0) at gnutls_pcert.c:201
...
(gdb)
Response / Solution:
TLS record handling vulnerability in GnuTLS [MU-201202-01] is fixed in GnuTLS
3.0.15. For more details, see
http://article.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/5912 .
ASN.1 length decoding vulnerability in Libtasn1 [MU-201202-02] is fixed in
Libtasn1 2.12 and GnuTLS 3.0.16. For more details, see
http://lists.gnu.org/archive/html/help-libtasn1/2012-03/msg00000.html and
http://article.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/5932 .
History:
Mon, 27 Feb 2012 14:13:45 -0800: TLS Record handling issue reported.
Tue, 28 Feb 2012 10:29:46 +0100: TLS Record handling patch created.
Fri, 02 Mar 2012 18:42:05 +0000: GnuTLS 3.0.15 release announced.
Fri, 02 Mar 2012 14:04:31 -0800: ASN.1 length decoding issue reported.
Wed, 14 Mar 2012 01:04:36 +0100: ASN.1 length decoding patch created.
Mon, 19 Mar 2012 10:57:42 +0100: Libtasn1 2.12 release announced.
Tue, 20 Mar 2012 23:40:00 +0000: Advisory released to the public.
See also:
http://article.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/5912
http://article.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/5932
http://lists.gnu.org/archive/html/help-libtasn1/2012-03/msg00000.html
http://git.savannah.gnu.org/gitweb/?p=gnutls.git;a=commitdiff;h=b495740f2ff66550ca9395b3fda3ea32c3acb185
http://git.savannah.gnu.org/gitweb/?p=libtasn1.git;a=commitdiff;h=6e534bf4fb3144be51c928ed3efcf9c36055c9c7
Credit:
These vulnerabilities were discovered by Matthew Hall <mhall at mudynamics.com>,
Senior Network Protocol Software Engineer at Mu Dynamics, via code inspection
and protocol fuzzing using a Mu 4000 security analyzer.
http://blog.mudynamics.com/wp-content/uploads/2012/03/pgpkey.txt
Mu Dynamics is the leading provider of solutions ensuring the performance and
security of both applications and network infrastructure. The company's
innovative solutions enable customers to confidently meet the challenges posed
by today's rapidly changing networks. This includes the ever-growing number of
applications and devices on the network, and the swift transition to mobile,
virtual and cloud environments. Hundreds of service providers, enterprises,
application developers and network equipment manufacturers count on its
purpose-built solutions, like Mu Studio and Blitz, to ensure their
applications and networks are scalable and secure. Mu Dynamics is
headquartered in Sunnyvale, California.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.18 (Darwin)
Comment: GPGTools - http://gpgtools.org
iQIcBAEBAgAGBQJPaRSqAAoJEEzdDa9po1UUuhkP/RnvCMvQwF+9UyZArKkEDVgu
Z6NvZPcquXZhQ4MNLuIbfkikQnGg+9e7EnbKUzbxxNp7tsVN/ioVGWFHRZw3diSp
Tknw8SZlMghz4Li/nw4ruPQcbT4r6EcDtlkJhTAtI+A2ZAlMkurMUAGIcPhX2CvU
cpNHwGdTPqYcT4+07zzhbhf2M2MD5y1268PNEx9EN9VrZEriyDpFRBKlkkOTeti5
LuSkhIUbiH0wChWGheM54rvsoi4LRSO8QEeeUED1kqBzNI5OJD7AB3g1RJ/K8vEB
DjSkGmKg2siDHifNQUgwOcnED0qcUnN9LAp/1qV0Wn+DJvG3RC5hleJSHYPAW6sk
/fmoOegRN+9TlXVI2ZBzF3ltCaDr8ktbwMUiOU/BZc0MAG2geUvGUyYGkZW0JLZN
MZaJRMzqAS2DrKMapwDWUeNXf5rvQHU49eRweBjyE8lUA2cMtMsvgbFtTOtxAFhl
JYytEI1e7Sh8Xo12GWUxsc6aMjIngFzs2VgfwQc283fnBjZWHZNsdX8gKcKoUY3f
oN6IcQijxfOgCEiWkERsPtcX9GN2+9y2QLs79Z5uDeBJXCOLSzeZg93gWqjEGfcm
64iJwKAWpGcHEr5Po3mxwA+9yREmrth+e6cax9LujiXAn1LrW2SkUXofCafYg8yd
ym77p8ibkKLczxASVN2I
=aqOg
-----END PGP SIGNATURE-----
More information about the Gnutls-devel
mailing list