Talos Vulnerability Report

TALOS-2017-0278

InsideSecure MatrixSSL x509 certificate General Names Information Disclosure Vulnerability

June 22, 2017
CVE Number

CVE-2017-2782

Summary

An integer overflow vulnerability exists in the X509 certificate parsing functionality of InsideSecure MatrixSSL 3.8.7b. A specially crafted x509 certificate can cause a length counter to overflow, leading to a controlled out of bounds copy operation. To trigger this vulnerability, a specially crafted x509 certificate must be presented to the vulnerable client or server application when initiating secure connection. Depending on the use of the underlying library, this issue could be abused to leak sensitive memory. It could also be used to cause a crash, resulting in a denial of service.

Tested Versions

InsideSecure MatrixSSL 3.8.7b

Product URLs

http://www.matrixssl.org/

CVSSv3 Score

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

CWE

CWE-191: Integer Underflow (Wrap or Wraparound)

Details

MatrixSSL is a secure socket layer cryptographic library aimed at embedded and IoT systems due to its 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 exists an issue in a way general names extensions are parsed. Specifically, a certificate with subject alternative name ASN1 strings of certain caracteristics can lead to an integer underflow, ultimately leading to an out of bounds memcpy call with full control over size parameter.

The vulnerability is present in function parseGeneralNames. The key points are highlighted in the following code:

  static int32_t parseGeneralNames(psPool_t *pool, const unsigned char **buf,
                              uint16_t len, const unsigned char *extEnd                       [1]
                              x509GeneralName_t **name, int16_t limit)
{


  


      while (len > 0) {                                                                        [2]
      ...


              activeName->id = *p & 0xF;
              p++; len--;                                                                      [3]
      ...
                      save = p;
              if (getAsnLength(&p, (uint32)(extEnd - p), &activeName->dataLen) < 0 ||
                              activeName->dataLen < 1 ||
                              (uint32)(extEnd - p) < activeName->dataLen) {
                      psTraceCrypto("ASN len error in parseGeneralNames\n");
                      return PS_PARSE_FAIL;
              }
              len -= (p - save);                                                               [4]


      ...


              activeName->data = psMalloc(pool, activeName->dataLen + 1);                      [5]
              if (activeName->data == NULL) {
                      psError("Memory allocation error: activeName->data\n");
                      return PS_MEM_FAIL;
              }
              /* This guarantees data is null terminated, even for non IA5Strings */
              memset(activeName->data, 0x0, activeName->dataLen + 1);
              memcpy(activeName->data, p, activeName->dataLen);                                 [6]

At [1], len is an unsigned 16 integer argument. At [2], len is checked to be greater than 0 in each loop iteration. At [3], len is decremented unconditionally. At [4] there’s another chance for len to be further decremented. At [5], a data buffer is allocated appropriately and is used as a destination for in a memcpy call at [6]. From the above quoted source code, it can be observed that in case initial value of len is small (1 for example), an underflow can happen, making it very big, which would allow the loop to execute more times than needed, causing further trouble. While parsing the ASN1 buffer that is specified, pointer p is constantly incremented, and is kept in bounds by being checked with extEnd pointer. But, due to len underflow, and carefully chosen ASN1 structure lengths, p can be incremented beyound extEnd causing another integer overflow. This allows us to further manipulate the parser, ultimately enabling us to control the activeName->dataLen value. If this value is set to be very big, the memcpy call at [6] will create an out of bounds read from the p pointer, copying adjacent heap memory beyond the end of the initial buffer. If this memory buffer and it’s length is later used and returned to the user in some way, it would represent sensitive information leak.

The attached minimal x509 certificate that triggers this vulnerability has a few things changed from the benign one. First, the length of the global names field is changed to 1, instead of real length, which causes len to be 1 initially. This achieves the first integer overflow. Second, size of the first subject alternative name is set to a large value. In this case, the buffer passed to parseGlobalNames is at most 100 bytes, so maximum size for first malformed alternative name entry is 0x63. This will cause the p pointer to be incremented beyond it’s end after the first iteration of the while loop. And finally, the rest of the certificate is adjusted so that malformed size points to an element with ID other than zero which can now have arbitrary size because the extEnd-p check is overflown.

The issue can be triggered with the certValidate sample application supplied with the library.

Crash Information

Address sanitizer output:

=================================================================
==93257==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffefe71b800 at pc 0x0000004a276d bp   
0x7ffefe715e30 sp 0x7ffefe7155e0
READ of size 62985 at 0x7ffefe71b800 thread T0
  #0 0x4a276c in __asan_memcpy ??:?
  #1 0x4a276c in ?? ??:0
  #2 0x5104b0 in parseGeneralNames /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:1727
  #3 0x5104b0 in ?? ??:0
  #4 0x507bf9 in getExplicitExtensions /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:2657
  #5 0x507bf9 in ?? ??:0
  #6 0x4ff704 in psX509ParseCert /ramdisk/triage/matrixssl/crypto/keyformat/x509.c:871
  #7 0x4ff704 in ?? ??:0
  #8 0x4e73ad in main /ramdisk/triage/matrixssl/matrixssl/test/certValidate.c:171
  #9 0x4e73ad in ?? ??:0
  #10 0x7f1379fdd82f in __libc_start_main /build/glibc-Qz8a69/glibc-2.23/csu/../csu/libc-start.c:291
  #11 0x7f1379fdd82f in ?? ??:0
  #12 0x418a98 in _start ??:?
  #13 0x418a98 in ?? ??:0

Address 0x7ffefe71b800 is located in stack of thread T0 at offset 20064 in frame #0 0x4e715f in main /ramdisk/triage/matrixssl/matrixssl/test/certValidate.c:121 #1 0x4e715f in ?? ??:0

This frame has 2 object(s):
  [32, 40) 'chain'
  [64, 20064) 'pp' <== Memory access at offset 20064 overflows this variable   HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
    (longjmp and C++ exceptions *are* supported)   SUMMARY: AddressSanitizer: stack-buffer-overflow (/ramdisk/triage/matrixssl/matrixssl/test/certValidate+0x4a276c)   Shadow bytes around the buggy address:
0x10005fcdb6b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005fcdb6c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005fcdb6d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005fcdb6e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005fcdb6f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10005fcdb700:[f3]f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3
0x10005fcdb710: f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3
0x10005fcdb720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005fcdb730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005fcdb740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005fcdb750: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    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
==93257==ABORTING

Timeline

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

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.