Vulnerability Note VU#20276

phf CGI Script fails to guard against newline characters

Original Release date: 28 Jan 2001 | Last revised: 17 Apr 2006

Overview

This document describes a vulnerability in a CGI script known as phf which was widely exploited in 1996 and 1997.

Description

The phf CGI script constructs a partial command line consisting of the ph command and appropriate arguments, and completes the command line based on the input from the user. The intent is to execute a ph query on behalf of the user. Annotated code from the vulnerable phf CGI script is shown below. Labels have been added for easy reference.

if (!atleastonequery) printf("<B>You did not enter a query!</B>%c",LF);
else {
1: strcpy(commandstr, "/usr/local/bin/ph -m ");
if (strlen(serverstr)) {
2: strcat(commandstr, " -s ");
/* RM 2/22/94 oops */
3: escape_shell_cmd(serverstr);
4: strcat(commandstr, serverstr);
strcat(commandstr, " ");
}
5: escape_shell_cmd(typestr);
6: strcat(commandstr, typestr);
if (atleastonereturn) {
7: escape_shell_cmd(returnstr);
strcat(commandstr, returnstr);
}

printf("The command is %s%c", commandstr, LF);
for (x = 0; x < strlen(commandstr); x++)
{
printf("%c", (int) commandstr[x]);
}
printf("<PRE>%c", LF);

8: phfp = popen(commandstr,"r");
send_fd(phfp, stdout);

printf("</PRE>%c", LF);
}

On line 1, the code builds a string "commandstr," consisting of the first part of a ph query. Input from the user is appended to "commandstr" on lines 2, 4, and 6. The particular text that gets appended depends on the type of query being executed.

A routine called escape_shell_cmd, called on lines 3, 5, and 7 tries to guard against shell command separators and shell history meta-characters by eliminating ampersands (&), semicolons (;), carets (^), exclamation points (!), and other characters from the input. However, the escape_shell_cmd code failed to guard against a newline character, which is also a valid command separator. If the intruder embeds a newline character in the string passed to the CGI script phf, the "popen" call on line 8 will interpret the string as two (or more) separate commands, separated by newlines.

The routine escape_shell_cmd is contained in a file named "util.c" that was distributed with web servers as part of a suite of CGI example scripts. escape_shell_cmd passes a command line to the operating system via the system call popen(3). The original escape_shell_cmd source from util.c is included below:

    void escape_shell_cmd(char *cmd) {
            register int x,y,l;

            l=strlen(cmd);
            for(x=0;cmd[x];x++) {
            if(ind("&;`'\"|*?~<>^()[]{}$\\",cmd[x]) != -1){
                for(y=l+1;y>x;y--)
                    cmd[y] = cmd[y-1];
                l++; /* length has been increased */
                cmd[x] = '\\';
                x++; /* skip the character */
            }
        }
    }

This routine attempts to limit the kinds of characters that can be passed to popen(3) in the CGI script phf. However, it fails to account for a newline character.

The popen(3) call is described by the man page as follows:

        FILE *popen(const char *command, const char *type);

        popen() creates a pipe between the calling program  and  the
        command  to  be  executed.   The  arguments  to  popen() are
        pointers to null-terminated strings.  "command" consists of  a
        shell command line.

In essence, popen provides the calling program the output of "command." One example of a command you could pass to popen is

cat /etc/passwd

In this case, popen would return the output of the "cat /etc/passwd" file to the calling program. You can also pass more complex shell commands to popen, such as

cat /etc/passwd & rm *

The ampersand character (&) puts the preceding command in the background and executes the rest of the command in the foreground. As another example, you can execute a sequence of commands by separating them with semicolons (;). For example,

ls ; rm * ; touch filename

This runs the commands sequentially.

Because escape_shell_cmd failed to guard against the newline character, the intruder is able to cause a newline character to get passed to popen. This will appear to be two separate commands, both of
which will be executed.

An annotated, updated version of the escape_shell_cmd code is shown below:

void escape_shell_cmd(char *cmd) {
register int x,y,l;

l=strlen(cmd);
for(x=0;cmd[x];x++) {
1: if(ind("&;`'\"|*?~<>^()[]{}$\\\x0A",cmd[x]) != -1){
for(y=l+1;y>x;y--)
cmd[y] = cmd[y-1];
l++; /* length has been increased */
cmd[x] = '\\';
x++; /* skip the character */
}
}
}

This code, distributed with later versions of util.c, checks for the newline character on line 1. The newline character is signified by "x0A".

The first known public discussion of this vulnerability was Monday, February 5, 1996 in a posting by Jennifer Myers to BugTraq. This message is available at


In general, we recommend against using the approach of filtering out bad characters. Instead, we recommend permitting only those characters that you are certain you can handle correctly. For more information on this recommendation, please see

Impact

Any remote user can run programs on the attacked machine as the uid running the Web sserver.

Solution

Update to the latest version of phf, or disable it if unused.

Systems Affected (Learn More)

No information available. If you are a vendor and your product is affected, let us know.

CVSS Metrics (Learn More)

Group Score Vector
Base N/A N/A
Temporal N/A N/A
Environmental N/A N/A

References

Credit

This document was written by Shawn V. Hernan

Other Information

  • CVE IDs: CVE-1999-0067
  • CERT Advisory: CA-1996-06
  • Date Public: 05 Feb 96
  • Date First Published: 28 Jan 2001
  • Date Last Updated: 17 Apr 2006
  • Severity Metric: 60.48
  • Document Revision: 8

Feedback

If you have feedback, comments, or additional information about this vulnerability, please send us email.