CVE-2009-0590: OpenSSL Invalid Memory Access
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

Leave a Reply