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