Software Engineering Institute

phf CGI Script fails to guard against newline characters

Vulnerability Note VU#20276

Original Release Date: 2001-01-28 | Last Revised: 2006-04-17

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("You did not enter a query!%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("
%c", LF);8:          phfp = popen(commandstr,"r");            send_fd(phfp, stdout);            printf("
%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 iscat /etc/passwdIn 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 ascat /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 filenameThis 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 ofwhich 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 athttp://www.securityfocus.com/frames/?content=/templates/archive.pike%3Flist%3D1%26msg%3D199602060250.UAA11818@marigold.eecs.nwu.eduIn 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 seehttp://www.cert.org/tech_tips/cgi_metacharacters.html

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.

CVSS Metrics

Group Score Vector
Base
Temporal
Environmental

Acknowledgements

This document was written by Shawn V. Hernan

Other Information

 CVE IDs: CVE-1999-0067 CERT Advisory: CA-1996-06 Severity Metric: 60.48 Date Public: 1996-02-05 Date First Published: 2001-01-28 Date Last Updated: 2006-04-17 18:47 UTC Document Revision: 8