COMMAND
php (CGI)
SYSTEMS AFFECTED
Systems running php.cgi 2.0beta10 or earlier
PROBLEM
Secure Networks Inc. in their Security Advisory talk about buffer
overflow in php.cgi. This text is based on their advisory so it
is their credit.
In the function FixFilename() function in file.c, PHP attempts to
pass strings whose length may be as long as 8 kilobytes into
buffers as small as 128 bytes. This overwrites the stack, making
it possible for an attacker to obtain shell access to the machine
running the web server.
The filename argument to FixFilename is derived from the command
line used to invoke to the CGI script, or from the QUERY_STRING
environment variable passed to it. The total length of either
can be as long as eight kilobytes, but the fn string is a mere
128 bytes long. An excerpt from the flawed code reads:
char *FixFilename(char *filename, int cd, int *ret) {
...
char fn[128], user[128], *s;
...
s = strrchr(filename,'/');
if(s) {
strcpy(fn,s+1);
...
Attackers can remotely obtain shell or command line access to any
vulnerable system and vulnerable is any computer running a web
server with php.cgi 2.0beta10 or earlier is vulnerable,
irrespective of what operating system it is running, provided
that PHP is run as a cgi, and not as an Apache module. When
compiled as an Apache module, PHP does not appear to execute the
problem code.
To determine whether a system is running a web server with
php.cgi installed as a cgi, use your favorite web browser to
access the URL:
http://hostname/cgi-bin/php.cgi
If you see something like:
PHP/FI Version 2.0b10
...
Then the machine hostname is running PHP/FI and feel free to panic.
SOLUTION
Use the patch program to apply the following diffs to file.c,
then recompile php.cgi. These diffs are against version 2.0b10.
*** file.c Thu Apr 17 09:36:07 1997
--- file.c.fixed Thu Apr 17 09:36:00 1997
***************
*** 295,315 ****
s = strrchr(filename,'/');
if(s) {
! strcpy(fn,s+1);
o=*s;
*s='\0';
! strcpy(path,filename);
*s=o;
} else {
#ifdef PHP_ROOT_DIR
! strcpy(path,PHP_ROOT_DIR);
#else
path[0] = '\0';
#endif
! strcpy(fn,filename);
}
if(fn && *fn=='~') {
! strcpy(path,fn);
fn[0]='\0';
}
if(*path) {
--- 295,320 ----
s = strrchr(filename,'/');
if(s) {
! strncpy(fn,s+1, sizeof (fn));
! fn[sizeof(fn) - 1] = '\0';
o=*s;
*s='\0';
! strncpy(path,filename, sizeof (path));
! path[sizeof(path) - 1] = '\0';
*s=o;
} else {
#ifdef PHP_ROOT_DIR
! strncpy(path,PHP_ROOT_DIR, sizeof(path));
! path[sizeof(path) -1] = '\0';
#else
path[0] = '\0';
#endif
! strncpy(fn,filename, sizeof (fn));
! fn[sizeof(fn) - 1] = '\0';
}
if(fn && *fn=='~') {
! strncpy(path,fn, sizeof (path));
! path[sizeof(path) - 1] = '\0';
fn[0]='\0';
}
if(*path) {
***************
*** 319,328 ****
o=*s;
*s='\0';
}
! strcpy(user,path+1);
if(s) {
*s=o;
! strcpy(temp,s);
} else temp[0]='\0';
#ifdef HAVE_PWD_H
if(*user) {
--- 324,335 ----
o=*s;
*s='\0';
}
! strncpy(user,path+1, sizeof (user));
! user[sizeof(user) - 1] = '\0';
if(s) {
*s=o;
! strncpy(temp,s, sizeof (temp));
! temp[sizeof(temp) - 1] = '\0';
} else temp[0]='\0';
#ifdef HAVE_PWD_H
if(*user) {
***************
*** 333,339 ****
pd = getenv(PHP_PUB_DIRNAME_ENV);
#endif
if (pd == 0) pd = PHP_PUB_DIRNAME;
! sprintf(path,"%s/%s%s",pw->pw_dir,pd,temp);
}
}
#endif
--- 340,351 ----
pd = getenv(PHP_PUB_DIRNAME_ENV);
#endif
if (pd == 0) pd = PHP_PUB_DIRNAME;
! strcpy (path,pw->pw_dir);
! strcat (path,"/");
! strncat (path, pd,
! sizeof(path) - strlen(path) - 1);
! strncat (path, temp,
! sizeof (path) - strlen(path) - 1);
}
}
#endif
***************
*** 343,352 ****
o=*s;
*s='\0';
}
! strcpy(user,path+2);
if(s) {
*s=o;
! strcpy(temp,s);
} else temp[0]='\0';
#if HAVE_PWD_H
if(*user) {
--- 355,366 ----
o=*s;
*s='\0';
}
! strncpy(user,path+2, sizeof (user));
! user[sizeof(user) - 1] = '\0';
if(s) {
*s=o;
! strncpy(temp,s,sizeof(temp));
! temp[sizeof(temp) - 1] = '\0';
} else temp[0]='\0';
#if HAVE_PWD_H
if(*user) {
***************
*** 357,363 ****
pd = getenv(PHP_PUB_DIRNAME_ENV);
#endif
if (pd == 0) pd = PHP_PUB_DIRNAME;
! sprintf(path,"%s/%s%s",pw->pw_dir,pd,temp); }
}
#endif
}
--- 371,383 ----
pd = getenv(PHP_PUB_DIRNAME_ENV);
#endif
if (pd == 0) pd = PHP_PUB_DIRNAME;
! strcpy (path,pw->pw_dir);
! strcat (path,"/");
! strncat (path, pd,
! sizeof(path) - strlen(path) - 1);
! strncat (path, temp,
! sizeof (path) - strlen(path) - 1);
! }
}
#endif
}
***************
*** 370,376 ****
}
}
if(*fn) {
! sprintf(temp,"%s/%s",path,fn);
#ifndef WINDOWS
st = stat(temp,&gsb);
#else
--- 390,399 ----
}
}
if(*fn) {
! strncpy (temp, path, sizeof (temp));
! temp[sizeof(temp) - 1] = '\0';
! strcat (temp,"/");
! strncat(temp,fn,sizeof(temp) - strlen(temp) - 1);
#ifndef WINDOWS
st = stat(temp,&gsb);
#else
***************
*** 382,394 ****
st = -1;
#endif
if((st!=-1) && (gsb.st_mode&S_IFMT)==S_IFDIR) {
! sprintf(temp,"%s/%s/index.html",path,fn);
st = stat(temp,&gsb);
if(st==-1) {
! sprintf(temp,"%s/%s/index.phtml",path,fn);
st = stat(temp,&gsb);
}
! sprintf(path,"%s/%s",path,fn);
} else if(st==-1) {
l = strlen(temp);
if(strlen(fn)>4) {
--- 405,431 ----
st = -1;
#endif
if((st!=-1) && (gsb.st_mode&S_IFMT)==S_IFDIR) {
! strncpy (temp,path,sizeof(temp));
! temp[sizeof(temp) - 1] = '\0';
! strcat (temp, "/");
! strncat (temp,fn,
! sizeof(temp) - strlen (temp) - 1);
! strncat (temp,"/index.html",
! sizeof(temp) - strlen (temp) - 1);
st = stat(temp,&gsb);
if(st==-1) {
! strncpy (temp,path,sizeof(temp));
! temp[sizeof(temp) - 1] = '\0';
! strcat (temp, "/");
! strncat (temp,fn,
! sizeof(temp) - strlen (temp) - 1);
! strncat (temp,"/index.html",
! sizeof(temp) - strlen (temp) - 1);
st = stat(temp,&gsb);
}
! strcat (path,"/");
! strncat (path, fn,
! sizeof(path) - strlen(path) - 1);
} else if(st==-1) {
l = strlen(temp);
if(strlen(fn)>4) {
***************
*** 410,422 ****
st = -1;
#endif
if((st!=-1) && (gsb.st_mode&S_IFMT)==S_IFDIR) {
! sprintf(temp,"%s/index.html",path);
st = stat(temp,&gsb);
if(st==-1) {
! sprintf(temp,"%s/index.phtml",path);
st = stat(temp,&gsb);
}
! } else strcpy(temp,path);
}
} else {
#ifndef WINDOWS
--- 447,468 ----
st = -1;
#endif
if((st!=-1) && (gsb.st_mode&S_IFMT)==S_IFDIR) {
! strncpy (temp, path, sizeof (temp));
! temp[sizeof(temp) - 1] = '\0';
! strncat (temp, "/index.html",
! sizeof (temp) - strlen (temp) - 1);
st = stat(temp,&gsb);
if(st==-1) {
! strncpy (temp, path, sizeof (temp));
! temp[sizeof(temp) - 1] = '\0';
! strncat (temp, "/index.phtml",
! sizeof (temp) - strlen (temp) - 1);
st = stat(temp,&gsb);
}
! } else {
! strncpy(temp,path, sizeof (temp));
! temp[sizeof (temp) - 1] = '\0';
! }
}
} else {
#ifndef WINDOWS
***************
*** 430,442 ****
st = -1;
#endif
if((st!=-1) && (gsb.st_mode&S_IFMT)==S_IFDIR) {
! sprintf(temp,"%s/index.html",fn);
st = stat(temp,&gsb);
if(st==-1) {
! sprintf(temp,"%s/index.phtml",fn);
st = stat(temp,&gsb);
}
! } else strcpy(temp,fn);
}
*ret=st;
return(temp);
--- 476,498 ----
st = -1;
#endif
if((st!=-1) && (gsb.st_mode&S_IFMT)==S_IFDIR) {
! strncpy (temp, fn, sizeof (temp));
! temp[sizeof(temp) - 1] = '\0';
! strncat (temp, "/index.html",
! sizeof (temp) - strlen (temp) - 1);
st = stat(temp,&gsb);
if(st==-1) {
! strncpy (temp, fn, sizeof (temp));
! temp[sizeof(temp) - 1] = '\0';
! strncat (temp, "/index..phtml",
! sizeof (temp) - strlen (temp) - 1);
!
st = stat(temp,&gsb);
}
! } else {
! strncpy(temp,fn,sizeof (temp));
! temp[sizeof(temp) - 1] = '\0';
! }
}
*ret=st;
return(temp);