Package: mimms
Version: 0.0.9-1
Severity: grave
Justification: user security hole
Tags: security patch
According to the patch attached in this report, it has many possible buffer
overflows.
For example,
- memcpy(buf, data, length) without bounding the limit of "length",
while "length" depend on the input data incoming from the internet.
- read(s, data, BUF_SIZE) in main(), where BUF_SIZE is much greater than
sizeof(data) which is only 1024 chars allocated in main(), while
BUF_SIZE is defined as 1024*128.
-- System Information:
Debian Release: testing/unstable
Architecture: i386 (i686)
Kernel: Linux 2.6.10-5-386
Locale: LANG=C, LC_CTYPE=thai
Versions of packages mimms depends on:
ii libc6 2.3.5-1ubuntu12 GNU C Library: Shared libraries an
ii libgcc1 1:3.4.2-2ubuntu1 GCC support library
ii libpopt0 1.7-4 lib for parsing cmdline parameters
ii libstdc++5 1:3.3.4-9ubuntu5 The GNU Standard C++ Library v3
ii libuuid1 1.35-6ubuntu1 Universally unique id library
-- no debconf information
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
--- mimms-0.0.9.orig/mimms.cpp 2004-10-22 21:38:33.000000000 +0700
+++ mimms-0.0.9/mimms.cpp 2006-05-13 21:27:57.168095728 +0700
@@ -66,6 +66,9 @@
int stream_ids[20];
int output_fh;
+// There's currently no use of put_32() that num_bytes depend on input and
+// cause buffer overflow.
+// Use put_32() with care, it can cause buffer overflow.
static void put_32 (command_t *cmd, uint32_t value) {
for (int i=0; i<4; i++) {
cmd->buf[cmd->num_bytes++] = (value >> (8*i)) & 0xff;
@@ -81,6 +84,8 @@
return value;
}
+#define min(a, b) (a) < (b) ? (a) : (b)
+#define max(a, b) (a) > (b) ? (a) : (b)
static void send_command (int s, int command, uint32_t switches,
uint32_t extra, int length,
char *data) {
@@ -107,8 +112,24 @@
put_32 (&cmd, switches);
put_32 (&cmd, extra);
- memcpy (&cmd.buf[48], data, length);
- if (length & 7)
+ // buffer overflow, when the length depend on input, such as, url length
+ //memcpy (&cmd.buf[48], data, length);
+ // Use min() to limit the upperbound length.
+ //memcpy (&cmd.buf[48], data, min(length, sizeof cmd.buf - 48));
+ // But the negative length can also cause buffer overflow, in case size_t is
+ // unsigned and length is casted to size_t.
+ // length can be negative in the line,
+ // send_command (s, 0x33, num_stream_ids,
+ // 0xFFFF | stream_ids[0] << 16,
+ // (num_stream_ids-1)*6+2 , data);
+ // in main() below, where num_stream_ids is zero.
+ // The solution is to cast length to size_t before min() compare.
+ memcpy (&cmd.buf[48], data,
+ min((size_t)length, sizeof cmd.buf - 48*(sizeof cmd.buf[0])));
+ if (length & 7 &&
+ // to avoid buffer overflow
+ (48 + length)*(sizeof cmd.buf[0]) + 8 - (length & 7) <= sizeof cmd.buf
+ )
memset(&cmd.buf[48 + length], 0, 8 - (length & 7));
if (send (s, cmd.buf, len8*8+48, 0) != (len8*8+48)) {
@@ -152,18 +173,37 @@
}
-static void string_utf16(char *dest, const char *src, int len) {
+// At this time, dest_size has unit in byte, which specify the length in bytes
+// of the buffer pointed to by dest.
+static void string_utf16(char *dest, size_t dest_size, const char *src, int len) {
int i;
memset (dest, 0, 1000);
- for (i=0; i<len; i++) {
+ //for (i=0; i<len && /* avoid buffer overflow */ i*2+1 < dest_size; i++)
+ for (i=0;
+ i<len &&
+ /* avoid buffer overflow */
+ // For generic stuff.
+ // This can be buffer overflow at ref_note{string_utf16#1} if
+ // sizeof dest[0] > 1
+ //(i*2+1)*(sizeof dest[0]) < dest_size;
+ // So, advance 1 step
+ (i*2+2)*(sizeof dest[0]) <= dest_size;
+ i++) {
dest[i*2] = src[i];
- dest[i*2+1] = 0;
+ dest[i*2+1] = 0; // ref_note{string_utf16#1}
}
+ //if (i*2+1 >= dest_size) return; // avoid buffer overflow
+ // For generic stuff.
+ // This can be buffer overflow at ref_note{string_utf16#2} if
+ // sizeof dest[0] > 1
+ //if ((i*2+1)*(sizeof dest[0]) >= dest_size) return; // avoid buffer overflow
+ // So, advance 1 step
+ if ((i*2+2)*(sizeof dest[0]) > dest_size) return; // avoid buffer overflow
dest[i*2] = 0;
- dest[i*2+1] = 0;
+ dest[i*2+1] = 0; // ref_note{string_utf16#2}
}
static void print_answer (char *data, int len) {
@@ -201,7 +241,10 @@
while (command == 0x1b) {
int len;
- len = recv (s, data, BUF_SIZE, 0) ;
+ //len = recv (s, data, BUF_SIZE, 0) ;
+ // For generic stuff
+ //len = recv (s, data, BUF_SIZE*(sizeof data[0]), 0) ;
+ len = recv (s, data, sizeof data, 0) ;
if (!len) {
dprintf ("\nalert! eof\n");
return;
@@ -214,14 +257,41 @@
}
}
-static int get_data (int s, char *buf, size_t count) {
+// This will continue to recv() data, even though buf_size is reached.
+// Both buf_size and count have unit in byte.
+// To allow buf_size <= 0 to not put any data in buf[]?
+static int get_data (int s, char *buf, size_t/*int*/ buf_size, size_t count) {
ssize_t len;
size_t total = 0;
+ // buf_size = min(count, buf_size);
+ if (buf_size > count) buf_size = count;
+
while (total < count) {
- len = recv (s, &buf[total], count-total, 0);
+ char data[1024];
+ //len = recv (s, &buf[total], count-total, 0); // can cause buffer overflow
+ if (total*(sizeof buf[0]) < buf_size)
+ // buf_size is already set to min(count, buf_size), no need to use min()
+ // here.
+ //len = recv (s, &buf[total], min(count-total, buf_size-total), 0);
+ // For the sake of generic programming habit, use total*(sizeof buf[0])
+ // to cover the case that sizeof buf[0] != 1
+ // :)
+ // At this time, this generic consideration just focus on avoiding buffer
+ // overflow only.
+ // It doesn't care if the other program logic is generic or not.
+ // The other program logic, such as, &buf[total], it can rather be
+ // &buf[total/(sizeof buf[0])], to be more generic and logically correct.
+ // But whether it is logically correct or not, this doesn't affect the
+ // buffer overflow, so it is ignored.
+ // This is to make the program secure in generic way, not to make the
+ // program work correctly in generic way :)
+ len = recv (s, &buf[total], buf_size-total*(sizeof buf[0]), 0);
+ else
+ // cast to size_t before min() compare
+ len = recv (s, data, min(size_t(count-total), sizeof data), 0);
if (len>0) {
dprintf ("[%d/%d]", total, count);
@@ -243,7 +313,18 @@
}
-static int get_header (int s, uint8_t *header) {
+// header_size should has the unit, which specify the number of elements of
+// that data type pointed to by header?
+// For example,
+// uint8_t header[80];
+// should has header_size of (sizeof header)/(sizeof header[0]) rather than
+// sizeof header? And the get_header() should aware that header_size is the
+// multiple of sizeof header rather than multiple of byte unit.
+// (When thinking in a highly generic and portable way :) )
+//
+// But at this time, header_size has unit in byte, which specify the length in
+// bytes of the buffer pointed to by header.
+static int get_header (int s, uint8_t *header, size_t header_size) {
unsigned char pre_header[8];
int i, header_len;
@@ -252,7 +333,7 @@
while (1) {
- if (!get_data (s, (char *)pre_header, 8)) {
+ if (!get_data (s, (char *)pre_header, sizeof pre_header, 8)) {
fprintf (stderr,_("pre-header read failed\n"));
return 0;
}
@@ -270,7 +351,17 @@
dprintf (_("asf header packet detected, len=%d\n"),
packet_len);
- if (!get_data (s, (char *)&header[header_len], packet_len)) {
+ if (!get_data (s, (char *)&header[header_len],
+ // casting negative to size_t may cause buffer overflow
+ //header_size - header_len,
+ // So, select non-negative value
+ //max(header_size - header_len, 0),
+ // This is equivalent to using max() above.
+ //header_size > header_len ? header_size - header_len : 0,
+ // For generic stuff.
+ header_size > header_len*(sizeof header[0]) ?
+ header_size - header_len*(sizeof header[0]) : 0,
+ packet_len)) {
fprintf (stderr,_("header data read failed\n"));
return 0;
}
@@ -292,7 +383,7 @@
int packet_len, command;
char data[BUF_SIZE];
- if (!get_data (s, (char *)&packet_len, 4)) {
+ if (!get_data (s, (char *)&packet_len, sizeof packet_len, 4)) {
fprintf (stderr,_("packet_len read failed\n"));
return 0;
}
@@ -302,7 +393,7 @@
dprintf (_("command packet detected, len=%d\n"),
packet_len);
- if (!get_data (s, data, packet_len)) {
+ if (!get_data (s, data, sizeof data, packet_len)) {
fprintf (stderr,_("command data read failed\n"));
return 0;
}
@@ -373,8 +464,10 @@
dprintf (_("stream object, stream id: %d\n"), stream_id);
- stream_ids[num_stream_ids] = stream_id;
- num_stream_ids++;
+ if (num_stream_ids < sizeof stream_ids / sizeof stream_ids[0]) {
+ stream_ids[num_stream_ids] = stream_id;
+ num_stream_ids++;
+ }
/*
@@ -402,7 +495,7 @@
int i;
char data[BUF_SIZE];
- if (!get_data (s, (char *)pre_header, 8)) {
+ if (!get_data (s, (char *)pre_header, sizeof pre_header, 8)) {
fprintf (stderr,_("pre-header read failed\n"));
return 0;
}
@@ -420,7 +513,7 @@
dprintf (_("asf media packet detected, len=%d\n"),
packet_len);
- if (!get_data (s, data, packet_len)) {
+ if (!get_data (s, data, sizeof data, packet_len)) {
fprintf (stderr,_("media data read failed\n"));
return 0;
}
@@ -431,7 +524,7 @@
int packet_len, command;
- if (!get_data (s, (char *)&packet_len, 4)) {
+ if (!get_data (s, (char *)&packet_len, sizeof packet_len, 4)) {
dprintf (_("packet_len read failed\n"));
return 0;
}
@@ -441,7 +534,7 @@
dprintf (_("command packet detected, len=%d\n"),
packet_len);
- if (!get_data (s, data, packet_len)) {
+ if (!get_data (s, data, sizeof data, packet_len)) {
fprintf (stderr,_("command data read failed\n"));
return 0;
}
@@ -669,32 +762,36 @@
char uuid_string[37];
uuid_unparse(client_uuid, uuid_string);
- sprintf (str,
+ snprintf (str, sizeof str,
"\034\003NSPlayer/9.0.0.2800; "
"{%s}; "
"Host: %s", uuid_string, host);
- string_utf16 (data, str, strlen(str)+2);
+ // long host[] can cause buffer overflow if use sprintf()
+ string_utf16 (data, sizeof data, str, strlen(str)+2);
send_command (s, 1, 0, 0x0004000b, strlen(str) * 2+8, data);
- len = read (s, data, BUF_SIZE) ;
+ len = read (s, data, sizeof data) ;
+ //len = read (s, data, BUF_SIZE) ; // buffer overflow
if (len)
print_answer (data, len);
/* cmd2 */
- string_utf16 (&data[8], "\002\000\\\\192.168.0.129\\TCP\\1037\0000",
+ string_utf16 (&data[8], sizeof data - 8*(sizeof data[0]),
+ "\002\000\\\\192.168.0.129\\TCP\\1037\0000",
28);
memset (data, 0, 8);
send_command (s, 2, 0, 0, 28*2+8, data);
- len = read (s, data, BUF_SIZE) ;
+ len = read (s, data, sizeof data) ;
+ //len = read (s, data, BUF_SIZE) ; // buffer overflow
if (len)
print_answer (data, len);
/* 0x5 */
- string_utf16 (&data[8], path, strlen(path));
+ string_utf16 (&data[8], sizeof data - 8*(sizeof data[0]), path, strlen(path));
memset (data, 0, 8);
send_command (s, 5, 0, 0, strlen(path)*2+12, data);
@@ -710,13 +807,17 @@
num_stream_ids = 0;
/* get_headers(s, asf_header); */
- asf_header_len = get_header (s, asf_header);
+ asf_header_len = get_header (s, asf_header, sizeof asf_header);
packet_length = interp_header (asf_header, asf_header_len);
/* 0x33 */
memset (data, 0, 40);
+ // num_stream_ids is limited to size of stream_ids array (in interp_header())
+ // which is 20.
+ // (20-1)*6 == 114 < sizeof data == 1024
+ // No buffer overflow here :)
for (i=1; i<num_stream_ids; i++) {
data [ (i-1) * 6 + 2 ] = 0xFF;
data [ (i-1) * 6 + 3 ] = 0xFF;