xorl %eax, %eax

CVE-2009-0590: OpenSSL Invalid Memory Access

leave a comment »

This is not a new bug but I just saw it on the yesterday’s FreeBSD advisories. It affects OpenSSL prior to 0.9.8k release. The code presented here is taken from 0.9.8j release. Here is the vulnerable function:

2 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3  * project 2000.
4  */
       ...
916 /* Translate ASN1 content octets into a structure */
917
918 int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
919                         int utype, char *free_cont, const ASN1_ITEM *it)
920         {
       ...
922         ASN1_STRING *stmp;
       ...
949         switch(utype)
950                 {
951                 case V_ASN1_OBJECT:
       ...
956                 case V_ASN1_NULL:
       ...
966                 case V_ASN1_BOOLEAN:
       ...
981                 case V_ASN1_BIT_STRING:
       ...
986                 case V_ASN1_INTEGER:
987                 case V_ASN1_NEG_INTEGER:
988                 case V_ASN1_ENUMERATED:
989                 case V_ASN1_NEG_ENUMERATED:
       ...
997                 case V_ASN1_OCTET_STRING:
998                 case V_ASN1_NUMERICSTRING:
999                 case V_ASN1_PRINTABLESTRING:
1000                case V_ASN1_T61STRING:
1001                case V_ASN1_VIDEOTEXSTRING:
1002                case V_ASN1_IA5STRING:
1003                case V_ASN1_UTCTIME:
1004                case V_ASN1_GENERALIZEDTIME:
1005                case V_ASN1_GRAPHICSTRING:
1006                case V_ASN1_VISIBLESTRING:
1007                case V_ASN1_GENERALSTRING:
1008                case V_ASN1_UNIVERSALSTRING:
1009                case V_ASN1_BMPSTRING:
1010                case V_ASN1_UTF8STRING:
1011                case V_ASN1_OTHER:
1012                case V_ASN1_SET:
1013                case V_ASN1_SEQUENCE:

This function is part of crypto/asn1/tasn_dec.c source code file and it is obvious that it’s used to translate ASN.1 contents. As you can see, there is no separate check for a BMPString or a UniversalString, Both of them (line 1008 and 1009 respectively) are handled by the default condition which is the following:

1015                 /* All based on ASN1_STRING and handled the same */
1016                 if (!*pval)
1017                         {
1018                         stmp = ASN1_STRING_type_new(utype);
1019                         if (!stmp)
1020                                 {
1021                                 ASN1err(ASN1_F_ASN1_EX_C2I,
1022                                                         ERR_R_MALLOC_FAILURE);
1023                                 goto err;
1024                                 }
1025                         *pval = (ASN1_VALUE *)stmp;
1026                         }

It checks if ASN.1 value pval is NULL at line 1016. If this is the case, ASN1_STRING_type_new(3) is called at line 1018. This will attempt to return a pointer to the structure of type utype (which is user controlled), and then initialize pval to that pointer.
If pval isn’t NULL (which is also user controlled parameter) it does the following:

1027                 else
1028                         {
1029                         stmp = (ASN1_STRING *)*pval;
1030                         stmp->type = utype;
1031                         }

It immediately sets stmp to whatever value pval has and its type is set to the user controlled utype. Then it attempts to use a buffer allocated earlier like this:

1032                 /* If we've already allocated a buffer use it */
1033                 if (*free_cont)
1034                         {
1035                         if (stmp->data)
1036                                 OPENSSL_free(stmp->data);
1037                         stmp->data = (unsigned char *)cont; /* UGLY CAST! RL */
1038                         stmp->length = len;
1039                         *free_cont = 0;
1040                         }

Removes the old data (if there are any) at line 1036 and proceeds with updating the structure with the new values at lines 1037-1039. Variable len at line 1038 represents the encoded string’s length. Now, if we move to the else part we’ll see this:

1041                 else
1042                         {
1043                         if (!ASN1_STRING_set(stmp, cont, len))
1044                                 {
1045                                 ASN1err(ASN1_F_ASN1_EX_C2I,
1046                                                         ERR_R_MALLOC_FAILURE);
1047                                 ASN1_STRING_free(stmp);
1048                                 *pval = NULL;
1049                                 goto err;
1050                                 }
1051                         }
1052                 break;
1053                 }

It calls ASN1_STRING_set(3) which according to its man page does this:

ASN1_STRING_set() sets the data of string str to the buffer 
data or length len. The supplied data is copied. If len 
is -1 then the length is determined by strlen(data).

If a malicious length is defined in our string then this will have an almost uninitialized stmp buffer which results in an invalid memory access if ASN1_STRING_print_ex() from crypto/asn1/a_strex.c attempts to read/print this value. Of course, this affects only applications that use this printing routine. To patch this they added the following conditions in the default case where BMPString and UniversalString types are handled:

         default:
+        if (utype == V_ASN1_BMPSTRING && (len & 1))
+            {
+            ASN1err(ASN1_F_ASN1_EX_C2I,
+                    ASN1_R_BMPSTRING_IS_WRONG_LENGTH);
+            goto err;
+            }
+        if (utype == V_ASN1_UNIVERSALSTRING && (len & 3))
+            {
+            ASN1err(ASN1_F_ASN1_EX_C2I,
+                    ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH);
+            goto err;
+            }
         /* All based on ASN1_STRING and handled the same */

This means that the missing checks where at least when:
– Type = BMPString and Encoded Length = <something that doesn’t match when ANDs with 1>
– Type = UniversalString and Encoded Length = <something that doesn’t match when ANDs with 3>
Using one of these types and an invalid encoding length you can access invalid memory and possibly crash the application attempting to do this. The constants for invalid lengths on the above patch where also added at crypto/asn1/asn1.h:

 #define ASN1_R_BAD_TAG                           104
+#define ASN1_R_BMPSTRING_IS_WRONG_LENGTH         210
 #define ASN1_R_BN_LIB                            105

And…

 #define ASN1_R_UNEXPECTED_EOC                          159
+#define ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH         211
 #define ASN1_R_UNKNOWN_FORMAT                          160

Now, the trigger is quiet simple… :-P

Written by xorl

April 23, 2009 at 16:14

Posted in bugs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s