Interactive Debugging

There are two ways of debugging interactively. The first is to simulate the web server environment, and the second is to get the web server to start the debugger for you.

To simulate the server environment, this section creates a short Perl script that reads in the form information, encodes it, and then calls the program.

To do this, you need the module CGI::Enurl, which provides the function enurl. This function does the reverse of Parse_CGI; it takes a hash and returns an encode string representing the hash.

The simulation function starts by asking the user for the name of the script and then gets the form data in the form of key/value pairs. Next, it encodes the string with the enurl function:

my $url_info = enurl \%cgi_info;

One of the simplest ways of passing data to a CGI program is through the environment. You simulate that by setting some key environment variables and then executing the program itself:

# Set a minimal set of environment variables 
$ENV{QUERY_STRING} = $url_info; 
$ENV{REQUEST_URI} = "$prog?$url_info"; 
$ENV{REQUEST_METHOD} = "GET"; 
exec("perl –d $prog");

A typical run looks like

perl debug.pl 
Script name: /home/httpd/cgi–bin/record.pl 
Enter key=value 
(End with a blank line) 
> name=oualline 
> group=Software 
> schedule=early 
> version=1.0 
> work=1 
> 

Loading DB routines from perl5db.pl version 1.0402 
Emacs support available. 

Enter h or `h h' for help. 

main::(/home/httpd/cgi–bin/record.pl:33): 
33:    print "Content–type: text/html
"; 
DB<1>

You can now debug the CGI program normally. Listing 12.4 shows the full version of the debug program.

Listing 12.4. debug.pl
use strict; 
use warnings; 

use CGI::Enurl; 

my %cgi_info = ();    # CGI Information we are creating 

print "Script name: "; 
my $prog = <STDIN>; 
chomp($prog); 

print "Enter key=value
"; 
print "(End with a blank line)
"; 

# Get pairs that look like    name=value 
while (1) {
    print "> "; 
    my $line = <STDIN>; 
    chomp($line); 

    if ($line eq "") {
        last; 
    } 

    if ($line !~ /^([^=]*)s*=s*(.*)$/) {
        print "ERROR: Could not understand $line
"; 
        next; 
    } 
    $cgi_info{$1} = $2; 
} 

# Encode the CGI information for simulation 
my $url_info = enurl \%cgi_info; 

# Set a minimal set of environment variables 
$ENV{QUERY_STRING} = $url_info; 
$ENV{REQUEST_URI} = "$prog?$url_info"; 
$ENV{REQUEST_METHOD} = "GET"; 
exec("perl –Td $prog");

Server-Started Debuggers

Another way to debug a CGI program is to have the server start the debugger. This may be a little difficult because the program starts with no terminal connected to it.

If you are using the X Windows system, you can perform some tricks to connect the debugger to your display. The first step is to find out what the name of your display is. This is done through the command

echo $DISPLAY 
:0.0

This example uses the display :0.0. The next step is to create a shell script that starts the debug on the specified window.

If you are starting the ptkdb debugger, the script is shown in Listing 12.5

Listing 12.5. record.ptkdb
#!/bin/sh 
DISPLAY=:0.0 
export DISPLAY 
exec perl –wTd record.pl "$*"

The first line is a magic line that tells the system that this is a shell (sh) script. The next two lines tell the system which display to use. Finally, the real program is executed using the debugger.

The script must be placed in the CGI directory and made executable.

Next, create a web form that calls the debug wrapper (record.ptkdb) rather than the real script (record.pl).

Finally, you need to tell the X Windows system that you are going to connect to it from a CGI program and that it’s okay to accept connections from the web server. This is done with the command

xhost +

Now when you submit the debugging form, the script record.ptkd runs. It redirects the output to your display (:0.0 in this example) and then starts the debugger on your script.

To summarize, the steps in doing a server debug are

1.
Find out what display you are running (echo $DISPLAY).

2.
Create a wrapper that sets the display and starts the debugger.

3.
Put the wrapper in the CGI directory.

4.
Create a form that calls the wrapper.

5.
Enable connections to your display from another system (xhost +).

6.
Run your debugging form.

You can use almost the same technique to debug the program using the command-line debugger. In this case, you use the xterm program to create a new window for debugging. Listing 12.6 shows how this is done.

Listing 12.6. record.debug
#!/bin/sh 
exec /usr/bin/X11/xterm –display :0.0 –e perl –wTd record.pl "$*"

Noted that when you use this debugging technique, all the output of CGI program is sent to the terminal, not the web server. The result is that you see everything, and the web browser gets a "Internal Server Error" message.

Security

Web forms have their own set of security problems. Fortunately, the operating system, the web browser, and Perl all provide some security against hackers. But that doesn’t mean that you should ignore it.

The first line of defense comes from the operating system and web browser. The web browser runs under a special user account, typically named www or something similar. This means that all CGI scripts can access only the data available to this user and can destroy only the files writable by this user.

However, sometimes this can be a problem. Suppose that you want the data created by your CGI program to be owned by you rather than the user www. Linux and UNIX solve this problem through use of something called a setuid bit. If a script has this bit set, the script runs as the user who owns it, not the user who invoked it.

To create a setuid script, all you have to do is issue the correct chmod command:

chmod u+s script.pl

This command tells the system that when script.pl is run, that it is to be run as if you ran it.

Taint Mode

The next defense against hackers is Perl’s Taint mode. This is enabled by the –T switch on the first line of the program. In Taint mode, anything supplied from the outside is considered tainted and generates an error if used in an insecure manner.

Say that you have a form that lets a user submit a name and password, and you want to validate it using an external program. Here’s an insecure code fragment that does the job:

my $user = $form_data{USER}; 
my $password = $form_data{PASSWORD}; 

my $status = system("validate_user $user $password"); 
if ($status == 0) {
    print "User is good
"; 
} else {
    print "User is bad
"; 
}

So what’s wrong with this program? The problem is that the user can supply anything he wants for his username. This can be something reasonable such as sam, or it can be something that allows him to hack the system such as sam ; rm –rf /.

The second answer causes the system command to execute the command

validate_user sam ; rm –rf /

Because a semicolon is the command separator for the shell, this is actually two commands: the validate_user and an rm –rf. This is not what was intended.

In general, Perl’s Taint mode prevents you from using any unchecked user input in a system or open function.

For example, when Taint mode is turned on, using tainted user input in a system call results in an error like the following:

Insecure dependency in system while running with –T switch at passwd.pl line 7, <> line 1.

Direct assignment will not remove the taint from the data. However, if you extract a string from tainted data, the result is untainted. For example:

$tainted_data =~ /^(s+)$/; 
my $untainted_data = $1;

Also anything coming in from the environment is considered tainted. In particular, Perl will not do a system command if the path is tainted. To remove this taint, you’ll need to set the path yourself as well as remove from your environment any variables that might affect the shell:

$ENV{PATH} = "/bin:/usr/bin:/usr/X11R6/bin"; 
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

How Perl Programs Break

Experience is the best teacher; unfortunately, experience in a security disaster is not something you really want to have. So take a few minutes to learn about what not to do.

First, remember that usernames, filenames, and other user input can contain special characters. You’ve already seen how a username of sam ; rm –rf / can create havoc.

Don’t assume that filenames contain only legal characters. For example, suppose that your script is designed to fetch any file in the tree /home/status/ data, and you write this as

chdir("/home/status/data"); 
open IN_FILE, "<$user_supplied_file_name";

The filename supplied by the user can look like something simple (status.txt) or something designed to get data it shouldn’t (../../../etc/passwd). In the second case, file specification gives the user access to the system password file. This file is useful to a hacker.

The Code Red worm uses a variation of this trick to break into Microsoft IIS servers. The server checked for something so simple as ../.., but there are ways of hiding this path using Unicode, which the worm exploited.

Another mistake is to assume that a hidden input field has not been altered by the user. Remember that users can write their own web pages and point them to your CGI program. A couple of online stores found out about this problem the hard way. The stores put the price of an item in a hidden field called PRICE in their generated web pages. The hackers came along and saw the line

<INPUT TYPE="hidden" NAME="price" VALUE="298.99">

and did a minor edit before submitting the form to the store:

<INPUT TYPE="hidden" NAME="price" VALUE="2.99">

Needless to say, they were getting the digital cameras very cheap.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset