Talos Vulnerability Report

TALOS-2017-0276

InsideSecure MatrixSSL x509 certificate SubjectDomainPolicy Remote Code Execution Vulnerability

June 22, 2017
CVE Number

CVE-2017-2780

Summary

An exploitable heap buffer overflow vulnerability exists in the X509 certificate parsing functionality of InsideSecure MatrixSSL 3.8.7b. A specially crafted x509 certificate can cause a buffer overflow on the heap resulting in remote code execution. To trigger this vulnerability, a specially crafted x509 certificate must be presented to the vulnerable client or server application when initiating secure connection.

Tested Versions

InsideSecure MatrixSSL 3.8.7b

Product URLs

http://www.matrixssl.org/

CVSSv3 Score

8.1 - CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

CWE

CWE-122: Heap-based Buffer Overflow

Details

MatrixSSL is a secure socket layer cryptographic library aimed at embedded and IoT systems due to it’s low code footprint and RAM utilization. It is supported on many embedded platforms, fully compatible with other SSL implementations and FIPS140-2 compliant.

While parsing an x509 certificate in DER form there exist an issue in with ASN1 encoded sequences. Specifically, while parsing x509 SubjectDomainPolicy PolicyMappings extension, an array of one size is allocated on the heap, but due to specially encoded OIDs, one more element can be copied into it. Specifically in the following code in crypto/keyformat/x509.c in fucntion parsePolicyMappings:

    if (getAsnLength(&p, (uint32)(polMappingsEnd - p), &len) < 0 ||                 [1]
        (uint32)(polMappingsEnd - p) < len) {
        psTraceCrypto("getAsnLength failure in policyMappings parsing\n");
        return PS_PARSE_FAIL;
    }
    memset(oid, 0, sizeof(oid));
    if ((oidlen = psParseOid(p, len, oid)) < 1) {                                   [2]
        psTraceCrypto("Malformed extension OID\n");
        return PS_PARSE_FAIL;
    }
    p += len;

    pol_map->subjectDomainPolicy = psMalloc(pool, len*sizeof(uint32_t));            [3]
    memset(pol_map->subjectDomainPolicy, 0, len*sizeof(uint32_t));

    for (i = 0; i < oidlen; i++) {
        pol_map->subjectDomainPolicy[i] = oid[i];                                   [4]
    }

At [1] getAsnLength is called to determine the length of the following ASN1 OID field, the value is stored in len variable. At [2], len is used as a parameter to psParseOid function. At [3], an array for OIDs is allocated using len for number of elements. In a for loop at [4], OIDs are coppied into the allocated array. If oidlen is bigger than len, an overflow can happen.

If we take a look at psParseOid we can observe the following key points:

oid[0] = *der / 40;
oid[1] = *der % 40;                                                                                                                             [1]
der++;
/* Zero the remainder of OID and leave n == 2 */
for (n = MAX_OID_LEN - 1; n > 2; n--) {
                oid[n] = 0;
}
while (der < end && n < MAX_OID_LEN) {
                /* If the high bit is 0, it's short form variable length quantity */
                if (!(*der & 0x80)) {                                                                                                           [2]
                                oid[n++] = *der++;
                } else {
                                sanity = 0;
                                /* Long form. High bit means another (lower) 7 bits following */
                                do {
                                                oid[n] |= (*der & 0x7F);                                                                    [3]
                                                /* A clear high bit ends the byte sequence */
                                                if (!(*der & 0x80)) {
                                                                break;
                                                }
                                                /* Allow a maximum of 4 x 7 bit shifts (28 bits) */
                                                if (++sanity > 4) {
                                                                return 0;
                                                }
                                                /* Make room for the next 7 bits */
                                                oid[n] <<= 7;
                                                der++;
                                } while (der < end);
                                der++;
                                n++;
                }
}

In the above code, an important thing to note is that at [1], a single byte from the buffer initializes two oid entries. Then, at [2], is the soft form is used, both n counter and der buffer pointer are incremented by one. And finaly, in a do-while loop around [3], a long form OIDs are parsed, which can consume more than one byte per OID entry.

Therefore, if a specially crafted x509 certificate can have a PolicyMapping with an ASN1 buffer of length N bytes (as returned by getAsnLength), but actually contain N+1 OIDs (two from first byte, and all other bytes being short form). As an example, the attached PoC x509 certificate has the following OID sequence:

060a60764801650302010502

This decodes to length 10. An array subjectDomainPolicy will be allocated for 10 entries only, but 11 will be copied into it (since none are in long form), causing a buffer overflow.

A simple fix for this issue would be to use oidLen instead of len when allocating mentioned arrays.

Crash Information

==70539==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60400000dd38 at pc 0x00000050df22 bp 0x7fff86e24df0 sp 0x7fff86e24de8
WRITE of size 4 at 0x60400000dd38 thread T0
    #0 0x50df21 in parsePolicyMappings /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:2299
    #1 0x50df21 in getExplicitExtensions /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:3071
    #2 0x50df21 in ?? ??:0
    #3 0x4ff704 in psX509ParseCert /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:871
    #4 0x4ff704 in ?? ??:0
    #5 0x4e73ad in main /ramdisk/triage/matrixssl/matrixssl/test/certValidate.c:171
    #6 0x4e73ad in ?? ??:0
    #7 0x7f0345b6f82f in __libc_start_main /build/glibc-Qz8a69/glibc-2.23/csu/../csu/libc-start.c:291
    #8 0x7f0345b6f82f in ?? ??:0
    #9 0x418a98 in _start ??:?
    #10 0x418a98 in ?? ??:0

0x60400000dd38 is located 0 bytes to the right of 40-byte region [0x60400000dd10,0x60400000dd38)
allocated by thread T0 here:
    #0 0x4b8a38 in __interceptor_malloc ??:?
    #1 0x4b8a38 in ?? ??:0
    #2 0x50a23f in parsePolicyMappings /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:2295
    #3 0x50a23f in getExplicitExtensions /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:3072
    #4 0x50a23f in ?? ??:0
    #5 0x4ff704 in psX509ParseCert /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:871
    #6 0x4ff704 in ?? ??:0
    #7 0x4e73ad in main /ramdisk/triage/matrixssl/matrixssl/test/certValidate.c:171
    #8 0x4e73ad in ?? ??:0
    #9 0x7f0345b6f82f in __libc_start_main /build/glibc-Qz8a69/glibc-2.23/csu/../csu/libc-start.c:291
    #10 0x7f0345b6f82f in ?? ??:0

SUMMARY: AddressSanitizer: heap-buffer-overflow (/ramdisk/triage/matrixssl/matrixssl/test/certValidate+0x50df21)
Shadow bytes around the buggy address:
  0x0c087fff9b50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff9b60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff9b70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff9b80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff9b90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c087fff9ba0: fa fa 00 00 00 00 00[fa]fa fa 00 00 00 00 00 fa
  0x0c087fff9bb0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
  0x0c087fff9bc0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
  0x0c087fff9bd0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
  0x0c087fff9be0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
  0x0c087fff9bf0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==70539==ABORTING

Mitigation

diff --git a/crypto/keyformat/x509.c b/crypto/keyformat/x509.c
index 33559b5..8822a2d 100644
--- a/crypto/keyformat/x509.c
+++ b/crypto/keyformat/x509.c
@@ -2266,8 +2266,8 @@ int32_t parsePolicyMappings(psPool_t *pool,
                                }
                                p += len;


-               pol_map->issuerDomainPolicy = psMalloc(pool, len*sizeof(uint32_t));
-               memset(pol_map->issuerDomainPolicy, 0, len*sizeof(uint32_t));   
+               pol_map->issuerDomainPolicy = psMalloc(pool, oidlen*sizeof(uint32_t));
+               memset(pol_map->issuerDomainPolicy, 0, oidlen*sizeof(uint32_t));


                                for (i = 0; i < oidlen; i++) {
                                                pol_map->issuerDomainPolicy[i] = oid[i];
@@ -2292,8 +2292,8 @@ int32_t parsePolicyMappings(psPool_t *pool,
                                }
                                p += len;


-               pol_map->subjectDomainPolicy = psMalloc(pool, len*sizeof(uint32_t));
-               memset(pol_map->subjectDomainPolicy, 0, len*sizeof(uint32_t));
+               pol_map->subjectDomainPolicy = psMalloc(pool, oidlen*sizeof(uint32_t));
+               memset(pol_map->subjectDomainPolicy, 0, oidlen*sizeof(uint32_t));


                                for (i = 0; i < oidlen; i++) {
                                                pol_map->subjectDomainPolicy[i] = oid[i];

Timeline

2017-02-07 - Vendor Disclosure
2017-06-22 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.