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);