Hi,

 The saga continues after the review of Matthias Hopf which suggested
 additional fixes.  See his message in attachment and the patches I made
 for Debian.

   Bye,
-- 
Loïc Minier <[EMAIL PROTECTED]>
--- Begin Message ---
Hi all,

unfortunately this only made it through after xine-lib 1.1.2 release:

There has been a vulnerability report about libmms on [vendor-sec].
(CVE-2006-2200)

Please note that the original patch from the Debian maintainer is
partially incorrect (it should read memset(dest,0,2*len)), but the memset
isn't really necessary and could be nuked anyway. The use of memset in
the patch certainly doesn't do any harm, though, and it fixes the
potential overflow.

Luckily, xine uses libmms in a way that these vulnerabilities cannot be
exploited (buffers are large enough), and the xine module even seems to
rely on the side effects of the memset of the 'broken' library.  Note
that the library sources are included (not an externally linked
library).

While analyzing the source I found a couple of potential heap overflows,
though, which I'm pretty sure that they can be exploited with some
effort. They are fixed in CVS. I also attached the according patch. But
I'm pretty sure that I overlooked some additional ones.

This source is a wormhole.
Sorry, Thibaut, but then you maybe coded the glue layer only :-]

Matthias

-- 
Matthias Hopf <[EMAIL PROTECTED]>       __        __   __
Maxfeldstr. 5 / 90409 Nuernberg    (_   | |  (_   |__         [EMAIL PROTECTED]
Phone +49-911-74053-715            __)  |_|  __)  |__  labs   www.mshopf.de
diff -ur ../xine-lib-1.1.1.orig/src/input/mms.c ./src/input/mms.c
--- ../xine-lib-1.1.1.orig/src/input/mms.c	2005-04-21 21:02:43.000000000 +0200
+++ ./src/input/mms.c	2006-07-06 20:41:18.000000000 +0200
@@ -138,7 +138,7 @@
   int           num_stream_ids;
   int           stream_ids[ASF_MAX_NUM_STREAMS];
   int           stream_types[ASF_MAX_NUM_STREAMS];
-  int           asf_packet_len;
+  uint32_t      asf_packet_len;
   uint64_t      file_len;
   char          guid[37];
   uint32_t      bitrates[ASF_MAX_NUM_STREAMS];
@@ -371,13 +371,17 @@
       goto error;
     
     header->packet_len = LE_32(this->buf + 8) + 4;
+    if (header->packet_len > BUF_SIZE - 12) {
+      header->packet_len = 0;
+      goto error;
+    }
     lprintf("mms command\n");
     packet_type = MMS_PACKET_COMMAND;
   } else {
     header->packet_seq     = LE_32(this->buf);
     header->packet_id_type = this->buf[4];
     header->flags          = this->buf[5];
-    header->packet_len     = LE_16(this->buf + 6) - 8;
+    header->packet_len     = (LE_16(this->buf + 6) - 8) & 0xffff;
     if (header->packet_id_type == ASF_HEADER_PACKET_ID_TYPE) {
       lprintf("asf header\n");
       packet_type = MMS_PACKET_ASF_HEADER;
@@ -497,6 +501,11 @@
         break;
       case MMS_PACKET_ASF_HEADER:
       case MMS_PACKET_ASF_PACKET:
+	if (header.packet_len + this->asf_header_len > ASF_HEADER_LEN) {
+	    xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
+		     "libmms: asf packet too large\n");
+	    return 0;
+	}
         len = _x_io_tcp_read (this->stream, this->s,
                               this->asf_header + this->asf_header_len, header.packet_len);
         if (len != header.packet_len) {
@@ -542,6 +551,12 @@
       case GUID_ASF_FILE_PROPERTIES:
 
         this->asf_packet_len = LE_32(this->asf_header + i + 92 - 24);
+        if (this->asf_packet_len > BUF_SIZE) {
+          this->asf_packet_len = 0;
+	  xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
+		   "libmms: asf packet len too large\n");
+	  break;
+        }
         this->file_len       = LE_64(this->asf_header + i + 40 - 24);
         lprintf ("file object, file_length = %lld, packet length = %d",
 		 this->file_len, this->asf_packet_len);
diff -ur ../xine-lib-1.1.1.orig/src/input/mmsh.c ./src/input/mmsh.c
--- ../xine-lib-1.1.1.orig/src/input/mmsh.c	2005-08-25 17:36:29.000000000 +0200
+++ ./src/input/mmsh.c	2006-07-06 20:05:58.000000000 +0200
@@ -182,7 +182,7 @@
   int           num_stream_ids;
   int           stream_ids[ASF_MAX_NUM_STREAMS];
   int           stream_types[ASF_MAX_NUM_STREAMS];
-  int           packet_length;
+  uint32_t      packet_length;
   int64_t       file_length;
   char          guid[37];
   uint32_t      bitrates[ASF_MAX_NUM_STREAMS];
@@ -491,6 +491,10 @@
       case GUID_ASF_FILE_PROPERTIES:
 
         this->packet_length = LE_32(this->asf_header + i + 92 - 24);
+	if (this->packet_length > CHUNK_SIZE) {
+	  this->packet_length = 0;
+	  break;
+	}
         this->file_length   = LE_64(this->asf_header + i + 40 - 24);
         /*lprintf ("file object, file_length = %lld, packet length = %d",
 		 this->file_length, this->packet_count);*/

--- End Message ---
--- Begin Message ---
        Hi,

On Tue, Jul 11, 2006, Matthias Hopf wrote:
> Please note that the original patch from the Debian maintainer is
> partially incorrect (it should read memset(dest,0,2*len)), but the memset
> isn't really necessary and could be nuked anyway. The use of memset in
> the patch certainly doesn't do any harm, though, and it fixes the
> potential overflow.
[...]
> While analyzing the source I found a couple of potential heap overflows,
> though, which I'm pretty sure that they can be exploited with some
> effort. They are fixed in CVS. I also attached the according patch. But
> I'm pretty sure that I overlooked some additional ones.

 Thanks for your review and for the additional fixes, I've adapted the
 patch and added the memset() fixes you mention for the libmms source
 package in Debian, I attach the incremental patch, "libmms_0.2-7.diff".
   I also attach the cumulative patch of all successive patches I have
 applied for CVE-2006-2200 to the libmms tree,
 "libmms_0.2-7-cumulative.diff".

   Bye,
-- 
Loïc Minier <[EMAIL PROTECTED]>
--- libmms-0.2/src/mms.c
+++ libmms-0.2/src/mms.c
@@ -144,7 +144,7 @@
   int           stream_types[ASF_MAX_NUM_STREAMS];
   off_t         start_packet_seq; /* for live streams != 0, need to keep it 
around */
   int           need_discont; /* whether we need to set start_packet_seq */
-  int           asf_packet_len;
+  uint32_t      asf_packet_len;
   uint64_t      file_len;
   char          guid[37];
   uint32_t      bitrates[ASF_MAX_NUM_STREAMS];
@@ -477,7 +477,7 @@
 }
 
 static void string_utf16(iconv_t url_conv, char *dest, char *src, int len) {
-    memset(dest, 0, len);
+    memset(dest, 0, 2 * len);
 
     if (url_conv == (iconv_t)-1) {
       int i;
@@ -503,7 +503,7 @@
 static void string_utf16(int unused, char *dest, char *src, int len) {
   int i;
 
-  memset (dest, 0, len);
+  memset (dest, 0, 2 * len);
 
   for (i = 0; i < len; i++) {
     dest[i * 2] = src[i];
@@ -539,13 +539,17 @@
       goto error;
     
     header->packet_len = LE_32(this->buf + 8) + 4;
+    if (header->packet_len > BUF_SIZE - 12) {
+        header->packet_len = 0;
+        goto error;
+    }
     lprintf("mms command\n");
     packet_type = MMS_PACKET_COMMAND;
   } else {
     header->packet_seq     = LE_32(this->buf);
     header->packet_id_type = this->buf[4];
     header->flags          = this->buf[5];
-    header->packet_len     = LE_16(this->buf + 6) - 8;
+    header->packet_len     = (LE_16(this->buf + 6) - 8) & 0xffff;
     if (header->packet_id_type == ASF_HEADER_PACKET_ID_TYPE) {
       lprintf("asf header\n");
       packet_type = MMS_PACKET_ASF_HEADER;
@@ -674,6 +678,11 @@
         break;
       case MMS_PACKET_ASF_HEADER:
       case MMS_PACKET_ASF_PACKET:
+        if (header.packet_len + this->asf_header_len > ASF_HEADER_LEN) {
+            lprintf( "***LOG:*** -- "
+                     "libmms: asf packet too large\n");
+            return 0;
+        }
         len = io_read(io,  this->s,
                               this->asf_header + this->asf_header_len, 
header.packet_len);
         if (len != header.packet_len) {
@@ -720,6 +729,12 @@
       case GUID_ASF_FILE_PROPERTIES:
 
         this->asf_packet_len = LE_32(this->asf_header + i + 92 - 24);
+        if (this->asf_packet_len > BUF_SIZE) {
+          this->asf_packet_len = 0;
+          lprintf( "***LOG:*** -- "
+                   "libmms: asf packet len too large\n");
+          break;
+        }
         this->file_len       = LE_64(this->asf_header + i + 40 - 24);
         lprintf ("file object, packet length = %d (%d)\n",
                  this->asf_packet_len, LE_32(this->asf_header + i + 96 - 24));
--- libmms-0.2/src/mmsh.c
+++ libmms-0.2/src/mmsh.c
@@ -184,7 +184,7 @@
   int           num_stream_ids;
   int           stream_ids[ASF_MAX_NUM_STREAMS];
   int           stream_types[ASF_MAX_NUM_STREAMS];
-  int           packet_length;
+  uint32_t      packet_length;
   int64_t       file_length;
   char          guid[37];
   uint32_t      bitrates[ASF_MAX_NUM_STREAMS];
@@ -604,6 +604,10 @@
       case GUID_ASF_FILE_PROPERTIES:
 
         this->packet_length = LE_32(this->asf_header + i + 92 - 24);
+        if (this->packet_length > CHUNK_SIZE) {
+          this->packet_length = 0;
+          break;
+        }
         this->file_length   = LE_64(this->asf_header + i + 40 - 24);
         lprintf ("file object, packet length = %d (%d)\n",
                 this->packet_length, LE_32(this->asf_header + i + 96 - 24));
--- libmms-0.2/debian/changelog
+++ libmms-0.2/debian/changelog
@@ -1,3 +1,15 @@
+libmms (0.2-7) unstable; urgency=high
+
+  * SECURITY: CVE-2006-2200: buffer overflows in mms / mmsh parsers:
+    additional fixes thanks to Matthias Hopf:
+    - even more checks on "packet_length" / "packet_len" in src/mms.c and
+      src/mmsh.c
+    - fix memset() calls in the two string_utf16() implementations in
+      src/mms.c to clear all bytes in dest, "len" is the UTF-16 length of the
+      string in wide chars, so the memset should use "2 * len".
+
+ -- Loic Minier <[EMAIL PROTECTED]>  Tue, 11 Jul 2006 13:11:11 +0200
+
 libmms (0.2-6) unstable; urgency=low
 
   * SECURITY: CVE-2006-2200: buffer overflows in mms / mmsh parsers: fix an
--- libmms-0.2/debian/changelog
+++ libmms-0.2/debian/changelog
@@ -1,3 +1,30 @@
+libmms (0.2-7) unstable; urgency=high
+
+  * SECURITY: CVE-2006-2200: buffer overflows in mms / mmsh parsers:
+    additional fixes thanks to Matthias Hopf:
+    - even more checks on "packet_length" / "packet_len" in src/mms.c and
+      src/mmsh.c
+    - fix memset() calls in the two string_utf16() implementations in
+      src/mms.c to clear all bytes in dest, "len" is the UTF-16 length of the
+      string in wide chars, so the memset should use "2 * len".
+
+ -- Loic Minier <[EMAIL PROTECTED]>  Tue, 11 Jul 2006 13:11:11 +0200
+
+libmms (0.2-6) unstable; urgency=low
+
+  * SECURITY: CVE-2006-2200: buffer overflows in mms / mmsh parsers: fix an
+    error that crept in the previous fix and use start < end instead of start
+    > end in src/mms.c and src/mmsh.c, thanks Martin Pitt.
+
+ -- Loic Minier <[EMAIL PROTECTED]>  Wed,  5 Jul 2006 18:13:36 +0200
+
+libmms (0.2-5) unstable; urgency=high
+
+  * SECURITY: CVE-2006-2200: buffer overflows in mms / mmsh parsers: apply
+    memset() range fixes adapted from #374577 by Wesley J. Landaker.
+
+ -- Loic Minier <[EMAIL PROTECTED]>  Thu, 22 Jun 2006 20:53:44 +0200
+
 libmms (0.2-4) unstable; urgency=low
 
   * Apply patch from Wesley J. Landaker for the headers to be usable from C++.
--- libmms-0.2.orig/src/mms.c
+++ libmms-0.2/src/mms.c
@@ -144,7 +144,7 @@
   int           stream_types[ASF_MAX_NUM_STREAMS];
   off_t         start_packet_seq; /* for live streams != 0, need to keep it 
around */
   int           need_discont; /* whether we need to set start_packet_seq */
-  int           asf_packet_len;
+  uint32_t      asf_packet_len;
   uint64_t      file_len;
   char          guid[37];
   uint32_t      bitrates[ASF_MAX_NUM_STREAMS];
@@ -477,7 +477,7 @@
 }
 
 static void string_utf16(iconv_t url_conv, char *dest, char *src, int len) {
-    memset(dest, 0, 1000);
+    memset(dest, 0, 2 * len);
 
     if (url_conv == (iconv_t)-1) {
       int i;
@@ -503,7 +503,7 @@
 static void string_utf16(int unused, char *dest, char *src, int len) {
   int i;
 
-  memset (dest, 0, 1000);
+  memset (dest, 0, 2 * len);
 
   for (i = 0; i < len; i++) {
     dest[i * 2] = src[i];
@@ -539,13 +539,17 @@
       goto error;
     
     header->packet_len = LE_32(this->buf + 8) + 4;
+    if (header->packet_len > BUF_SIZE - 12) {
+        header->packet_len = 0;
+        goto error;
+    }
     lprintf("mms command\n");
     packet_type = MMS_PACKET_COMMAND;
   } else {
     header->packet_seq     = LE_32(this->buf);
     header->packet_id_type = this->buf[4];
     header->flags          = this->buf[5];
-    header->packet_len     = LE_16(this->buf + 6) - 8;
+    header->packet_len     = (LE_16(this->buf + 6) - 8) & 0xffff;
     if (header->packet_id_type == ASF_HEADER_PACKET_ID_TYPE) {
       lprintf("asf header\n");
       packet_type = MMS_PACKET_ASF_HEADER;
@@ -674,6 +678,11 @@
         break;
       case MMS_PACKET_ASF_HEADER:
       case MMS_PACKET_ASF_PACKET:
+        if (header.packet_len + this->asf_header_len > ASF_HEADER_LEN) {
+            lprintf( "***LOG:*** -- "
+                     "libmms: asf packet too large\n");
+            return 0;
+        }
         len = io_read(io,  this->s,
                               this->asf_header + this->asf_header_len, 
header.packet_len);
         if (len != header.packet_len) {
@@ -720,6 +729,12 @@
       case GUID_ASF_FILE_PROPERTIES:
 
         this->asf_packet_len = LE_32(this->asf_header + i + 92 - 24);
+        if (this->asf_packet_len > BUF_SIZE) {
+          this->asf_packet_len = 0;
+          lprintf( "***LOG:*** -- "
+                   "libmms: asf packet len too large\n");
+          break;
+        }
         this->file_len       = LE_64(this->asf_header + i + 40 - 24);
         lprintf ("file object, packet length = %d (%d)\n",
                  this->asf_packet_len, LE_32(this->asf_header + i + 96 - 24));
@@ -1420,8 +1435,20 @@
 
         /* explicit padding with 0 */
         lprintf("padding: %d bytes\n", this->asf_packet_len - 
header.packet_len);
-        memset(this->buf + header.packet_len, 0, this->asf_packet_len - 
header.packet_len);
-        this->buf_size = this->asf_packet_len;
+       {
+         char *base  = (char *)(this->buf);
+         char *start = base + header.packet_len;
+         char *end   = start + this->asf_packet_len - header.packet_len;
+         if ((start > base) && (start < (base+BUF_SIZE-1)) &&
+             (start < end)  && (end < (base+BUF_SIZE-1))) {
+           memset(this->buf + header.packet_len, 0, this->asf_packet_len - 
header.packet_len);
+         }
+         if (this->asf_packet_len > BUF_SIZE) {
+            this->buf_size = BUF_SIZE;
+          } else {
+            this->buf_size = this->asf_packet_len;
+          }
+       }
       }
       break;
   }
--- libmms-0.2.orig/src/mmsh.c
+++ libmms-0.2/src/mmsh.c
@@ -184,7 +184,7 @@
   int           num_stream_ids;
   int           stream_ids[ASF_MAX_NUM_STREAMS];
   int           stream_types[ASF_MAX_NUM_STREAMS];
-  int           packet_length;
+  uint32_t      packet_length;
   int64_t       file_length;
   char          guid[37];
   uint32_t      bitrates[ASF_MAX_NUM_STREAMS];
@@ -604,6 +604,10 @@
       case GUID_ASF_FILE_PROPERTIES:
 
         this->packet_length = LE_32(this->asf_header + i + 92 - 24);
+        if (this->packet_length > CHUNK_SIZE) {
+          this->packet_length = 0;
+          break;
+        }
         this->file_length   = LE_64(this->asf_header + i + 40 - 24);
         lprintf ("file object, packet length = %d (%d)\n",
                 this->packet_length, LE_32(this->asf_header + i + 96 - 24));
@@ -1054,9 +1058,22 @@
                 this->chunk_length, this->packet_length);
        return 0;
       }
-      memset(this->buf + this->chunk_length, 0,
-             this->packet_length - this->chunk_length);
-      this->buf_size = this->packet_length;
+
+      {
+       char *base  = (char *)(this->buf);
+       char *start = base + this->chunk_length;
+       char *end   = start + this->packet_length - this->chunk_length;
+       if ((start > base) && (start < (base+CHUNK_SIZE-1)) &&
+           (start < end)  && (end < (base+CHUNK_SIZE-1))) {
+         memset(start, 0,
+                this->packet_length - this->chunk_length);
+       }
+       if (this->packet_length > CHUNK_SIZE) {
+         this->buf_size = CHUNK_SIZE;
+       } else {
+         this->buf_size = this->packet_length;
+       }
+      }
       return 1;
     } else {
       lprintf ("mmsh: read error, %d != %d\n", len, this->chunk_length);

--- End Message ---

Reply via email to