CVE-2017-2781
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.
InsideSecure MatrixSSL 3.8.7b
8.1 - CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE-122: Heap-based Buffer Overflow
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 IssuerPolicy 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 function 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->issuerDomainPolicy = psMalloc(pool, len*sizeof(uint32_t)); [3]
memset(pol_map->issuerDomainPolicy, 0, len*sizeof(uint32_t));
for (i = 0; i < oidlen; i++) { [4]
pol_map->issuerDomainPolicy[i] = oid[i];
}
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:
060A60764801650302010301
This decodes to length 10. An array issuerDomainPolicy
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.
==102930==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000ef1c at pc 0x00000050de44 bp 0x7ffc5a018410 sp 0x7ffc5a018408
WRITE of size 4 at 0x60200000ef1c thread T0
#0 0x50de43 in parsePolicyMappings /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:2275
#1 0x50de43 in getExplicitExtensions /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:3073
#2 0x50de43 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 0x7f36b42b782f in __libc_start_main /build/glibc-Qz8a69/glibc-2.23/csu/../csu/libc-start.c:291
#8 0x7f36b42b782f in ?? ??:0
#9 0x418a98 in _start ??:?
#10 0x418a98 in ?? ??:0
0x60200000ef1c is located 0 bytes to the right of 12-byte region [0x60200000ef10,0x60200000ef1c)
allocated by thread T0 here:
#0 0x4b8a38 in __interceptor_malloc ??:?
#1 0x4b8a38 in ?? ??:0
#2 0x509c6f in parsePolicyMappings /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:2271
#3 0x509c6f in getExplicitExtensions /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:3073
#4 0x509c6f 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 0x7f36b42b782f in __libc_start_main /build/glibc-Qz8a69/glibc-2.23/csu/../csu/libc-start.c:291
#10 0x7f36b42b782f in ?? ??:0
SUMMARY: AddressSanitizer: heap-buffer-overflow (/ramdisk/triage/matrixssl/matrixssl/test/certValidate+0x50de43)
Shadow bytes around the buggy address:
0x0c047fff9d90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff9de0: fa fa 00[04]fa fa 00 00 fa fa 00 00 fa fa 00 07
0x0c047fff9df0: fa fa 00 06 fa fa 00 06 fa fa 00 05 fa fa 03 fa
0x0c047fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 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
==102930==ABORTING
Proposed patch:
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];
2017-02-07 - Vendor Disclosure
2017-06-22 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.