xorl %eax, %eax

CVE-2008-1391: NetBSD strfmon() Integer Overflow

leave a comment »

This is an old but still nice vulnerability. It was disclosed on 25 March 2008 by Maksymilian Arciemowicz. This issue also affects Apple MAC OS X v10.5.6 as well as FreeBSD 6 and 7.0. The following code is from NetBSD 4.0, and specifically from lib/libc/stdlib/strfmon.c:

ssize_t
strfmon(char * __restrict s, size_t maxsize, const char * __restrict format,
    ...)
{
 va_list		ap;
 char 		*dst;		/* output destination pointer */
 const char 	*fmt;		/* current format poistion pointer */
          ...
 int		width;		/* field width */
          ...
         /* Flags */
 	while (/* CONSTCOND */ 1) {
 		switch (*++fmt) {
          ...
 	break;
 	}

 	/* field Width */
 	if (isdigit((unsigned char)*fmt)) {
 		GET_NUMBER(width);
 		/* Do we have enough space to put number with
 		 * required width ?
 		 */

 		if (dst + width >= maxsize)
 			goto e2big_error;
 	}
          ...
 	tmpptr = dst;
          ...
 	if (dst - tmpptr < width) {
          ...
 		} else {
 			pad_size = dst-tmpptr;
 			memmove(tmpptr + width-pad_size, tmpptr,
 			    (size_t) pad_size);
 			memset(tmpptr, ' ', (size_t) width-pad_size);
 			dst += width-pad_size;
 		}
 	}
          ...
}


As you can read from its man page, this function is being used to place characters into an array pointed by its first argument. The above implementation uses GET_NUMBER() macro to determine the width required for a format specifier. This macro is defined at the same source code file like this:


#define GET_NUMBER(VAR)	do {   		 	        \
 int ovar;						\
        ovar = VAR = 0;                                 \
 while (isdigit((unsigned char)*fmt)) {			\
 	VAR *= 10;					\
 	VAR += *fmt - '0';				\
 	if (ovar > VAR)				        \
 		goto e2big_error;			\
                else                                    \
                        ovar = VAR;                     \
 	fmt++;						\
 }							\
} while (/* CONSTCOND */ 0)


It’s simple. It initializes ovar signed integer to 0 and then iterates through the format character at the while loop. If VAR is less than ovar it will go to e2big_error label, in any other case it will just update the value of ovar and continue. However, a malicious user can still use huge format specifiers that will eventually lead to an overflow at the calculations of strfmon() and this leads to memory corruption while performing the memmove() and memset(). The multiplication inside GET_NUMBER() as well as the pointer arithmetic at strfmon() can lead to such integer overflows. The patch was to remove the ovar check from GET_NUMBER() and place this:



#define GET_NUMBER(VAR)	do {					\
-	int ovar;						\
-       ovar = VAR = 0;                                         \
+       VAR = 0;                                                \
 while (isdigit((unsigned char)*fmt)) { 			\
 	VAR *= 10;				         	\
 	VAR += *fmt - '0';	     	   	        	\
-		if (ovar > VAR)				        \
+		if (VAR > 0x00ffffff)				\
 		        goto e2big_error;		        \
-               else                                            \
-                       ovar = VAR;                             \
 	fmt++;						        \
 }							        \


And also, they added a new variable of type ptrdiff_t to temporarily store the value of dst to perform a valid check of the required versus the maximum size like this:

 	if (isdigit((unsigned char)*fmt)) {
+			ptrdiff_t d = dst - s;
 		GET_NUMBER(width);
 		/* Do we have enough space to put number with
 		 * required width ?
 		 */
-
-			if (dst + width >= maxsize)
+                       if (d + width >= maxsize)
 			goto e2big_error;


Maksymilian Arciemowicz had also published a simple trigger code for this bug which was this:

#include <stdio.h>
#include <monetary.h>

int main(int argc, char* argv[]) {
 char buff[51];
 char *bux=buff;
 int res;

 res=strfmon(bux, 50, argv[1], "0");
 return 0;
}


With argv[1] set to “%99999999999999999999n” on his trigger. Anyway, old but funny :-)

Written by xorl

April 11, 2009 at 14:02

Posted in bugs, netbsd

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s