[ Team LiB ] Previous Section Next Section

Plugging Security Holes with escapeshellcmd()

Before looking at escapeshellcmd(), let's examine the danger it guards against. We want to allow users to type in the names of manual pages and view output online. Now that we can output one manual page, it is a trivial matter to output any available page. Do not install the code in Listing 21.5; we are deliberately leaving a major security gap unplugged.

Listing 21.5 Calling the man Command
 1: <!DOCTYPE html PUBLIC
 2:   "-//W3C//DTD XHTML 1.0 Strict//EN"
 3:   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 4: <html>
 5: <head>
 6: <title>Listing 21.5 Calling the 'man' Command.
 7:    This Script is NOT Secure</title>
 8: </head>
 9: <body>
10: <div>
11: <form action="<?php print $PHP_SELF ?>" method="post">
12: <p>
13: <input type="text" value="<?php print $_REQUEST['manpage'] ?>" name="manpage" />
14: </p>
15: </form>
16: <pre>
17: <?php
18: if ( isset( $_REQUEST['manpage'] ) ) {
19:   system( "man ".$_REQUEST['manpage']." | col -b" );
20: }
21: ?>
22: </pre>
23: </div>
24: </body>
25: </html>

We extend our previous examples a little by adding a text field on line 13 and including the value from the form submission to the shell command we pass to the system() function on line 19. We are being trusting, however. On a Unix system, a malicious user could add his own commands to the manpage field, thus gaining limited access to the server. Figure 21.3 shows a simple hack that could be applied to this script.

Figure 21.3. Calling the man command.

graphics/21fig03.jpg

The malicious user has submitted the value xxx; ls -al via the form. This value is stored in the $_REQUEST['manpage'] element. After we combine this text with the shell command string we pass to system(), we end up with the following string:


"man xxx; ls -al | col -b"

This instructs the shell to fetch the manual page for xxx, which doesn't exist. It then performs a full directory listing, running the output through the col command. If you think that this is as bad as it gets, think again. An unfriendly visitor can list any readable directory on your system. He can even read your /etc/passwd file by adding the following line to the form field:


xxx; cat /etc/passwd

graphics/bytheway_icon.gif

Vulnerabilities due to programming errors or omissions are very common on the Internet. Large, well-resourced companies and organizations have famously left their systems open to attackers by failing to check user input or leaving administration tools open to the public.

It is easy to become complacent. As you code, try to keep security issues in mind at all times.


This clearly represents a grave breach in security and we cannot allow it to happen. The safest way of protecting against this is never to pass user input directly to a shell. You can make yourself a little safer, though, by using the escapeshellcmd() function to add backslashes to any metacharacters the user might submit. escapeshellcmd() requires a string and returns a converted copy. We can now amend our code, making our script a little safer, as shown in Listing 21.6.

Listing 21.6 Escaping User Input with the escapeshellcmd() Function
 1: <!DOCTYPE html PUBLIC
 2:   "-//W3C//DTD XHTML 1.0 Strict//EN"
 3:   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 4: <html>
 5: <head>
 6: <title>Listing 21.5 Escaping user input with
 7:    the escapeshellcmd() function</title>
 8: </head>
 9: <body>
10: <div>
11: <form action="<?php print $PHP_SELF ?>" method="post">
12: <p>
13: <input type="text" value="<?php print $_REQUEST['manpage'] ?>" name="manpage" />
14: </p>
15: </form>
16: <pre>
17: <?php
18: if ( isset( $_REQUEST['manpage'] ) ) {
19:   $manpage = escapeshellcmd( $_REQUEST['manpage'] );
20:   system( "man $manpage | col -b" );
21: }
22: ?>
23: </pre>
24: </div>
25: </body>
26: </html>

The only addition to this example is the use of escapeshellcmd() on line 19. If the user attempts to enter "xxx; cat /etc/passwd " now, it is amended to "xxx\; cat /etc/passwd ", preventing a new command from being issued. In fact, he will be presented with the manual page for the cat command rather than our password file!

Although you can improve security by using escapeshellcmd(), avoid passing user-submitted content to the shell. You could make your script even safer by compiling a list of all valid manual pages on your system and testing user input against this before calling system(). We do something similar in the next section.

    [ Team LiB ] Previous Section Next Section