Date: Sun, 19 Oct 1997 20:38:40 -0400From: bryan berg To: BUGTRAQ@NETSPACE.ORGSubject: Vulnerability in PHP Example Logging Scripts Whilst perusing various things included with the PHP distribution, I noticed that there was a gaping security hole in a few of the example scripts, specifically mlog.html and mylog.html, which allow any remote user to read any arbitrary file on the system. (which is readable to the user that httpd and thus PHP are running as) To top it all off, this exploit is really easy to accomplish. The problem lies in the line: in both mlog.html and mylog.html. The idea is to include a file for each type of logging stats, however, there is no escaping of slashes, so one can specify any file on the system. The exploit for dummies: http://some.stupid.isp.net/~dumbuser/cool-logs/mlog.html?screen=[fully qualified path to any file on the system] useful files to see are /etc/hosts.allow, /etc/passwd (for unshadowed systems..) and just about anything else. Temporary fix:insert the line just before the > ...The workaround is to set the following in php.h >> >> #define PATTERN_RESTRICT ".*\\.phtml$" >> >> This will limit the php.cgi parser to only display files ending in .phtml >> >> The exact same adviasory applies to any other parser someone might decide >> to stick in their cgi-bin directory. This is in no way specific to PHP/FI. >> >> You can also avoid the problem by using either CGI redirection or >> by using the Apache module version. >> >> -Rasmus ---------------------------------------------------------------------------- The current PHP/FI distribution may be obtained from http://www.vex.net/php J-Man Th' Shaman [DiGiTAL iNFORMATiON SOCiETY] jshaman@m-net.arbornet.org jamin@avatar.ml.org --SNI advisory: From davids@SECNET.COM Fri Apr 18 22:29:51 1997 Date: Thu 17 Apr 1997 16:02:07 -0600 From: David Sacerdote To: BUGTRAQ@NETSPACE.ORG Subject: PHP/FI command line buffer overflow X-Premail-Auth: Key matching expected Key ID 9E55000D not found -----BEGIN PGP SIGNED MESSAGE----- ###### ## ## ###### ## ### ## ## ###### ## # ## ## ## ## ### ## ###### . ## ## . ######. 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 . 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-----