CVE-2015-8790: Exposure of Sensitive Information to an Unauthorized Actor (CWE-200)
value: The EbmlUnicodeString::UpdateFromUTF8 function in libEBML before 1.3.3 allows context-dependent attackers to obtain sensitive information from process heap memory via a crafted UTF-8 string, which triggers an invalid memory access.
CWE description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
CVSS v3 base score: 4.3
Published: 2016-01-29T19:59Z
Repository: https://github.com/Matroska-Org/libebml
Language: Other
Files changed: ChangeLog, src/EbmlUnicodeString.cpp
Commit date: 2015-10-20 14:54:55 +0200
Commit message: EbmlUnicodeString: don't read beyond end of string

The conversion from an UTF-8 encoded string into a wchar_t one was
reading from beyond the end of the source buffer if the length indicated
by a UTF-8 character's first byte exceeds the number of bytes actually
present afterwards.

Fixes the issue reported as Cisco TALOS-CAN-0036.

Diff: diff --git a/ChangeLog b/ChangeLog
index a1c95dc..a5139c4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
+2015-10-20  Moritz Bunkus  <moritz@bunkus.org>
+
+        * EbmlUnicodeString::UpdateFromUTF8(): Fixed an invalid memory
+        access. When reading from a UTF-8 string in which the length
+        indicated by a UTF-8 character's first byte exceeds the string's
+        actual number of bytes the parser would access beyond the end of
+        the string resulting in a heap information leak. Fixes the issue
+        reported as Cisco TALOS-CAN-0036.
+
 2015-10-17  Moritz Bunkus  <moritz@bunkus.org>
 
         * Released v1.3.2.
 
 2015-08-21  Steve Lhomme <robUx4@gmail.com>
diff --git a/src/EbmlUnicodeString.cpp b/src/EbmlUnicodeString.cpp
index 3255add..7b7cd8e 100644
--- a/src/EbmlUnicodeString.cpp
+++ b/src/EbmlUnicodeString.cpp
@@ -45,10 +45,25 @@
 
 START_LIBEBML_NAMESPACE
 
 // ===================== UTFstring class ===================
 
+static unsigned int UTFCharLength(uint8 lead)
+{
+  if (lead < 0x80)
+    return 1;
+  else if ((lead >> 5) == 0x6)
+    return 2;
+  else if ((lead >> 4) == 0xe)
+    return 3;
+  else if ((lead >> 3) == 0x1e)
+    return 4;
+  else
+    // Invalid size?
+    return 0;
+}
+
 UTFstring::UTFstring()
   :_Length(0)
   ,_Data(NULL)
 {}
 
@@ -141,43 +156,43 @@ void UTFstring::SetUTF8(const std::string & _aStr)
 void UTFstring::UpdateFromUTF8()
 {
   delete [] _Data;
   // find the size of the final UCS-2 string
   size_t i;
-  for (_Length=0, i=0; i<UTF8string.length(); _Length++) {
-    uint8 lead = static_cast<uint8>(UTF8string[i]);
-    if (lead < 0x80)
-      i++;
-    else if ((lead >> 5) == 0x6)
-      i += 2;
-    else if ((lead >> 4) == 0xe)
-      i += 3;
-    else if ((lead >> 3) == 0x1e)
-      i += 4;
+  const size_t SrcLength = UTF8string.length();
+  for (_Length=0, i=0; i<SrcLength; _Length++) {
+    const unsigned int CharLength = UTFCharLength(static_cast<uint8>(UTF8string[i]));
+    if ((CharLength >= 1) && (CharLength <= 4))
+      i += CharLength;
     else
       // Invalid size?
       break;
   }
   _Data = new wchar_t[_Length+1];
   size_t j;
-  for (j=0, i=0; i<UTF8string.length(); j++) {
-    uint8 lead = static_cast<uint8>(UTF8string[i]);
-    if (lead < 0x80) {
+  for (j=0, i=0; i<SrcLength; j++) {
+    const uint8 lead              = static_cast<uint8>(UTF8string[i]);
+    const unsigned int CharLength = UTFCharLength(lead);
+    if ((CharLength < 1) || (CharLength > 4))
+      // Invalid char?
+      break;
+
+    if ((i + CharLength) > SrcLength)
+      // Guard against invalid memory access beyond the end of the
+      // source buffer.
+      break;
+
+    if (CharLength == 1)
       _Data[j] = lead;
-      i++;
-    } else if ((lead >> 5) == 0x6) {
+    else if (CharLength == 2)
       _Data[j] = ((lead & 0x1F) << 6) + (UTF8string[i+1] & 0x3F);
-      i += 2;
-    } else if ((lead >> 4) == 0xe) {
+    else if (CharLength == 3)
       _Data[j] = ((lead & 0x0F) << 12) + ((UTF8string[i+1] & 0x3F) << 6) + (UTF8string[i+2] & 0x3F);
-      i += 3;
-    } else if ((lead >> 3) == 0x1e) {
+    else if (CharLength == 4)
       _Data[j] = ((lead & 0x07) << 18) + ((UTF8string[i+1] & 0x3F) << 12) + ((UTF8string[i+2] & 0x3F) << 6) + (UTF8string[i+3] & 0x3F);
-      i += 4;
-    } else
-      // Invalid char?
-      break;
+
+    i += CharLength;
   }
   _Data[j] = 0;
 }
 
 void UTFstring::UpdateFromUCS2()

Vulnerable code:   for (_Length=0, i=0; i<UTF8string.length(); _Length++) {
    uint8 lead = static_cast<uint8>(UTF8string[i]);
    if (lead < 0x80)
      i++;
    else if ((lead >> 5) == 0x6)
      i += 2;
    else if ((lead >> 4) == 0xe)
      i += 3;
    else if ((lead >> 3) == 0x1e)
      i += 4;
  for (j=0, i=0; i<UTF8string.length(); j++) {
    uint8 lead = static_cast<uint8>(UTF8string[i]);
    if (lead < 0x80) {
      i++;
    } else if ((lead >> 5) == 0x6) {
      i += 2;
    } else if ((lead >> 4) == 0xe) {
      i += 3;
    } else if ((lead >> 3) == 0x1e) {
      i += 4;
    } else
      // Invalid char?
      break;
Fixed code: 2015-10-20  Moritz Bunkus  <moritz@bunkus.org>

        * EbmlUnicodeString::UpdateFromUTF8(): Fixed an invalid memory
        access. When reading from a UTF-8 string in which the length
        indicated by a UTF-8 character's first byte exceeds the string's
        actual number of bytes the parser would access beyond the end of
        the string resulting in a heap information leak. Fixes the issue
        reported as Cisco TALOS-CAN-0036.

static unsigned int UTFCharLength(uint8 lead)
{
  if (lead < 0x80)
    return 1;
  else if ((lead >> 5) == 0x6)
    return 2;
  else if ((lead >> 4) == 0xe)
    return 3;
  else if ((lead >> 3) == 0x1e)
    return 4;
  else
    // Invalid size?
    return 0;
}

  const size_t SrcLength = UTF8string.length();
  for (_Length=0, i=0; i<SrcLength; _Length++) {
    const unsigned int CharLength = UTFCharLength(static_cast<uint8>(UTF8string[i]));
    if ((CharLength >= 1) && (CharLength <= 4))
      i += CharLength;
  for (j=0, i=0; i<SrcLength; j++) {
    const uint8 lead              = static_cast<uint8>(UTF8string[i]);
    const unsigned int CharLength = UTFCharLength(lead);
    if ((CharLength < 1) || (CharLength > 4))
      // Invalid char?
      break;

    if ((i + CharLength) > SrcLength)
      // Guard against invalid memory access beyond the end of the
      // source buffer.
      break;

    if (CharLength == 1)
    else if (CharLength == 2)
    else if (CharLength == 3)
    else if (CharLength == 4)

    i += CharLength;