###### ## ## ######
## ### ## ##
###### ## # ## ##
## ## ### ##
###### . ## ## . ######.
Secure Networks Inc.
Security Advisory
April 17, 1997
Buffer Overflow in php.cgi
This advisory describes a remotely exploitable buffer overflow in the PHP
cgi program. This is *NOT* the PATTERN_RESTRICT issue described in
earlier bugtraq discussion.
Problem Description
~~~~~~~~~~~~~~~~~~~
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.
Technical Details
~~~~~~~~~~~~~~~~~
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);
...
Impact
~~~~~~
Attackers can remotely obtain shell or command line access to any vulnerable
system.
Vulnerable Systems
~~~~~~~~~~~~~~~~~~
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.
Fix information
~~~~~~~~~~~~~~~
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);
Additional Information
~~~~~~~~~~~~~~~~~~~~~~
If you have any questions about this advisory, feel free to mail me at
davids@secnet.com. Past Secure Networks advisories can be found at
ftp://ftp.secnet.com/pub/advisories, and Secure Networks papers can be
found at ftp://ftp.secnet.com/pub/papers.
PHP/FI was written by Rasmus Lerdorf <rasmus@vex.net>. Additional
information about PHP/FI can be found at http://www.vex.net/php
This advisory does NOT address a recently published hole in the php.cgi
which allows attackers to obtain copies of any file on the web server
which is readable by the user the php.cgi program runs as.
FIRST and vendor contacts please note that this advisory was expedited.
This is becuase we felt that previous public post (by another) party would
lead to the eventual discovery of this bug. As a result of this we therefore
wanted a fix to be availible to the public as soon as possible.
The following PGP key is for davids@secnet.com, should you wish to encrypt
any message traffic to the author of this advisory:
- -----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.2
mQCNAzJ4qJAAAAEEAOgB7mooQ6NgzcUSIehKUufGsyojutC7phVXZ+p8FnHLLZNB
BLQEtj5kmfww2A2pR29q4rgPeqEUOjWPlLNdSLby3NI8yKz1AQSQLHAwIDXt/lku
8QXClaV6pNIaQSN8cnyyvjH6TYF778yZhYz0mwLqW6dU5whHtP93ojDw1UhtAAUR
tCtEYXZpZCBTYWNlcmRvdGUgPGRhdmlkc0BzaWxlbmNlLnNlY25ldC5jb20+
=LtL9
- -----END PGP PUBLIC KEY BLOCK-----
Feel free to send responses and commments to sni@secnet.com. If you
should wish to encrypt such traffic, please use the Secure Networks Inc.
key:
- -----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.2
mQCNAzLaFzIAAAEEAKsVzPR7Y6oFN5VPE/Rp6Sm82oE0y6Mkuof8QzERV6taihn5
uySb31UeNJ4l6Ud9alOPT/0YdeOO9on6eD1iU8qumFxzO3TLm8nTAdZehQSAQfoa
rWmpwj7KpXN/3n+VyBWvhpBdKxe08SQN4ZjvV5HXy4YIrE5bTbgIhFKeVQANAAUR
tCVTZWN1cmUgTmV0d29ya3MgSW5jLiA8c25pQHNlY25ldC5jb20+iQCVAwUQM03n
27Tl3s+VYMi5AQHdGwP+N3hhILzzhSvhx1gj6ZElgsLa7Q1P3cTlc/Xqx50/wkcX
qIwiPudH+9UHvpL8fUNaHc9iZf3y8YZz0HWz56Vm5SG7uBfB/ksq4x04pQ65dQ1m
v51DYCvLG9u0jL4hC3Mz9WvIMANXqOUlAhuU1iy0wM41joE8aHdh2jsLHlB5qlSJ
AJUDBRAzTlbK/3eiMPDVSG0BAcTNA/9eF0X4Ei8LM4CXFW7JTB5vwXxerR6FmKI8
0JXt6KTrjGBzTfBrDGUZHNakPELjQPQI+fqg6hKJ7Ro1eSL4QbtX2BTO+wIWoLJG
hQmccKleuEK5N9vFgzvPTRknfkbqL1Ta7g3Z9tE8TQhFbj0x4yNFAPB/hOhVvY3s
YOkUx4T12A==
=ljNl
- -----END PGP PUBLIC KEY BLOCK-----
Copyright Notice
~~~~~~~~~~~~~~~~
The contents of this advisory are Copyright (C) 1997 Secure Networks Inc,
and may be distributed freely provided that no fee is charged for
distribution, and that proper credit is given.
PHP sources distributed as part of this advisory fall under the GNU
Public license, version 2.
-----BEGIN PGP SIGNATURE-----
Version: 2.6.2
iQCVAwUBM1ZjwbgIhFKeVQANAQE/HQP/cuvfviM3RdAtMFw4JlafVfAZ8KNYaP61
dHcg0KmEZYeChOFgtRULHj9d2bhYniAjwVkioO4vIwvsmfNZSGnP3kMMQRv0oSkJ
9MNqeupqeF3cs3i88m8bT8TH9qNOvsbHAGix/TNHvgSK63rzUfzbfDbId8YwA2JD
2892qweY4b0=
=vuDt
-----END PGP SIGNATURE-----